From 1e12db0e89aa57a967b0b0135e4b4a8dfdc7820a Mon Sep 17 00:00:00 2001 From: Marcos Date: Thu, 10 Jul 2025 12:41:22 -0300 Subject: [PATCH 01/22] feat: Add support to plugin list endpoint --- src/api/controllers/plugin.rs | 26 +++++++ src/api/routes/plugin.rs | 52 ++++++++++++- src/repositories/plugin/mod.rs | 8 ++ src/repositories/plugin/plugin_in_memory.rs | 34 +++++++++ src/repositories/plugin/plugin_redis.rs | 84 +++++++++++++++++---- 5 files changed, 187 insertions(+), 17 deletions(-) diff --git a/src/api/controllers/plugin.rs b/src/api/controllers/plugin.rs index 26e2b894c..8fec9c478 100644 --- a/src/api/controllers/plugin.rs +++ b/src/api/controllers/plugin.rs @@ -62,6 +62,32 @@ where } } +/// List plugins +/// +/// # Arguments +/// +/// * `state` - The application state containing the plugin repository. +/// +/// # Returns +/// +/// The result of the plugin list. +pub async fn list_plugins( + state: ThinDataAppState, +) -> Result +where + J: JobProducerTrait + Send + Sync + 'static, + RR: RelayerRepository + Repository + Send + Sync + 'static, + TR: TransactionRepository + Repository + Send + Sync + 'static, + NR: NetworkRepository + Repository + Send + Sync + 'static, + NFR: Repository + Send + Sync + 'static, + SR: Repository + Send + Sync + 'static, + TCR: TransactionCounterTrait + Send + Sync + 'static, + PR: PluginRepositoryTrait + Send + Sync + 'static, +{ + let plugins = state.plugin_repository.get_all().await?; + Ok(HttpResponse::Ok().json(plugins)) +} + #[cfg(test)] mod tests { use std::time::Duration; diff --git a/src/api/routes/plugin.rs b/src/api/routes/plugin.rs index 9b7532462..6b46949eb 100644 --- a/src/api/routes/plugin.rs +++ b/src/api/routes/plugin.rs @@ -5,7 +5,13 @@ use crate::{ api::controllers::plugin, models::{DefaultAppState, PluginCallRequest}, }; -use actix_web::{post, web, Responder}; +use actix_web::{get, post, web, Responder}; + +/// List plugins +#[get("/plugins")] +async fn list_plugins(data: web::ThinData) -> impl Responder { + plugin::list_plugins(data).await +} /// Calls a plugin method. #[post("/plugins/{plugin_id}/call")] @@ -21,12 +27,15 @@ async fn plugin_call( pub fn init(cfg: &mut web::ServiceConfig) { // Register routes with literal segments before routes with path parameters cfg.service(plugin_call); // /plugins/{plugin_id}/call + cfg.service(list_plugins); // /plugins } #[cfg(test)] mod tests { + use std::time::Duration; + use super::*; - use crate::services::plugins::PluginCallResponse; + use crate::{models::PluginModel, services::plugins::PluginCallResponse}; use actix_web::{test, App, HttpResponse}; async fn mock_plugin_call() -> impl Responder { @@ -40,6 +49,21 @@ mod tests { }) } + async fn mock_list_plugins() -> impl Responder { + HttpResponse::Ok().json(vec![ + PluginModel { + id: "test-plugin".to_string(), + path: "test-path".to_string(), + timeout: Duration::from_secs(69), + }, + PluginModel { + id: "test-plugin2".to_string(), + path: "test-path2".to_string(), + timeout: Duration::from_secs(69), + }, + ]) + } + #[actix_web::test] async fn test_plugin_call() { let app = test::init_service( @@ -67,4 +91,28 @@ mod tests { let plugin_call_response: PluginCallResponse = serde_json::from_slice(&body).unwrap(); assert!(plugin_call_response.success); } + + #[actix_web::test] + async fn test_list_plugins() { + let app = test::init_service( + App::new() + .service(web::resource("/plugins").route(web::get().to(mock_list_plugins))) + .configure(init), + ) + .await; + + let req = test::TestRequest::get().uri("/plugins").to_request(); + let resp = test::call_service(&app, req).await; + + assert!(resp.status().is_success()); + + let body = test::read_body(resp).await; + let plugin_call_response: Vec = serde_json::from_slice(&body).unwrap(); + + assert_eq!(plugin_call_response.len(), 2); + assert_eq!(plugin_call_response[0].id, "test-plugin"); + assert_eq!(plugin_call_response[0].path, "test-path"); + assert_eq!(plugin_call_response[1].id, "test-plugin2"); + assert_eq!(plugin_call_response[1].path, "test-path2"); + } } diff --git a/src/repositories/plugin/mod.rs b/src/repositories/plugin/mod.rs index 940c41b57..89a482039 100644 --- a/src/repositories/plugin/mod.rs +++ b/src/repositories/plugin/mod.rs @@ -48,6 +48,7 @@ use crate::{ pub trait PluginRepositoryTrait { async fn get_by_id(&self, id: &str) -> Result, RepositoryError>; async fn add(&self, plugin: PluginModel) -> Result<(), RepositoryError>; + async fn get_all(&self) -> Result, RepositoryError>; } /// Enum wrapper for different plugin repository implementations @@ -86,6 +87,13 @@ impl PluginRepositoryTrait for PluginRepositoryStorage { PluginRepositoryStorage::Redis(repo) => repo.add(plugin).await, } } + + async fn get_all(&self) -> Result, RepositoryError> { + match self { + PluginRepositoryStorage::InMemory(repo) => repo.get_all().await, + PluginRepositoryStorage::Redis(repo) => repo.get_all().await, + } + } } impl TryFrom for PluginModel { diff --git a/src/repositories/plugin/plugin_in_memory.rs b/src/repositories/plugin/plugin_in_memory.rs index d160945d4..e17d23377 100644 --- a/src/repositories/plugin/plugin_in_memory.rs +++ b/src/repositories/plugin/plugin_in_memory.rs @@ -67,6 +67,11 @@ impl PluginRepositoryTrait for InMemoryPluginRepository { store.insert(plugin.id.clone(), plugin); Ok(()) } + + async fn get_all(&self) -> Result, RepositoryError> { + let store = Self::acquire_lock(&self.store).await?; + Ok(store.values().cloned().collect()) + } } #[cfg(test)] @@ -135,4 +140,33 @@ mod tests { Some(plugin) ); } + + #[tokio::test] + async fn test_get_all_plugins() { + let plugin_repository = Arc::new(InMemoryPluginRepository::new()); + + let plugin1 = PluginModel { + id: "test-plugin1".to_string(), + path: "test-path1".to_string(), + timeout: Duration::from_secs(69), + }; + + let plugin2 = PluginModel { + id: "test-plugin2".to_string(), + path: "test-path2".to_string(), + timeout: Duration::from_secs(69), + }; + + plugin_repository.add(plugin1.clone()).await.unwrap(); + plugin_repository.add(plugin2.clone()).await.unwrap(); + + let retrieved = plugin_repository.get_all().await.unwrap(); + assert_eq!(retrieved.len(), 2); + assert_eq!(retrieved[0].id, plugin1.id); + assert_eq!(retrieved[0].path, plugin1.path); + assert_eq!(retrieved[0].timeout, plugin1.timeout); + assert_eq!(retrieved[1].id, plugin2.id); + assert_eq!(retrieved[1].path, plugin2.path); + assert_eq!(retrieved[1].timeout, plugin2.timeout); + } } diff --git a/src/repositories/plugin/plugin_redis.rs b/src/repositories/plugin/plugin_redis.rs index dbfb10aa2..272bc58f8 100644 --- a/src/repositories/plugin/plugin_redis.rs +++ b/src/repositories/plugin/plugin_redis.rs @@ -47,27 +47,25 @@ impl RedisPluginRepository { fn plugin_list_key(&self) -> String { format!("{}:{}", self.key_prefix, PLUGIN_LIST_KEY) } -} -impl fmt::Debug for RedisPluginRepository { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("RedisPluginRepository") - .field("client", &"") - .field("key_prefix", &self.key_prefix) - .finish() - } -} - -#[async_trait] -impl PluginRepositoryTrait for RedisPluginRepository { - async fn get_by_id(&self, id: &str) -> Result, RepositoryError> { + /// Get plugin by ID using an existing connection. + /// This method is useful to prevent creating new connections for + /// getting individual plugins on list operations. + /// + /// # Arguments + /// + /// * `id` - The ID of the plugin to get. + /// * `conn` - The connection to use. + async fn get_by_id_with_connection( + &self, + id: &str, + conn: &mut ConnectionManager, + ) -> Result, RepositoryError> { if id.is_empty() { return Err(RepositoryError::InvalidData( "Plugin ID cannot be empty".to_string(), )); } - - let mut conn = self.client.as_ref().clone(); let key = self.plugin_key(id); debug!("Fetching plugin data for ID: {}", id); @@ -89,6 +87,42 @@ impl PluginRepositoryTrait for RedisPluginRepository { } } } +} + +impl fmt::Debug for RedisPluginRepository { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RedisPluginRepository") + .field("client", &"") + .field("key_prefix", &self.key_prefix) + .finish() + } +} + +#[async_trait] +impl PluginRepositoryTrait for RedisPluginRepository { + async fn get_by_id(&self, id: &str) -> Result, RepositoryError> { + let mut conn = self.client.as_ref().clone(); + self.get_by_id_with_connection(id, &mut conn).await + } + + async fn get_all(&self) -> Result, RepositoryError> { + let mut conn = self.client.as_ref().clone(); + let list_key: String = self.plugin_list_key(); + let plugin_ids: Vec = conn + .smembers(&list_key) + .await + .map_err(|e| self.map_redis_error(e, "list_all_plugins"))?; + + let mut plugins = Vec::new(); + for id in plugin_ids { + match self.get_by_id_with_connection(&id, &mut conn).await { + Ok(Some(plugin)) => plugins.push(plugin), + Ok(None) => {} + Err(e) => return Err(e), + } + } + Ok(plugins) + } async fn add(&self, plugin: PluginModel) -> Result<(), RepositoryError> { if plugin.id.is_empty() { @@ -299,6 +333,26 @@ mod tests { .contains("ID cannot be empty")); } + #[tokio::test] + #[ignore = "Requires active Redis instance"] + async fn test_get_all_plugins() { + let repo = setup_test_repo().await; + let plugin_name1 = uuid::Uuid::new_v4().to_string(); + let plugin_name2 = uuid::Uuid::new_v4().to_string(); + let plugin1 = create_test_plugin(&plugin_name1, "/path/to/plugin1"); + let plugin2 = create_test_plugin(&plugin_name2, "/path/to/plugin2"); + + repo.add(plugin1.clone()).await.unwrap(); + repo.add(plugin2.clone()).await.unwrap(); + + let retrieved = repo.get_all().await.unwrap(); + assert!(retrieved.len() == 2); + assert_eq!(retrieved[0].id, plugin1.id); + assert_eq!(retrieved[0].path, plugin1.path); + assert_eq!(retrieved[1].id, plugin2.id); + assert_eq!(retrieved[1].path, plugin2.path); + } + #[tokio::test] #[ignore = "Requires active Redis instance"] async fn test_add_plugin_with_empty_id() { From 7b098410887eb8daebe1e9b08a7a4817e7ca385c Mon Sep 17 00:00:00 2001 From: Marcos Date: Thu, 10 Jul 2025 13:05:39 -0300 Subject: [PATCH 02/22] docs: Add plugin list to docs --- src/api/routes/docs/plugin_docs.rs | 72 +++++++++++++++++++++++++++++- src/models/plugin.rs | 3 +- 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/src/api/routes/docs/plugin_docs.rs b/src/api/routes/docs/plugin_docs.rs index 42e59fe88..14e566889 100644 --- a/src/api/routes/docs/plugin_docs.rs +++ b/src/api/routes/docs/plugin_docs.rs @@ -1,5 +1,5 @@ use crate::{ - models::{ApiResponse, PluginCallRequest}, + models::{ApiResponse, PluginCallRequest, PluginModel}, services::plugins::PluginCallResponse, }; @@ -76,3 +76,73 @@ use crate::{ )] #[allow(dead_code)] fn doc_call_plugin() {} + +/// List plugins. +#[utoipa::path( + get, + path = "/api/v1/plugins", + tag = "Plugins", + operation_id = "listPlugins", + security( + ("bearer_auth" = []) + ), + responses( + ( + status = 200, + description = "Plugins listed successfully", + body = ApiResponse> + ), + ( + status = 400, + description = "BadRequest", + body = ApiResponse, + example = json!({ + "success": false, + "message": "Bad Request", + "data": null + }) + ), + ( + status = 401, + description = "Unauthorized", + body = ApiResponse, + example = json!({ + "success": false, + "message": "Unauthorized", + "data": null + }) + ), + ( + status = 404, + description = "Not Found", + body = ApiResponse, + example = json!({ + "success": false, + "message": "Plugin with ID plugin_id not found", + "data": null + }) + ), + ( + status = 429, + description = "Too Many Requests", + body = ApiResponse, + example = json!({ + "success": false, + "message": "Too Many Requests", + "data": null + }) + ), + ( + status = 500, + description = "Internal server error", + body = ApiResponse, + example = json!({ + "success": false, + "message": "Internal Server Error", + "data": null + }) + ), + ) +)] +#[allow(dead_code)] +fn doc_list_plugins() {} diff --git a/src/models/plugin.rs b/src/models/plugin.rs index c8dcbca44..5c05cb5e8 100644 --- a/src/models/plugin.rs +++ b/src/models/plugin.rs @@ -3,13 +3,14 @@ use std::time::Duration; use serde::{Deserialize, Serialize}; use utoipa::ToSchema; -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct PluginModel { /// Plugin ID pub id: String, /// Plugin path pub path: String, /// Plugin timeout + #[schema(value_type = u64)] pub timeout: Duration, } From 2aa09db36e77d7966d53aa0ea8478bb08258088b Mon Sep 17 00:00:00 2001 From: Marcos Date: Fri, 11 Jul 2025 10:30:33 -0300 Subject: [PATCH 03/22] fix: Use paginated list --- src/api/controllers/plugin.rs | 23 +- src/api/routes/docs/plugin_docs.rs | 7 +- src/api/routes/plugin.rs | 9 +- src/repositories/mod.rs | 4 +- src/repositories/plugin/mod.rs | 26 ++- src/repositories/plugin/plugin_in_memory.rs | 58 +++-- src/repositories/plugin/plugin_redis.rs | 228 ++++++++++++++++---- 7 files changed, 278 insertions(+), 77 deletions(-) diff --git a/src/api/controllers/plugin.rs b/src/api/controllers/plugin.rs index 8fec9c478..542d2575e 100644 --- a/src/api/controllers/plugin.rs +++ b/src/api/controllers/plugin.rs @@ -5,8 +5,9 @@ use crate::{ jobs::JobProducerTrait, models::{ - ApiError, ApiResponse, NetworkRepoModel, NotificationRepoModel, PluginCallRequest, - RelayerRepoModel, SignerRepoModel, ThinDataAppState, TransactionRepoModel, + ApiError, ApiResponse, NetworkRepoModel, NotificationRepoModel, PaginationMeta, + PaginationQuery, PluginCallRequest, PluginModel, RelayerRepoModel, SignerRepoModel, + ThinDataAppState, TransactionRepoModel, }, repositories::{ NetworkRepository, PluginRepositoryTrait, RelayerRepository, Repository, @@ -66,12 +67,16 @@ where /// /// # Arguments /// +/// * `query` - The pagination query parameters. +/// * `page` - The page number. +/// * `per_page` - The number of items per page. /// * `state` - The application state containing the plugin repository. /// /// # Returns /// /// The result of the plugin list. pub async fn list_plugins( + query: PaginationQuery, state: ThinDataAppState, ) -> Result where @@ -84,8 +89,18 @@ where TCR: TransactionCounterTrait + Send + Sync + 'static, PR: PluginRepositoryTrait + Send + Sync + 'static, { - let plugins = state.plugin_repository.get_all().await?; - Ok(HttpResponse::Ok().json(plugins)) + let plugins = state.plugin_repository.list_paginated(query).await?; + + let plugin_items: Vec = plugins.items.into_iter().collect(); + + Ok(HttpResponse::Ok().json(ApiResponse::paginated( + plugin_items, + PaginationMeta { + total_items: plugins.total, + current_page: plugins.page, + per_page: plugins.per_page, + }, + ))) } #[cfg(test)] diff --git a/src/api/routes/docs/plugin_docs.rs b/src/api/routes/docs/plugin_docs.rs index 14e566889..007600345 100644 --- a/src/api/routes/docs/plugin_docs.rs +++ b/src/api/routes/docs/plugin_docs.rs @@ -1,5 +1,6 @@ use crate::{ models::{ApiResponse, PluginCallRequest, PluginModel}, + repositories::PaginatedResult, services::plugins::PluginCallResponse, }; @@ -86,11 +87,15 @@ fn doc_call_plugin() {} security( ("bearer_auth" = []) ), + params( + ("page" = Option, Query, description = "Page number for pagination (starts at 1)"), + ("per_page" = Option, Query, description = "Number of items per page (default: 10)") + ), responses( ( status = 200, description = "Plugins listed successfully", - body = ApiResponse> + body = ApiResponse> ), ( status = 400, diff --git a/src/api/routes/plugin.rs b/src/api/routes/plugin.rs index 6b46949eb..58f9f4aba 100644 --- a/src/api/routes/plugin.rs +++ b/src/api/routes/plugin.rs @@ -3,14 +3,17 @@ //! The routes are integrated with the Actix-web framework and interact with the plugin controller. use crate::{ api::controllers::plugin, - models::{DefaultAppState, PluginCallRequest}, + models::{DefaultAppState, PaginationQuery, PluginCallRequest}, }; use actix_web::{get, post, web, Responder}; /// List plugins #[get("/plugins")] -async fn list_plugins(data: web::ThinData) -> impl Responder { - plugin::list_plugins(data).await +async fn list_plugins( + query: web::Query, + data: web::ThinData, +) -> impl Responder { + plugin::list_plugins(query.into_inner(), data).await } /// Calls a plugin method. diff --git a/src/repositories/mod.rs b/src/repositories/mod.rs index 24c5e5101..cd5904500 100644 --- a/src/repositories/mod.rs +++ b/src/repositories/mod.rs @@ -5,6 +5,7 @@ use crate::models::{PaginationQuery, RepositoryError}; use async_trait::async_trait; use eyre::Result; +use serde::{Deserialize, Serialize}; use thiserror::Error; mod relayer; @@ -31,7 +32,7 @@ pub use plugin::*; // Redis base utilities for shared functionality pub mod redis_base; -#[derive(Debug)] +#[derive(Debug, Serialize, Deserialize, ToSchema)] pub struct PaginatedResult { pub items: Vec, pub total: u64, @@ -46,6 +47,7 @@ pub struct BatchRetrievalResult { #[cfg(test)] use mockall::automock; +use utoipa::ToSchema; #[async_trait] #[allow(dead_code)] diff --git a/src/repositories/plugin/mod.rs b/src/repositories/plugin/mod.rs index 89a482039..2a2349379 100644 --- a/src/repositories/plugin/mod.rs +++ b/src/repositories/plugin/mod.rs @@ -38,8 +38,8 @@ use mockall::automock; use crate::{ config::PluginFileConfig, constants::DEFAULT_PLUGIN_TIMEOUT_SECONDS, - models::{PluginModel, RepositoryError}, - repositories::ConversionError, + models::{PaginationQuery, PluginModel, RepositoryError}, + repositories::{ConversionError, PaginatedResult}, }; #[async_trait] @@ -48,7 +48,11 @@ use crate::{ pub trait PluginRepositoryTrait { async fn get_by_id(&self, id: &str) -> Result, RepositoryError>; async fn add(&self, plugin: PluginModel) -> Result<(), RepositoryError>; - async fn get_all(&self) -> Result, RepositoryError>; + async fn list_paginated( + &self, + query: PaginationQuery, + ) -> Result, RepositoryError>; + async fn count(&self) -> Result; } /// Enum wrapper for different plugin repository implementations @@ -88,10 +92,20 @@ impl PluginRepositoryTrait for PluginRepositoryStorage { } } - async fn get_all(&self) -> Result, RepositoryError> { + async fn list_paginated( + &self, + query: PaginationQuery, + ) -> Result, RepositoryError> { match self { - PluginRepositoryStorage::InMemory(repo) => repo.get_all().await, - PluginRepositoryStorage::Redis(repo) => repo.get_all().await, + PluginRepositoryStorage::InMemory(repo) => repo.list_paginated(query).await, + PluginRepositoryStorage::Redis(repo) => repo.list_paginated(query).await, + } + } + + async fn count(&self) -> Result { + match self { + PluginRepositoryStorage::InMemory(repo) => repo.count().await, + PluginRepositoryStorage::Redis(repo) => repo.count().await, } } } diff --git a/src/repositories/plugin/plugin_in_memory.rs b/src/repositories/plugin/plugin_in_memory.rs index e17d23377..0b0f8ca68 100644 --- a/src/repositories/plugin/plugin_in_memory.rs +++ b/src/repositories/plugin/plugin_in_memory.rs @@ -3,8 +3,8 @@ //! The `InMemoryPluginRepository` struct is used to store and retrieve plugins //! script paths for further execution. use crate::{ - models::PluginModel, - repositories::{PluginRepositoryTrait, RepositoryError}, + models::{PaginationQuery, PluginModel}, + repositories::{PaginatedResult, PluginRepositoryTrait, RepositoryError}, }; use async_trait::async_trait; @@ -68,9 +68,34 @@ impl PluginRepositoryTrait for InMemoryPluginRepository { Ok(()) } - async fn get_all(&self) -> Result, RepositoryError> { - let store = Self::acquire_lock(&self.store).await?; - Ok(store.values().cloned().collect()) + async fn list_paginated( + &self, + query: PaginationQuery, + ) -> Result, RepositoryError> { + let total = self.count().await?; + let start = ((query.page - 1) * query.per_page) as usize; + + let items = self + .store + .lock() + .await + .values() + .skip(start) + .take(query.per_page as usize) + .cloned() + .collect(); + + Ok(PaginatedResult { + items, + total: total as u64, + page: query.page, + per_page: query.per_page, + }) + } + + async fn count(&self) -> Result { + let store = self.store.lock().await; + Ok(store.len()) } } @@ -142,31 +167,32 @@ mod tests { } #[tokio::test] - async fn test_get_all_plugins() { + async fn test_list_paginated() { let plugin_repository = Arc::new(InMemoryPluginRepository::new()); let plugin1 = PluginModel { id: "test-plugin1".to_string(), path: "test-path1".to_string(), - timeout: Duration::from_secs(69), + timeout: Duration::from_secs(DEFAULT_PLUGIN_TIMEOUT_SECONDS), }; let plugin2 = PluginModel { id: "test-plugin2".to_string(), path: "test-path2".to_string(), - timeout: Duration::from_secs(69), + timeout: Duration::from_secs(DEFAULT_PLUGIN_TIMEOUT_SECONDS), }; plugin_repository.add(plugin1.clone()).await.unwrap(); plugin_repository.add(plugin2.clone()).await.unwrap(); - let retrieved = plugin_repository.get_all().await.unwrap(); - assert_eq!(retrieved.len(), 2); - assert_eq!(retrieved[0].id, plugin1.id); - assert_eq!(retrieved[0].path, plugin1.path); - assert_eq!(retrieved[0].timeout, plugin1.timeout); - assert_eq!(retrieved[1].id, plugin2.id); - assert_eq!(retrieved[1].path, plugin2.path); - assert_eq!(retrieved[1].timeout, plugin2.timeout); + let query = PaginationQuery { + page: 1, + per_page: 2, + }; + + let result = plugin_repository.list_paginated(query).await; + assert!(result.is_ok()); + let result = result.unwrap(); + assert_eq!(result.items.len(), 2); } } diff --git a/src/repositories/plugin/plugin_redis.rs b/src/repositories/plugin/plugin_redis.rs index 272bc58f8..783f243f5 100644 --- a/src/repositories/plugin/plugin_redis.rs +++ b/src/repositories/plugin/plugin_redis.rs @@ -1,10 +1,10 @@ //! Redis-backed implementation of the PluginRepository. -use crate::models::{PluginModel, RepositoryError}; +use crate::models::{PaginationQuery, PluginModel, RepositoryError}; use crate::repositories::redis_base::RedisRepository; -use crate::repositories::PluginRepositoryTrait; +use crate::repositories::{BatchRetrievalResult, PaginatedResult, PluginRepositoryTrait}; use async_trait::async_trait; -use log::{debug, error}; +use log::{debug, error, warn}; use redis::aio::ConnectionManager; use redis::AsyncCommands; use std::fmt; @@ -43,7 +43,7 @@ impl RedisPluginRepository { format!("{}:{}:{}", self.key_prefix, PLUGIN_PREFIX, plugin_id) } - /// Generate key for plugin list: plugin_list (set of all plugin IDs) + /// Generate key for plugin list: plugin_list (paginated list of plugin IDs) fn plugin_list_key(&self) -> String { format!("{}:{}", self.key_prefix, PLUGIN_LIST_KEY) } @@ -87,6 +87,59 @@ impl RedisPluginRepository { } } } + + async fn get_by_ids( + &self, + ids: &[String], + ) -> Result, RepositoryError> { + if ids.is_empty() { + debug!("No plugin IDs provided for batch fetch"); + return Ok(BatchRetrievalResult { + results: vec![], + failed_ids: vec![], + }); + } + + let mut conn = self.client.as_ref().clone(); + let keys: Vec = ids.iter().map(|id| self.plugin_key(id)).collect(); + + let values: Vec> = conn + .mget(&keys) + .await + .map_err(|e| self.map_redis_error(e, "batch_fetch_plugins"))?; + + let mut plugins = Vec::new(); + let mut failed_count = 0; + let mut failed_ids = Vec::new(); + for (i, value) in values.into_iter().enumerate() { + match value { + Some(json) => match self.deserialize_entity(&json, &ids[i], "plugin") { + Ok(plugin) => plugins.push(plugin), + Err(e) => { + failed_count += 1; + error!("Failed to deserialize plugin {}: {}", ids[i], e); + failed_ids.push(ids[i].clone()); + } + }, + None => { + warn!("Plugin {} not found in batch fetch", ids[i]); + } + } + } + + if failed_count > 0 { + warn!( + "Failed to deserialize {} out of {} plugins in batch", + failed_count, + ids.len() + ); + } + + Ok(BatchRetrievalResult { + results: plugins, + failed_ids, + }) + } } impl fmt::Debug for RedisPluginRepository { @@ -105,25 +158,6 @@ impl PluginRepositoryTrait for RedisPluginRepository { self.get_by_id_with_connection(id, &mut conn).await } - async fn get_all(&self) -> Result, RepositoryError> { - let mut conn = self.client.as_ref().clone(); - let list_key: String = self.plugin_list_key(); - let plugin_ids: Vec = conn - .smembers(&list_key) - .await - .map_err(|e| self.map_redis_error(e, "list_all_plugins"))?; - - let mut plugins = Vec::new(); - for id in plugin_ids { - match self.get_by_id_with_connection(&id, &mut conn).await { - Ok(Some(plugin)) => plugins.push(plugin), - Ok(None) => {} - Err(e) => return Err(e), - } - } - Ok(plugins) - } - async fn add(&self, plugin: PluginModel) -> Result<(), RepositoryError> { if plugin.id.is_empty() { return Err(RepositoryError::InvalidData( @@ -173,6 +207,72 @@ impl PluginRepositoryTrait for RedisPluginRepository { debug!("Successfully added plugin with ID: {}", plugin.id); Ok(()) } + + async fn list_paginated( + &self, + query: PaginationQuery, + ) -> Result, RepositoryError> { + if query.page == 0 { + return Err(RepositoryError::InvalidData( + "Page number must be greater than 0".to_string(), + )); + } + + if query.per_page == 0 { + return Err(RepositoryError::InvalidData( + "Per page count must be greater than 0".to_string(), + )); + } + + let mut conn = self.client.as_ref().clone(); + let plugin_list_key = self.plugin_list_key(); + + // Get total count + let total: u64 = conn + .scard(&plugin_list_key) + .await + .map_err(|e| self.map_redis_error(e, "list_paginated_count"))?; + + if total == 0 { + return Ok(PaginatedResult { + items: vec![], + total: 0, + page: query.page, + per_page: query.per_page, + }); + } + + // Get all IDs and paginate in memory + let all_ids: Vec = conn + .smembers(&plugin_list_key) + .await + .map_err(|e| self.map_redis_error(e, "list_paginated_members"))?; + + let start = ((query.page - 1) * query.per_page) as usize; + let end = (start + query.per_page as usize).min(all_ids.len()); + + let ids_to_query = &all_ids[start..end]; + let items = self.get_by_ids(ids_to_query).await?; + + Ok(PaginatedResult { + items: items.results.clone(), + total, + page: query.page, + per_page: query.per_page, + }) + } + + async fn count(&self) -> Result { + let mut conn = self.client.as_ref().clone(); + let plugin_list_key = self.plugin_list_key(); + + let count: u64 = conn + .scard(&plugin_list_key) + .await + .map_err(|e| self.map_redis_error(e, "count_plugins"))?; + + Ok(count as usize) + } } #[cfg(test)] @@ -191,12 +291,19 @@ mod tests { } async fn setup_test_repo() -> RedisPluginRepository { - let client = - redis::Client::open("redis://127.0.0.1:6379/").expect("Failed to create Redis client"); - let connection_manager = redis::aio::ConnectionManager::new(client) + let redis_url = + std::env::var("REDIS_URL").unwrap_or_else(|_| "redis://127.0.0.1:6379/".to_string()); + let client = redis::Client::open(redis_url).expect("Failed to create Redis client"); + let mut connection_manager = ConnectionManager::new(client) .await .expect("Failed to create Redis connection manager"); + // Clear the plugin lists + connection_manager + .del::<&str, ()>("test_plugin:plugin_list") + .await + .unwrap(); + RedisPluginRepository::new(Arc::new(connection_manager), "test_plugin".to_string()) .expect("Failed to create Redis plugin repository") } @@ -333,26 +440,6 @@ mod tests { .contains("ID cannot be empty")); } - #[tokio::test] - #[ignore = "Requires active Redis instance"] - async fn test_get_all_plugins() { - let repo = setup_test_repo().await; - let plugin_name1 = uuid::Uuid::new_v4().to_string(); - let plugin_name2 = uuid::Uuid::new_v4().to_string(); - let plugin1 = create_test_plugin(&plugin_name1, "/path/to/plugin1"); - let plugin2 = create_test_plugin(&plugin_name2, "/path/to/plugin2"); - - repo.add(plugin1.clone()).await.unwrap(); - repo.add(plugin2.clone()).await.unwrap(); - - let retrieved = repo.get_all().await.unwrap(); - assert!(retrieved.len() == 2); - assert_eq!(retrieved[0].id, plugin1.id); - assert_eq!(retrieved[0].path, plugin1.path); - assert_eq!(retrieved[1].id, plugin2.id); - assert_eq!(retrieved[1].path, plugin2.path); - } - #[tokio::test] #[ignore = "Requires active Redis instance"] async fn test_add_plugin_with_empty_id() { @@ -380,4 +467,53 @@ mod tests { .to_string() .contains("path cannot be empty")); } + + #[tokio::test] + #[ignore = "Requires active Redis instance"] + async fn test_get_by_ids_plugins() { + let repo = setup_test_repo().await; + let plugin_name1 = uuid::Uuid::new_v4().to_string(); + let plugin_name2 = uuid::Uuid::new_v4().to_string(); + let plugin1 = create_test_plugin(&plugin_name1, "/path/to/plugin1"); + let plugin2 = create_test_plugin(&plugin_name2, "/path/to/plugin2"); + + repo.add(plugin1.clone()).await.unwrap(); + repo.add(plugin2.clone()).await.unwrap(); + + let retrieved = repo + .get_by_ids(&[plugin1.id.clone(), plugin2.id.clone()]) + .await + .unwrap(); + assert!(retrieved.results.len() == 2); + assert_eq!(retrieved.results[0].id, plugin2.id); + assert_eq!(retrieved.results[1].id, plugin1.id); + assert_eq!(retrieved.failed_ids.len(), 0); + } + + #[tokio::test] + #[ignore = "Requires active Redis instance"] + async fn test_list_paginated_plugins() { + let repo = setup_test_repo().await; + + let plugin_id1 = uuid::Uuid::new_v4().to_string(); + let plugin_id2 = uuid::Uuid::new_v4().to_string(); + let plugin_id3 = uuid::Uuid::new_v4().to_string(); + let plugin1 = create_test_plugin(&plugin_id1, "/path/to/plugin1"); + let plugin2 = create_test_plugin(&plugin_id2, "/path/to/plugin2"); + let plugin3 = create_test_plugin(&plugin_id3, "/path/to/plugin3"); + + repo.add(plugin1.clone()).await.unwrap(); + repo.add(plugin2.clone()).await.unwrap(); + repo.add(plugin3.clone()).await.unwrap(); + + let query = PaginationQuery { + page: 1, + per_page: 2, + }; + + let result = repo.list_paginated(query).await; + assert!(result.is_ok()); + let result = result.unwrap(); + assert!(result.items.len() == 2); + } } From f3b0b9b57a7b244acfac8aac2471a1a58c43a0dc Mon Sep 17 00:00:00 2001 From: Marcos Date: Fri, 11 Jul 2025 10:35:33 -0300 Subject: [PATCH 04/22] fix: Lint --- docs/modules/ROOT/pages/evm.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/modules/ROOT/pages/evm.adoc b/docs/modules/ROOT/pages/evm.adoc index dacd696f4..df371a7ac 100644 --- a/docs/modules/ROOT/pages/evm.adoc +++ b/docs/modules/ROOT/pages/evm.adoc @@ -141,7 +141,7 @@ The relayer uses a two-tier approach for gas limit estimation: 2. **Fallback Method**: When RPC estimation fails, default gas limits are applied based on transaction type: - **Simple ETH transfer** (no data): 21,000 gas - - **ERC20 transfer** (`0xa9059cbb`): 65,000 gas + - **ERC20 transfer** (`0xa9059cbb`): 65,000 gas - **ERC721/ERC20 transferFrom** (`0x23b872dd`): 80,000 gas - **Complex contracts** (all other function calls): 200,000 gas @@ -323,4 +323,4 @@ For help with EVM integration: == License -This project is licensed under the GNU Affero General Public License v3.0. \ No newline at end of file +This project is licensed under the GNU Affero General Public License v3.0. From 6b7fb426182cae262bb3a4c1aeaccf5b7263b70f Mon Sep 17 00:00:00 2001 From: Marcos Date: Thu, 24 Jul 2025 16:14:10 -0300 Subject: [PATCH 05/22] feat: Create API keys CRUD methods --- src/api/controllers/api_key.rs | 181 +++++++++++ src/api/controllers/mod.rs | 1 + src/api/controllers/plugin.rs | 17 +- src/api/routes/api_keys.rs | 51 +++ src/api/routes/mod.rs | 4 +- src/api/routes/relayer.rs | 23 +- src/bootstrap/config_processor.rs | 47 +-- src/bootstrap/initialize_app_state.rs | 13 +- src/domain/relayer/mod.rs | 12 +- src/domain/relayer/util.rs | 19 +- src/domain/transaction/util.rs | 9 +- src/models/api_key.rs | 27 ++ src/models/app_state.rs | 32 +- src/models/mod.rs | 3 + src/repositories/api_key/api_key_in_memory.rs | 115 +++++++ src/repositories/api_key/api_key_redis.rs | 301 ++++++++++++++++++ src/repositories/api_key/mod.rs | 130 ++++++++ src/repositories/mod.rs | 3 + src/services/plugins/mod.rs | 39 +-- src/services/plugins/relayer_api.rs | 50 +-- src/services/plugins/runner.rs | 35 +- src/services/plugins/socket.rs | 22 +- src/utils/mocks.rs | 17 +- 23 files changed, 1023 insertions(+), 128 deletions(-) create mode 100644 src/api/controllers/api_key.rs create mode 100644 src/api/routes/api_keys.rs create mode 100644 src/models/api_key.rs create mode 100644 src/repositories/api_key/api_key_in_memory.rs create mode 100644 src/repositories/api_key/api_key_redis.rs create mode 100644 src/repositories/api_key/mod.rs diff --git a/src/api/controllers/api_key.rs b/src/api/controllers/api_key.rs new file mode 100644 index 000000000..05d1d26ee --- /dev/null +++ b/src/api/controllers/api_key.rs @@ -0,0 +1,181 @@ +//! # Api Key Controller +//! +//! Handles HTTP endpoints for api key operations including: +//! - Create api keys +//! - List api keys +//! - Delete api keys +use crate::{ + jobs::JobProducerTrait, + models::{ + ApiError, ApiKeyModel, ApiKeyRequest, ApiKeyResponse, ApiResponse, NetworkRepoModel, + NotificationRepoModel, PaginationMeta, PaginationQuery, RelayerRepoModel, SignerRepoModel, + ThinDataAppState, TransactionRepoModel, + }, + repositories::{ + ApiKeyRepositoryTrait, NetworkRepository, PluginRepositoryTrait, RelayerRepository, + Repository, TransactionCounterTrait, TransactionRepository, + }, +}; +use actix_web::HttpResponse; +use chrono::Utc; +use eyre::Result; +use uuid::Uuid; + +/// Create api key +/// +/// # Arguments +/// +/// * `api_key_request` - The api key request. +/// * `name` - The name of the api key. +/// * `allowed_origins` - The allowed origins for the api key. +/// * `permissions` - The permissions for the api key. +/// * `state` - The application state containing the api key repository. +/// +/// # Returns +/// +/// The result of the plugin call. +pub async fn create_api_key( + api_key_request: ApiKeyRequest, + state: ThinDataAppState, +) -> Result +where + J: JobProducerTrait + Send + Sync + 'static, + RR: RelayerRepository + Repository + Send + Sync + 'static, + TR: TransactionRepository + Repository + Send + Sync + 'static, + NR: NetworkRepository + Repository + Send + Sync + 'static, + NFR: Repository + Send + Sync + 'static, + SR: Repository + Send + Sync + 'static, + TCR: TransactionCounterTrait + Send + Sync + 'static, + PR: PluginRepositoryTrait + Send + Sync + 'static, + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, +{ + let api_key = ApiKeyModel { + id: Uuid::new_v4().to_string(), + value: Uuid::new_v4().to_string(), + name: api_key_request.name, + allowed_origins: api_key_request + .allowed_origins + .unwrap_or(vec!["*".to_string()]), + permissions: api_key_request.permissions, + created_at: Utc::now().to_string(), + }; + + let api_key = state.api_key_repository.create(api_key).await?; + + Ok(HttpResponse::Ok().json(ApiResponse::success(api_key))) +} + +/// List api keys +/// +/// # Arguments +/// +/// * `query` - The pagination query parameters. +/// * `page` - The page number. +/// * `per_page` - The number of items per page. +/// * `state` - The application state containing the api key repository. +/// +/// # Returns +/// +/// The result of the api key list. +pub async fn list_api_keys( + query: PaginationQuery, + state: ThinDataAppState, +) -> Result +where + J: JobProducerTrait + Send + Sync + 'static, + RR: RelayerRepository + Repository + Send + Sync + 'static, + TR: TransactionRepository + Repository + Send + Sync + 'static, + NR: NetworkRepository + Repository + Send + Sync + 'static, + NFR: Repository + Send + Sync + 'static, + SR: Repository + Send + Sync + 'static, + TCR: TransactionCounterTrait + Send + Sync + 'static, + PR: PluginRepositoryTrait + Send + Sync + 'static, + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, +{ + let api_keys = state.api_key_repository.list_paginated(query).await?; + + let api_key_items: Vec = api_keys.items.into_iter().collect(); + + // Subtract the "value" from the api key to avoid exposing it. + let api_key_items: Vec = api_key_items + .into_iter() + .map(|api_key| ApiKeyResponse { + id: api_key.id, + name: api_key.name, + allowed_origins: api_key.allowed_origins, + created_at: api_key.created_at, + permissions: api_key.permissions, + }) + .collect(); + + Ok(HttpResponse::Ok().json(ApiResponse::paginated( + api_key_items, + PaginationMeta { + total_items: api_keys.total, + current_page: api_keys.page, + per_page: api_keys.per_page, + }, + ))) +} + +/// Get api key permissions +/// +/// # Arguments +/// +/// * `api_key_id` - The id of the api key. +/// * `state` - The application state containing the api key repository. +/// +pub async fn get_api_key_permissions( + api_key_id: String, + state: ThinDataAppState, +) -> Result +where + J: JobProducerTrait + Send + Sync + 'static, + RR: RelayerRepository + Repository + Send + Sync + 'static, + TR: TransactionRepository + Repository + Send + Sync + 'static, + NR: NetworkRepository + Repository + Send + Sync + 'static, + NFR: Repository + Send + Sync + 'static, + SR: Repository + Send + Sync + 'static, + TCR: TransactionCounterTrait + Send + Sync + 'static, + PR: PluginRepositoryTrait + Send + Sync + 'static, + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, +{ + let permissions = state + .api_key_repository + .list_permissions(&api_key_id) + .await?; + + Ok(HttpResponse::Ok().json(ApiResponse::success(permissions))) +} + +/// Delete api key +/// +/// # Arguments +/// +/// * `api_key_id` - The id of the api key. +/// * `state` - The application state containing the api key repository. +/// +/// If the API key is the last Admin API key in the system, it will return an error. +/// +pub async fn delete_api_key( + api_key_id: String, + state: ThinDataAppState, +) -> Result +where + J: JobProducerTrait + Send + Sync + 'static, + RR: RelayerRepository + Repository + Send + Sync + 'static, + TR: TransactionRepository + Repository + Send + Sync + 'static, + NR: NetworkRepository + Repository + Send + Sync + 'static, + NFR: Repository + Send + Sync + 'static, + SR: Repository + Send + Sync + 'static, + TCR: TransactionCounterTrait + Send + Sync + 'static, + PR: PluginRepositoryTrait + Send + Sync + 'static, + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, +{ + state.api_key_repository.delete_by_id(&api_key_id).await?; + + Ok(HttpResponse::Ok().json(ApiResponse::success(api_key_id))) +} + +#[cfg(test)] +mod tests {} diff --git a/src/api/controllers/mod.rs b/src/api/controllers/mod.rs index 174551326..58603b11f 100644 --- a/src/api/controllers/mod.rs +++ b/src/api/controllers/mod.rs @@ -7,5 +7,6 @@ //! * `relayer` - Transaction and relayer management endpoints //! * `plugin` - Plugin endpoints +pub mod api_key; pub mod plugin; pub mod relayer; diff --git a/src/api/controllers/plugin.rs b/src/api/controllers/plugin.rs index 542d2575e..766303e0f 100644 --- a/src/api/controllers/plugin.rs +++ b/src/api/controllers/plugin.rs @@ -10,8 +10,8 @@ use crate::{ ThinDataAppState, TransactionRepoModel, }, repositories::{ - NetworkRepository, PluginRepositoryTrait, RelayerRepository, Repository, - TransactionCounterTrait, TransactionRepository, + ApiKeyRepositoryTrait, NetworkRepository, PluginRepositoryTrait, RelayerRepository, + Repository, TransactionCounterTrait, TransactionRepository, }, services::plugins::{PluginCallResponse, PluginRunner, PluginService, PluginServiceTrait}, }; @@ -30,10 +30,10 @@ use std::sync::Arc; /// # Returns /// /// The result of the plugin call. -pub async fn call_plugin( +pub async fn call_plugin( plugin_id: String, plugin_call_request: PluginCallRequest, - state: ThinDataAppState, + state: ThinDataAppState, ) -> Result where J: JobProducerTrait + Send + Sync + 'static, @@ -44,6 +44,7 @@ where SR: Repository + Send + Sync + 'static, TCR: TransactionCounterTrait + Send + Sync + 'static, PR: PluginRepositoryTrait + Send + Sync + 'static, + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, { let plugin = state .plugin_repository @@ -75,9 +76,9 @@ where /// # Returns /// /// The result of the plugin list. -pub async fn list_plugins( +pub async fn list_plugins( query: PaginationQuery, - state: ThinDataAppState, + state: ThinDataAppState, ) -> Result where J: JobProducerTrait + Send + Sync + 'static, @@ -88,6 +89,7 @@ where SR: Repository + Send + Sync + 'static, TCR: TransactionCounterTrait + Send + Sync + 'static, PR: PluginRepositoryTrait + Send + Sync + 'static, + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, { let plugins = state.plugin_repository.list_paginated(query).await?; @@ -122,7 +124,8 @@ mod tests { path: "test-path".to_string(), timeout: Duration::from_secs(DEFAULT_PLUGIN_TIMEOUT_SECONDS), }; - let app_state = create_mock_app_state(None, None, None, Some(vec![plugin]), None).await; + let app_state = + create_mock_app_state(None, None, None, None, Some(vec![plugin]), None).await; let plugin_call_request = PluginCallRequest { params: serde_json::json!({"key":"value"}), }; diff --git a/src/api/routes/api_keys.rs b/src/api/routes/api_keys.rs new file mode 100644 index 000000000..1dd896552 --- /dev/null +++ b/src/api/routes/api_keys.rs @@ -0,0 +1,51 @@ +//! This module defines the HTTP routes for api keys operations. +//! The routes are integrated with the Actix-web framework and interact with the api key controller. +use crate::api::controllers::api_key; +use crate::models::{ApiKeyRequest, DefaultAppState, PaginationQuery}; +use actix_web::{delete, get, post, web, Responder}; + +/// List plugins +#[get("/api-keys")] +async fn list_api_keys( + query: web::Query, + data: web::ThinData, +) -> impl Responder { + api_key::list_api_keys(query.into_inner(), data).await +} + +#[get("/api-keys/{api_key_id}/permissions")] +async fn get_api_key_permissions( + api_key_id: web::Path, + data: web::ThinData, +) -> impl Responder { + api_key::get_api_key_permissions(api_key_id.into_inner(), data).await +} + +/// Calls a plugin method. +#[post("/api-keys")] +async fn create_api_key( + req: web::Json, + data: web::ThinData, +) -> impl Responder { + api_key::create_api_key(req.into_inner(), data).await +} + +#[delete("/api-keys/{api_key_id}")] +async fn delete_api_key( + api_key_id: web::Path, + data: web::ThinData, +) -> impl Responder { + api_key::delete_api_key(api_key_id.into_inner(), data).await +} + +/// Initializes the routes for api keys. +pub fn init(cfg: &mut web::ServiceConfig) { + // Register routes with literal segments before routes with path parameters + cfg.service(create_api_key); // /api-keys + cfg.service(list_api_keys); // /api-keys + cfg.service(get_api_key_permissions); // /api-keys/{api_key_id}/permissions + cfg.service(delete_api_key); // /api-keys/{api_key_id} +} + +#[cfg(test)] +mod tests {} diff --git a/src/api/routes/mod.rs b/src/api/routes/mod.rs index 1f697e56b..da02a3d54 100644 --- a/src/api/routes/mod.rs +++ b/src/api/routes/mod.rs @@ -7,6 +7,7 @@ //! * `/health` - Health check endpoints //! * `/relayers` - Relayer management endpoints +pub mod api_keys; pub mod docs; pub mod health; pub mod metrics; @@ -18,5 +19,6 @@ pub fn configure_routes(cfg: &mut web::ServiceConfig) { cfg.configure(health::init) .configure(relayer::init) .configure(plugin::init) - .configure(metrics::init); + .configure(metrics::init) + .configure(api_keys::init); } diff --git a/src/api/routes/relayer.rs b/src/api/routes/relayer.rs index be31e7539..b0359271d 100644 --- a/src/api/routes/relayer.rs +++ b/src/api/routes/relayer.rs @@ -190,11 +190,12 @@ mod tests { use crate::{ config::{EvmNetworkConfig, NetworkConfigCommon}, jobs::MockJobProducerTrait, - models::AppState, + models::{ApiKeyModel, AppState}, repositories::{ - NetworkRepositoryStorage, NotificationRepositoryStorage, PluginRepositoryStorage, - RelayerRepositoryStorage, Repository, SignerRepositoryStorage, - TransactionCounterRepositoryStorage, TransactionRepositoryStorage, + ApiKeyRepositoryStorage, ApiKeyRepositoryTrait, NetworkRepositoryStorage, + NotificationRepositoryStorage, PluginRepositoryStorage, RelayerRepositoryStorage, + Repository, SignerRepositoryStorage, TransactionCounterRepositoryStorage, + TransactionRepositoryStorage, }, }; use actix_web::{http::StatusCode, test, App}; @@ -210,11 +211,13 @@ mod tests { SignerRepositoryStorage, TransactionCounterRepositoryStorage, PluginRepositoryStorage, + ApiKeyRepositoryStorage, > { let relayer_repo = Arc::new(RelayerRepositoryStorage::new_in_memory()); let transaction_repo = Arc::new(TransactionRepositoryStorage::new_in_memory()); let signer_repo = Arc::new(SignerRepositoryStorage::new_in_memory()); let network_repo = Arc::new(NetworkRepositoryStorage::new_in_memory()); + let api_key_repo = Arc::new(ApiKeyRepositoryStorage::new_in_memory()); // Create test entities so routes don't return 404 @@ -304,6 +307,17 @@ mod tests { }; transaction_repo.create(test_transaction).await.unwrap(); + // Create test api key + let test_api_key = ApiKeyModel { + id: "test-api-key".to_string(), + name: "Test API Key".to_string(), + value: "test-value".to_string(), + permissions: vec!["test-permission".to_string()], + created_at: chrono::Utc::now().to_rfc3339(), + allowed_origins: vec!["*".to_string()], + }; + api_key_repo.create(test_api_key).await.unwrap(); + AppState { relayer_repository: relayer_repo, transaction_repository: transaction_repo, @@ -315,6 +329,7 @@ mod tests { ), job_producer: Arc::new(MockJobProducerTrait::new()), plugin_repository: Arc::new(PluginRepositoryStorage::new_in_memory()), + api_key_repository: api_key_repo, } } diff --git a/src/bootstrap/config_processor.rs b/src/bootstrap/config_processor.rs index 85cd39036..b818a948b 100644 --- a/src/bootstrap/config_processor.rs +++ b/src/bootstrap/config_processor.rs @@ -12,8 +12,8 @@ use crate::{ ThinDataAppState, TransactionRepoModel, TurnkeySignerConfig, VaultTransitSignerConfig, }, repositories::{ - NetworkRepository, PluginRepositoryTrait, RelayerRepository, Repository, - TransactionCounterTrait, TransactionRepository, + ApiKeyRepositoryTrait, NetworkRepository, PluginRepositoryTrait, RelayerRepository, + Repository, TransactionCounterTrait, TransactionRepository, }, services::{Signer, SignerFactory, VaultConfig, VaultService, VaultServiceTrait}, utils::unsafe_generate_random_private_key, @@ -25,9 +25,9 @@ use secrets::SecretVec; use zeroize::Zeroizing; /// Process all plugins from the config file and store them in the repository. -async fn process_plugins( +async fn process_plugins( config_file: &Config, - app_state: &ThinDataAppState, + app_state: &ThinDataAppState, ) -> Result<()> where J: JobProducerTrait + Send + Sync + 'static, @@ -38,6 +38,7 @@ where SR: Repository + Send + Sync + 'static, TCR: TransactionCounterTrait + Send + Sync + 'static, PR: PluginRepositoryTrait + Send + Sync + 'static, + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, { if let Some(plugins) = &config_file.plugins { let plugin_futures = plugins.iter().map(|plugin| async { @@ -241,9 +242,9 @@ async fn process_signer(signer: &SignerFileConfig) -> Result { /// 2. Store the resulting model in the repository /// /// This function processes signers in parallel using futures. -async fn process_signers( +async fn process_signers( config_file: &Config, - app_state: &ThinDataAppState, + app_state: &ThinDataAppState, ) -> Result<()> where J: JobProducerTrait + Send + Sync + 'static, @@ -254,6 +255,7 @@ where SR: Repository + Send + Sync + 'static, TCR: TransactionCounterTrait + Send + Sync + 'static, PR: PluginRepositoryTrait + Send + Sync + 'static, + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, { let signer_futures = config_file.signers.iter().map(|signer| async { let signer_repo_model = process_signer(signer).await?; @@ -279,9 +281,9 @@ where /// 2. Store the resulting model in the repository /// /// This function processes notifications in parallel using futures. -async fn process_notifications( +async fn process_notifications( config_file: &Config, - app_state: &ThinDataAppState, + app_state: &ThinDataAppState, ) -> Result<()> where J: JobProducerTrait + Send + Sync + 'static, @@ -292,6 +294,7 @@ where SR: Repository + Send + Sync + 'static, TCR: TransactionCounterTrait + Send + Sync + 'static, PR: PluginRepositoryTrait + Send + Sync + 'static, + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, { let notification_futures = config_file.notifications.iter().map(|notification| async { let notification_repo_model = NotificationRepoModel::try_from(notification.clone()) @@ -318,9 +321,9 @@ where /// 2. Store the resulting model in the repository /// /// This function processes networks in parallel using futures. -async fn process_networks( +async fn process_networks( config_file: &Config, - app_state: &ThinDataAppState, + app_state: &ThinDataAppState, ) -> Result<()> where J: JobProducerTrait + Send + Sync + 'static, @@ -331,6 +334,7 @@ where SR: Repository + Send + Sync + 'static, TCR: TransactionCounterTrait + Send + Sync + 'static, PR: PluginRepositoryTrait + Send + Sync + 'static, + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, { let network_futures = config_file.networks.iter().map(|network| async move { let network_repo_model = NetworkRepoModel::try_from(network.clone())?; @@ -359,9 +363,9 @@ where /// 5. Store the resulting model in the repository /// /// This function processes relayers in parallel using futures. -async fn process_relayers( +async fn process_relayers( config_file: &Config, - app_state: &ThinDataAppState, + app_state: &ThinDataAppState, ) -> Result<()> where J: JobProducerTrait + Send + Sync + 'static, @@ -372,6 +376,7 @@ where SR: Repository + Send + Sync + 'static, TCR: TransactionCounterTrait + Send + Sync + 'static, PR: PluginRepositoryTrait + Send + Sync + 'static, + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, { let signers = app_state.signer_repository.list_all().await?; @@ -411,9 +416,9 @@ where /// 2. Process notifications /// 3. Process networks /// 4. Process relayers -pub async fn process_config_file( +pub async fn process_config_file( config_file: Config, - app_state: ThinDataAppState, + app_state: ThinDataAppState, ) -> Result<()> where J: JobProducerTrait + Send + Sync + 'static, @@ -424,6 +429,7 @@ where SR: Repository + Send + Sync + 'static, TCR: TransactionCounterTrait + Send + Sync + 'static, PR: PluginRepositoryTrait + Send + Sync + 'static, + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, { process_plugins(&config_file, &app_state).await?; process_signers(&config_file, &app_state).await?; @@ -447,10 +453,11 @@ mod tests { jobs::MockJobProducerTrait, models::{AppState, NetworkType, PlainOrEnvValue, SecretString}, repositories::{ - InMemoryNetworkRepository, InMemoryNotificationRepository, InMemoryPluginRepository, - InMemorySignerRepository, InMemoryTransactionCounter, InMemoryTransactionRepository, - NetworkRepositoryStorage, NotificationRepositoryStorage, PluginRepositoryStorage, - RelayerRepositoryStorage, SignerRepositoryStorage, TransactionCounterRepositoryStorage, + ApiKeyRepositoryStorage, InMemoryApiKeyRepository, InMemoryNetworkRepository, + InMemoryNotificationRepository, InMemoryPluginRepository, InMemorySignerRepository, + InMemoryTransactionCounter, InMemoryTransactionRepository, NetworkRepositoryStorage, + NotificationRepositoryStorage, PluginRepositoryStorage, RelayerRepositoryStorage, + SignerRepositoryStorage, TransactionCounterRepositoryStorage, TransactionRepositoryStorage, }, }; @@ -469,6 +476,7 @@ mod tests { SignerRepositoryStorage, TransactionCounterRepositoryStorage, PluginRepositoryStorage, + ApiKeyRepositoryStorage, > { // Create a mock job producer let mut mock_job_producer = MockJobProducerTrait::new(); @@ -501,6 +509,7 @@ mod tests { ), job_producer: Arc::new(mock_job_producer), plugin_repository: Arc::new(PluginRepositoryStorage::new_in_memory()), + api_key_repository: Arc::new(ApiKeyRepositoryStorage::new_in_memory()), } } @@ -1186,6 +1195,7 @@ mod tests { )); let transaction_counter = Arc::new(InMemoryTransactionCounter::default()); let plugin_repo = Arc::new(InMemoryPluginRepository::default()); + let api_key_repo = Arc::new(InMemoryApiKeyRepository::default()); // Create a mock job producer let mut mock_job_producer = MockJobProducerTrait::new(); @@ -1213,6 +1223,7 @@ mod tests { transaction_counter_store: transaction_counter.clone(), job_producer: job_producer.clone(), plugin_repository: plugin_repo.clone(), + api_key_repository: api_key_repo.clone(), }); // Process the entire config file diff --git a/src/bootstrap/initialize_app_state.rs b/src/bootstrap/initialize_app_state.rs index 967449935..34abebf8f 100644 --- a/src/bootstrap/initialize_app_state.rs +++ b/src/bootstrap/initialize_app_state.rs @@ -7,9 +7,9 @@ use crate::{ jobs::{self, Queue}, models::{AppState, DefaultAppState}, repositories::{ - NetworkRepositoryStorage, NotificationRepositoryStorage, PluginRepositoryStorage, - RelayerRepositoryStorage, SignerRepositoryStorage, TransactionCounterRepositoryStorage, - TransactionRepositoryStorage, + ApiKeyRepositoryStorage, NetworkRepositoryStorage, NotificationRepositoryStorage, + PluginRepositoryStorage, RelayerRepositoryStorage, SignerRepositoryStorage, + TransactionCounterRepositoryStorage, TransactionRepositoryStorage, }, }; use actix_web::web; @@ -26,6 +26,7 @@ pub struct RepositoryCollection { pub network: Arc, pub transaction_counter: Arc, pub plugin: Arc, + pub api_key: Arc, } /// Initializes repositories based on the server configuration @@ -45,6 +46,7 @@ pub async fn initialize_repositories(config: &ServerConfig) -> eyre::Result { warn!("Redis repository storage support is experimental"); @@ -88,6 +90,10 @@ pub async fn initialize_repositories(config: &ServerConfig) -> eyre::Result + Send + Sync + 'static, TCR: TransactionCounterTrait + Send + Sync + 'static, PR: PluginRepositoryTrait + Send + Sync + 'static, + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, > { async fn create_relayer( relayer: RelayerRepoModel, signer: SignerRepoModel, - state: &ThinData>, + state: &ThinData>, ) -> Result, RelayerError>; } @@ -344,12 +345,13 @@ impl< SR: Repository + Send + Sync + 'static, TCR: TransactionCounterTrait + Send + Sync + 'static, PR: PluginRepositoryTrait + Send + Sync + 'static, - > RelayerFactoryTrait for RelayerFactory + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, + > RelayerFactoryTrait for RelayerFactory { async fn create_relayer( relayer: RelayerRepoModel, signer: SignerRepoModel, - state: &ThinData>, + state: &ThinData>, ) -> Result, RelayerError> { match relayer.network_type { NetworkType::Evm => { diff --git a/src/domain/relayer/util.rs b/src/domain/relayer/util.rs index 032866a1e..ce5a7378c 100644 --- a/src/domain/relayer/util.rs +++ b/src/domain/relayer/util.rs @@ -19,8 +19,8 @@ use crate::{ SignerRepoModel, ThinDataAppState, TransactionRepoModel, }, repositories::{ - NetworkRepository, PluginRepositoryTrait, RelayerRepository, Repository, - TransactionCounterTrait, TransactionRepository, + ApiKeyRepositoryTrait, NetworkRepository, PluginRepositoryTrait, RelayerRepository, + Repository, TransactionCounterTrait, TransactionRepository, }, }; @@ -37,9 +37,9 @@ use super::NetworkRelayer; /// /// * `Result` - Returns a `RelayerRepoModel` on success, or an /// `ApiError` on failure. -pub async fn get_relayer_by_id( +pub async fn get_relayer_by_id( relayer_id: String, - state: &ThinDataAppState, + state: &ThinDataAppState, ) -> Result where J: JobProducerTrait + Send + Sync + 'static, @@ -50,6 +50,7 @@ where SR: Repository + Send + Sync + 'static, TCR: TransactionCounterTrait + Send + Sync + 'static, PR: PluginRepositoryTrait + Send + Sync + 'static, + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, { state .relayer_repository @@ -69,9 +70,9 @@ where /// /// * `Result` - Returns a `NetworkRelayer` on success, or an `ApiError` /// on failure. -pub async fn get_network_relayer( +pub async fn get_network_relayer( relayer_id: String, - state: &ThinDataAppState, + state: &ThinDataAppState, ) -> Result, ApiError> where J: JobProducerTrait + Send + Sync + 'static, @@ -82,6 +83,7 @@ where SR: Repository + Send + Sync + 'static, TCR: TransactionCounterTrait + Send + Sync + 'static, PR: PluginRepositoryTrait + Send + Sync + 'static, + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, { let relayer_model = get_relayer_by_id(relayer_id.clone(), state).await?; let signer_model = state @@ -105,9 +107,9 @@ where /// /// * `Result` - Returns a `NetworkRelayer` on success, or an `ApiError` /// on failure. -pub async fn get_network_relayer_by_model( +pub async fn get_network_relayer_by_model( relayer_model: RelayerRepoModel, - state: &ThinDataAppState, + state: &ThinDataAppState, ) -> Result, ApiError> where J: JobProducerTrait + Send + Sync + 'static, @@ -118,6 +120,7 @@ where SR: Repository + Send + Sync + 'static, TCR: TransactionCounterTrait + Send + Sync + 'static, PR: PluginRepositoryTrait + Send + Sync + 'static, + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, { let signer_model = state .signer_repository diff --git a/src/domain/transaction/util.rs b/src/domain/transaction/util.rs index 68d541c2e..e2f450551 100644 --- a/src/domain/transaction/util.rs +++ b/src/domain/transaction/util.rs @@ -13,8 +13,8 @@ use crate::{ SignerRepoModel, ThinDataAppState, TransactionError, TransactionRepoModel, }, repositories::{ - NetworkRepository, PluginRepositoryTrait, RelayerRepository, Repository, - TransactionCounterTrait, TransactionRepository, + ApiKeyRepositoryTrait, NetworkRepository, PluginRepositoryTrait, RelayerRepository, + Repository, TransactionCounterTrait, TransactionRepository, }, }; @@ -31,9 +31,9 @@ use super::{NetworkTransaction, RelayerTransactionFactory}; /// /// A `Result` containing a `TransactionRepoModel` if successful, or an `ApiError` if an error /// occurs. -pub async fn get_transaction_by_id( +pub async fn get_transaction_by_id( transaction_id: String, - state: &ThinDataAppState, + state: &ThinDataAppState, ) -> Result where J: JobProducerTrait + Send + Sync + 'static, @@ -44,6 +44,7 @@ where SR: Repository + Send + Sync + 'static, TCR: TransactionCounterTrait + Send + Sync + 'static, PR: PluginRepositoryTrait + Send + Sync + 'static, + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, { state .transaction_repository diff --git a/src/models/api_key.rs b/src/models/api_key.rs new file mode 100644 index 000000000..5bcd77317 --- /dev/null +++ b/src/models/api_key.rs @@ -0,0 +1,27 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize)] +pub struct ApiKeyRequest { + pub name: String, + pub permissions: Vec, + pub allowed_origins: Option>, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ApiKeyModel { + pub id: String, + pub value: String, + pub name: String, + pub allowed_origins: Vec, + pub created_at: String, + pub permissions: Vec, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ApiKeyResponse { + pub id: String, + pub name: String, + pub allowed_origins: Vec, + pub created_at: String, + pub permissions: Vec, +} diff --git a/src/models/app_state.rs b/src/models/app_state.rs index 4ad8a474a..46dcb11c0 100644 --- a/src/models/app_state.rs +++ b/src/models/app_state.rs @@ -12,11 +12,11 @@ use crate::{ TransactionRepoModel, }, repositories::{ - NetworkRepository, NetworkRepositoryStorage, NotificationRepositoryStorage, - PluginRepositoryStorage, PluginRepositoryTrait, RelayerRepository, - RelayerRepositoryStorage, Repository, SignerRepositoryStorage, - TransactionCounterRepositoryStorage, TransactionCounterTrait, TransactionRepository, - TransactionRepositoryStorage, + ApiKeyRepositoryStorage, ApiKeyRepositoryTrait, NetworkRepository, + NetworkRepositoryStorage, NotificationRepositoryStorage, PluginRepositoryStorage, + PluginRepositoryTrait, RelayerRepository, RelayerRepositoryStorage, Repository, + SignerRepositoryStorage, TransactionCounterRepositoryStorage, TransactionCounterTrait, + TransactionRepository, TransactionRepositoryStorage, }, }; @@ -32,6 +32,7 @@ pub struct AppState< SR: Repository + Send + Sync + 'static, TCR: TransactionCounterTrait + Send + Sync + 'static, PR: PluginRepositoryTrait + Send + Sync + 'static, + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, > { /// Repository for managing relayer data. pub relayer_repository: Arc, @@ -49,11 +50,13 @@ pub struct AppState< pub job_producer: Arc, /// Repository for managing plugins. pub plugin_repository: Arc, + /// Repository for managing api keys. + pub api_key_repository: Arc, } /// type alias for the app state wrapped in a ThinData to avoid clippy warnings -pub type ThinDataAppState = - ThinData>; +pub type ThinDataAppState = + ThinData>; pub type DefaultAppState = AppState< JobProducer, @@ -64,6 +67,7 @@ pub type DefaultAppState = AppState< SignerRepositoryStorage, TransactionCounterRepositoryStorage, PluginRepositoryStorage, + ApiKeyRepositoryStorage, >; impl< @@ -75,7 +79,8 @@ impl< SR: Repository + Send + Sync + 'static, TCR: TransactionCounterTrait + Send + Sync + 'static, PR: PluginRepositoryTrait + Send + Sync + 'static, - > AppState + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, + > AppState { /// Returns a clone of the relayer repository. /// @@ -148,6 +153,15 @@ impl< pub fn plugin_repository(&self) -> Arc { Arc::clone(&self.plugin_repository) } + + /// Returns a clone of the api key repository. + /// + /// # Returns + /// + /// An `Arc` pointing to the `InMemoryApiKeyRepository`. + pub fn api_key_repository(&self) -> Arc { + Arc::clone(&self.api_key_repository) + } } #[cfg(test)] @@ -166,6 +180,7 @@ mod tests { SignerRepositoryStorage, TransactionCounterRepositoryStorage, PluginRepositoryStorage, + ApiKeyRepositoryStorage, > { // Create a mock job producer let mut mock_job_producer = MockJobProducerTrait::new(); @@ -198,6 +213,7 @@ mod tests { ), job_producer: Arc::new(mock_job_producer), plugin_repository: Arc::new(PluginRepositoryStorage::new_in_memory()), + api_key_repository: Arc::new(ApiKeyRepositoryStorage::new_in_memory()), } } diff --git a/src/models/mod.rs b/src/models/mod.rs index 305793516..a09b64d91 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -46,3 +46,6 @@ pub use plain_or_env_value::*; mod plugin; pub use plugin::*; + +mod api_key; +pub use api_key::*; diff --git a/src/repositories/api_key/api_key_in_memory.rs b/src/repositories/api_key/api_key_in_memory.rs new file mode 100644 index 000000000..e5d772cc8 --- /dev/null +++ b/src/repositories/api_key/api_key_in_memory.rs @@ -0,0 +1,115 @@ +//! This module provides an in-memory implementation of api keys. +//! +//! The `InMemoryApiKeyRepository` struct is used to store and retrieve api keys +//! permissions. +use crate::{ + models::{ApiKeyModel, PaginationQuery}, + repositories::{ApiKeyRepositoryTrait, PaginatedResult, RepositoryError}, +}; + +use async_trait::async_trait; + +use std::collections::HashMap; +use tokio::sync::{Mutex, MutexGuard}; + +#[derive(Debug)] +pub struct InMemoryApiKeyRepository { + store: Mutex>, +} + +impl Clone for InMemoryApiKeyRepository { + fn clone(&self) -> Self { + // Try to get the current data, or use empty HashMap if lock fails + let data = self + .store + .try_lock() + .map(|guard| guard.clone()) + .unwrap_or_else(|_| HashMap::new()); + + Self { + store: Mutex::new(data), + } + } +} + +impl InMemoryApiKeyRepository { + pub fn new() -> Self { + Self { + store: Mutex::new(HashMap::new()), + } + } + + async fn acquire_lock(lock: &Mutex) -> Result, RepositoryError> { + Ok(lock.lock().await) + } +} + +impl Default for InMemoryApiKeyRepository { + fn default() -> Self { + Self::new() + } +} + +#[async_trait] +impl ApiKeyRepositoryTrait for InMemoryApiKeyRepository { + async fn create(&self, api_key: ApiKeyModel) -> Result { + let mut store = Self::acquire_lock(&self.store).await?; + store.insert(api_key.id.clone(), api_key.clone()); + Ok(api_key) + } + + async fn get_by_id(&self, id: &str) -> Result, RepositoryError> { + let store = Self::acquire_lock(&self.store).await?; + Ok(store.get(id).cloned()) + } + + async fn list_paginated( + &self, + query: PaginationQuery, + ) -> Result, RepositoryError> { + let total = self.count().await?; + let start = ((query.page - 1) * query.per_page) as usize; + + let items = self + .store + .lock() + .await + .values() + .skip(start) + .take(query.per_page as usize) + .cloned() + .collect(); + + Ok(PaginatedResult { + items, + total: total as u64, + page: query.page, + per_page: query.per_page, + }) + } + + async fn count(&self) -> Result { + let store = self.store.lock().await; + Ok(store.len()) + } + + async fn list_permissions(&self, api_key_id: &str) -> Result, RepositoryError> { + let store = self.store.lock().await; + let api_key = store + .get(api_key_id) + .ok_or(RepositoryError::NotFound(format!( + "Api key with id {} not found", + api_key_id + )))?; + Ok(api_key.permissions.clone()) + } + + async fn delete_by_id(&self, api_key_id: &str) -> Result<(), RepositoryError> { + let mut store = self.store.lock().await; + store.remove(api_key_id); + Ok(()) + } +} + +#[cfg(test)] +mod tests {} diff --git a/src/repositories/api_key/api_key_redis.rs b/src/repositories/api_key/api_key_redis.rs new file mode 100644 index 000000000..4fdcec6e3 --- /dev/null +++ b/src/repositories/api_key/api_key_redis.rs @@ -0,0 +1,301 @@ +//! Redis-backed implementation of the ApiKeyRepository. + +use crate::models::{ApiKeyModel, PaginationQuery, RepositoryError}; +use crate::repositories::redis_base::RedisRepository; +use crate::repositories::{ApiKeyRepositoryTrait, BatchRetrievalResult, PaginatedResult}; +use async_trait::async_trait; +use log::{debug, error, warn}; +use redis::aio::ConnectionManager; +use redis::AsyncCommands; +use std::fmt; +use std::sync::Arc; + +const API_KEY_PREFIX: &str = "apikey"; +const API_KEY_LIST_KEY: &str = "apikey_list"; + +#[derive(Clone)] +pub struct RedisApiKeyRepository { + pub client: Arc, + pub key_prefix: String, +} + +impl RedisRepository for RedisApiKeyRepository {} + +impl RedisApiKeyRepository { + pub fn new( + connection_manager: Arc, + key_prefix: String, + ) -> Result { + if key_prefix.is_empty() { + return Err(RepositoryError::InvalidData( + "Redis key prefix cannot be empty".to_string(), + )); + } + + Ok(Self { + client: connection_manager, + key_prefix, + }) + } + + /// Generate key for api key data: apikey:{api_key_id} + fn api_key_key(&self, api_key_id: &str) -> String { + format!("{}:{}:{}", self.key_prefix, API_KEY_PREFIX, api_key_id) + } + + /// Generate key for api key list: apikey_list (paginated list of api key IDs) + fn api_key_list_key(&self) -> String { + format!("{}:{}", self.key_prefix, API_KEY_LIST_KEY) + } + + async fn get_by_ids( + &self, + ids: &[String], + ) -> Result, RepositoryError> { + if ids.is_empty() { + debug!("No api key IDs provided for batch fetch"); + return Ok(BatchRetrievalResult { + results: vec![], + failed_ids: vec![], + }); + } + + let mut conn = self.client.as_ref().clone(); + let keys: Vec = ids.iter().map(|id| self.api_key_key(id)).collect(); + + let values: Vec> = conn + .mget(&keys) + .await + .map_err(|e| self.map_redis_error(e, "batch_fetch_api_keys"))?; + + let mut apikeys = Vec::new(); + let mut failed_count = 0; + let mut failed_ids = Vec::new(); + for (i, value) in values.into_iter().enumerate() { + match value { + Some(json) => match self.deserialize_entity(&json, &ids[i], "apikey") { + Ok(apikey) => apikeys.push(apikey), + Err(e) => { + failed_count += 1; + error!("Failed to deserialize api key {}: {}", ids[i], e); + failed_ids.push(ids[i].clone()); + } + }, + None => { + warn!("Plugin {} not found in batch fetch", ids[i]); + } + } + } + + if failed_count > 0 { + warn!( + "Failed to deserialize {} out of {} api keys in batch", + failed_count, + ids.len() + ); + } + + Ok(BatchRetrievalResult { + results: apikeys, + failed_ids, + }) + } +} + +impl fmt::Debug for RedisApiKeyRepository { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "RedisApiKeyRepository {{ key_prefix: {} }}", + self.key_prefix + ) + } +} + +#[async_trait] +impl ApiKeyRepositoryTrait for RedisApiKeyRepository { + async fn create(&self, entity: ApiKeyModel) -> Result { + if entity.id.is_empty() { + return Err(RepositoryError::InvalidData( + "API Key ID cannot be empty".to_string(), + )); + } + + let key = self.api_key_key(&entity.id); + let json = self.serialize_entity(&entity, |a| &a.id, "apikey")?; + + let mut conn = self.client.as_ref().clone(); + + let existing: Option = conn + .get(&key) + .await + .map_err(|e| self.map_redis_error(e, "create_api_key_check"))?; + + if existing.is_some() { + return Err(RepositoryError::ConstraintViolation(format!( + "API Key with ID {} already exists", + entity.id + ))); + } + + // Use atomic pipeline for consistency + let mut pipe = redis::pipe(); + pipe.atomic(); + pipe.set(&key, json); + + pipe.exec_async(&mut conn) + .await + .map_err(|e| self.map_redis_error(e, "create_api_key"))?; + + debug!("Successfully created API Key {}", entity.id); + Ok(entity) + } + + async fn list_paginated( + &self, + query: PaginationQuery, + ) -> Result, RepositoryError> { + if query.page == 0 { + return Err(RepositoryError::InvalidData( + "Page number must be greater than 0".to_string(), + )); + } + + if query.per_page == 0 { + return Err(RepositoryError::InvalidData( + "Per page count must be greater than 0".to_string(), + )); + } + let mut conn = self.client.as_ref().clone(); + let api_key_list_key = self.api_key_list_key(); + + // Get total count + let total: u64 = conn + .scard(&api_key_list_key) + .await + .map_err(|e| self.map_redis_error(e, "list_paginated_count"))?; + + if total == 0 { + return Ok(PaginatedResult { + items: vec![], + total: 0, + page: query.page, + per_page: query.per_page, + }); + } + + // Get all IDs and paginate in memory + let all_ids: Vec = conn + .smembers(&api_key_list_key) + .await + .map_err(|e| self.map_redis_error(e, "list_paginated_members"))?; + + let start = ((query.page - 1) * query.per_page) as usize; + let end = (start + query.per_page as usize).min(all_ids.len()); + + let ids_to_query = &all_ids[start..end]; + let items = self.get_by_ids(ids_to_query).await?; + + Ok(PaginatedResult { + items: items.results.clone(), + total, + page: query.page, + per_page: query.per_page, + }) + } + + async fn get_by_id(&self, id: &str) -> Result, RepositoryError> { + if id.is_empty() { + return Err(RepositoryError::InvalidData( + "API Key ID cannot be empty".to_string(), + )); + } + + let mut conn = self.client.as_ref().clone(); + let api_key_key = self.api_key_key(id); + + debug!("Fetching api key with ID: {}", id); + + let json: Option = conn + .get(&api_key_key) + .await + .map_err(|e| self.map_redis_error(e, "get_api_key_by_id"))?; + + match json { + Some(json) => { + debug!("Found api key with ID: {}", id); + self.deserialize_entity(&json, id, "apikey") + } + None => { + debug!("Api key with ID {} not found", id); + Ok(None) + } + } + } + + async fn list_permissions(&self, api_key_id: &str) -> Result, RepositoryError> { + let api_key = self.get_by_id(api_key_id).await?; + match api_key { + Some(api_key) => Ok(api_key.permissions), + None => Err(RepositoryError::NotFound(format!( + "Api key with ID {} not found", + api_key_id + ))), + } + } + + async fn delete_by_id(&self, id: &str) -> Result<(), RepositoryError> { + if id.is_empty() { + return Err(RepositoryError::InvalidData( + "API Key ID cannot be empty".to_string(), + )); + } + + let key = self.api_key_key(id); + let api_key_list_key = self.api_key_list_key(); + let mut conn = self.client.as_ref().clone(); + + debug!("Deleting api key with ID: {}", id); + + // Check if api key exists + let existing: Option = conn + .get(&key) + .await + .map_err(|e| self.map_redis_error(e, "delete_api_key_check"))?; + + if existing.is_none() { + return Err(RepositoryError::NotFound(format!( + "Api key with ID {} not found", + id + ))); + } + + // Use atomic pipeline to ensure consistency + let mut pipe = redis::pipe(); + pipe.atomic(); + pipe.del(&key); + pipe.srem(&api_key_list_key, id); + + pipe.exec_async(&mut conn) + .await + .map_err(|e| self.map_redis_error(e, "delete_api_key"))?; + + debug!("Successfully deleted api key {}", id); + Ok(()) + } + + async fn count(&self) -> Result { + let mut conn = self.client.as_ref().clone(); + let api_key_list_key = self.api_key_list_key(); + + let count: u64 = conn + .scard(&api_key_list_key) + .await + .map_err(|e| self.map_redis_error(e, "count_api_keys"))?; + + Ok(count as usize) + } +} + +#[cfg(test)] +mod tests {} diff --git a/src/repositories/api_key/mod.rs b/src/repositories/api_key/mod.rs new file mode 100644 index 000000000..8e25c991d --- /dev/null +++ b/src/repositories/api_key/mod.rs @@ -0,0 +1,130 @@ +//! API Key Repository Module +//! +//! This module provides the api key repository for the OpenZeppelin Relayer service. +//! It implements a specialized repository pattern for managing api key configurations, +//! supporting both in-memory and Redis-backed storage implementations. +//! +//! ## Repository Implementations +//! +//! - [`InMemoryApiKeyRepository`]: In-memory storage for testing/development +//! - [`RedisApiKeyRepository`]: Redis-backed storage for production environments +//! +//! ## API Keys +//! +//! The api key system allows extending relayer authorization scheme through api keys. +//! Each api key is identified by a unique ID and contains a list of permissions that +//! restrict the api key's access to the server. +//! +use async_trait::async_trait; +use redis::aio::ConnectionManager; +use std::sync::Arc; + +pub mod api_key_in_memory; +pub mod api_key_redis; + +pub use api_key_in_memory::*; +pub use api_key_redis::*; + +#[cfg(test)] +use mockall::automock; + +use crate::{ + models::{ApiKeyModel, PaginationQuery, RepositoryError}, + repositories::PaginatedResult, +}; + +#[async_trait] +#[allow(dead_code)] +#[cfg_attr(test, automock)] +pub trait ApiKeyRepositoryTrait { + async fn get_by_id(&self, id: &str) -> Result, RepositoryError>; + async fn create(&self, api_key: ApiKeyModel) -> Result; + async fn list_paginated( + &self, + query: PaginationQuery, + ) -> Result, RepositoryError>; + async fn count(&self) -> Result; + async fn list_permissions(&self, api_key_id: &str) -> Result, RepositoryError>; + async fn delete_by_id(&self, api_key_id: &str) -> Result<(), RepositoryError>; +} + +/// Enum wrapper for different plugin repository implementations +#[derive(Debug, Clone)] +pub enum ApiKeyRepositoryStorage { + InMemory(InMemoryApiKeyRepository), + Redis(RedisApiKeyRepository), +} + +impl ApiKeyRepositoryStorage { + pub fn new_in_memory() -> Self { + Self::InMemory(InMemoryApiKeyRepository::new()) + } + + pub fn new_redis( + connection_manager: Arc, + key_prefix: String, + ) -> Result { + let redis_repo = RedisApiKeyRepository::new(connection_manager, key_prefix)?; + Ok(Self::Redis(redis_repo)) + } +} + +#[async_trait] +impl ApiKeyRepositoryTrait for ApiKeyRepositoryStorage { + async fn get_by_id(&self, id: &str) -> Result, RepositoryError> { + match self { + ApiKeyRepositoryStorage::InMemory(repo) => repo.get_by_id(id).await, + ApiKeyRepositoryStorage::Redis(repo) => repo.get_by_id(id).await, + } + } + + async fn create(&self, api_key: ApiKeyModel) -> Result { + match self { + ApiKeyRepositoryStorage::InMemory(repo) => repo.create(api_key).await, + ApiKeyRepositoryStorage::Redis(repo) => repo.create(api_key).await, + } + } + + async fn list_permissions(&self, api_key_id: &str) -> Result, RepositoryError> { + match self { + ApiKeyRepositoryStorage::InMemory(repo) => repo.list_permissions(api_key_id).await, + ApiKeyRepositoryStorage::Redis(repo) => repo.list_permissions(api_key_id).await, + } + } + + async fn delete_by_id(&self, api_key_id: &str) -> Result<(), RepositoryError> { + match self { + ApiKeyRepositoryStorage::InMemory(repo) => repo.delete_by_id(api_key_id).await, + ApiKeyRepositoryStorage::Redis(repo) => repo.delete_by_id(api_key_id).await, + } + } + + async fn list_paginated( + &self, + query: PaginationQuery, + ) -> Result, RepositoryError> { + match self { + ApiKeyRepositoryStorage::InMemory(repo) => repo.list_paginated(query).await, + ApiKeyRepositoryStorage::Redis(repo) => repo.list_paginated(query).await, + } + } + + async fn count(&self) -> Result { + match self { + ApiKeyRepositoryStorage::InMemory(repo) => repo.count().await, + ApiKeyRepositoryStorage::Redis(repo) => repo.count().await, + } + } +} + +impl PartialEq for ApiKeyModel { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + && self.name == other.name + && self.allowed_origins == other.allowed_origins + && self.permissions == other.permissions + } +} + +#[cfg(test)] +mod tests {} diff --git a/src/repositories/mod.rs b/src/repositories/mod.rs index cd5904500..2310ae878 100644 --- a/src/repositories/mod.rs +++ b/src/repositories/mod.rs @@ -29,6 +29,9 @@ pub use network::*; mod plugin; pub use plugin::*; +pub mod api_key; +pub use api_key::*; + // Redis base utilities for shared functionality pub mod redis_base; diff --git a/src/services/plugins/mod.rs b/src/services/plugins/mod.rs index 70421bc9f..23b1854c8 100644 --- a/src/services/plugins/mod.rs +++ b/src/services/plugins/mod.rs @@ -9,8 +9,8 @@ use crate::{ RelayerRepoModel, SignerRepoModel, ThinDataAppState, TransactionRepoModel, }, repositories::{ - NetworkRepository, PluginRepositoryTrait, RelayerRepository, Repository, - TransactionCounterTrait, TransactionRepository, + ApiKeyRepositoryTrait, NetworkRepository, PluginRepositoryTrait, RelayerRepository, + Repository, TransactionCounterTrait, TransactionRepository, }, }; use actix_web::web; @@ -87,11 +87,11 @@ impl PluginService { } #[allow(clippy::type_complexity)] - async fn call_plugin( + async fn call_plugin( &self, plugin: PluginModel, plugin_call_request: PluginCallRequest, - state: Arc>, + state: Arc>, ) -> Result where J: JobProducerTrait + Send + Sync + 'static, @@ -106,6 +106,7 @@ impl PluginService { SR: Repository + Send + Sync + 'static, TCR: TransactionCounterTrait + Send + Sync + 'static, PR: PluginRepositoryTrait + Send + Sync + 'static, + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, { let socket_path = format!("/tmp/{}.sock", Uuid::new_v4()); let script_path = Self::resolve_plugin_path(&plugin.path); @@ -138,7 +139,7 @@ impl PluginService { #[async_trait] #[cfg_attr(test, automock)] -pub trait PluginServiceTrait: Send + Sync +pub trait PluginServiceTrait: Send + Sync where J: JobProducerTrait + 'static, TR: TransactionRepository + Repository + Send + Sync + 'static, @@ -148,18 +149,19 @@ where SR: Repository + Send + Sync + 'static, TCR: TransactionCounterTrait + Send + Sync + 'static, PR: PluginRepositoryTrait + Send + Sync + 'static, + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, { fn new(runner: PluginRunner) -> Self; async fn call_plugin( &self, plugin: PluginModel, plugin_call_request: PluginCallRequest, - state: Arc>>, + state: Arc>>, ) -> Result; } #[async_trait] -impl PluginServiceTrait +impl PluginServiceTrait for PluginService where J: JobProducerTrait + 'static, @@ -170,6 +172,7 @@ where SR: Repository + Send + Sync + 'static, TCR: TransactionCounterTrait + Send + Sync + 'static, PR: PluginRepositoryTrait + Send + Sync + 'static, + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, { fn new(runner: PluginRunner) -> Self { Self::new(runner) @@ -179,7 +182,7 @@ where &self, plugin: PluginModel, plugin_call_request: PluginCallRequest, - state: Arc>>, + state: Arc>>, ) -> Result { self.call_plugin(plugin, plugin_call_request, state).await } @@ -194,9 +197,9 @@ mod tests { jobs::MockJobProducerTrait, models::PluginModel, repositories::{ - NetworkRepositoryStorage, NotificationRepositoryStorage, PluginRepositoryStorage, - RelayerRepositoryStorage, SignerRepositoryStorage, TransactionCounterRepositoryStorage, - TransactionRepositoryStorage, + ApiKeyRepositoryStorage, NetworkRepositoryStorage, NotificationRepositoryStorage, + PluginRepositoryStorage, RelayerRepositoryStorage, SignerRepositoryStorage, + TransactionCounterRepositoryStorage, TransactionRepositoryStorage, }, utils::mocks::mockutils::create_mock_app_state, }; @@ -228,21 +231,13 @@ mod tests { path: "test-path".to_string(), timeout: Duration::from_secs(DEFAULT_PLUGIN_TIMEOUT_SECONDS), }; - let app_state: AppState< - MockJobProducerTrait, - RelayerRepositoryStorage, - TransactionRepositoryStorage, - NetworkRepositoryStorage, - NotificationRepositoryStorage, - SignerRepositoryStorage, - TransactionCounterRepositoryStorage, - PluginRepositoryStorage, - > = create_mock_app_state(None, None, None, Some(vec![plugin.clone()]), None).await; + let app_state = + create_mock_app_state(None, None, None, None, Some(vec![plugin.clone()]), None).await; let mut plugin_runner = MockPluginRunnerTrait::default(); plugin_runner - .expect_run::() + .expect_run::() .returning(|_, _, _, _, _| { Ok(ScriptResult { logs: vec![LogEntry { diff --git a/src/services/plugins/relayer_api.rs b/src/services/plugins/relayer_api.rs index 8f48288be..04bf49363 100644 --- a/src/services/plugins/relayer_api.rs +++ b/src/services/plugins/relayer_api.rs @@ -12,7 +12,7 @@ use crate::models::{ SignerRepoModel, ThinDataAppState, TransactionRepoModel, TransactionResponse, }; use crate::repositories::{ - NetworkRepository, PluginRepositoryTrait, RelayerRepository, Repository, + ApiKeyRepositoryTrait, NetworkRepository, PluginRepositoryTrait, RelayerRepository, Repository, TransactionCounterTrait, TransactionRepository, }; use actix_web::web; @@ -58,7 +58,7 @@ pub struct Response { #[async_trait] #[cfg_attr(test, automock)] -pub trait RelayerApiTrait: Send + Sync +pub trait RelayerApiTrait: Send + Sync where J: JobProducerTrait + 'static, TR: TransactionRepository + Repository + Send + Sync + 'static, @@ -68,29 +68,30 @@ where SR: Repository + Send + Sync + 'static, TCR: TransactionCounterTrait + Send + Sync + 'static, PR: PluginRepositoryTrait + Send + Sync + 'static, + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, { async fn handle_request( &self, request: Request, - state: &web::ThinData>, + state: &web::ThinData>, ) -> Response; async fn process_request( &self, request: Request, - state: &web::ThinData>, + state: &web::ThinData>, ) -> Result; async fn handle_send_transaction( &self, request: Request, - state: &web::ThinData>, + state: &web::ThinData>, ) -> Result; async fn handle_get_transaction( &self, request: Request, - state: &web::ThinData>, + state: &web::ThinData>, ) -> Result; } @@ -98,10 +99,10 @@ where pub struct RelayerApi; impl RelayerApi { - pub async fn handle_request( + pub async fn handle_request( &self, request: Request, - state: &ThinDataAppState, + state: &ThinDataAppState, ) -> Response where J: JobProducerTrait + 'static, @@ -116,6 +117,7 @@ impl RelayerApi { SR: Repository + Send + Sync + 'static, TCR: TransactionCounterTrait + Send + Sync + 'static, PR: PluginRepositoryTrait + Send + Sync + 'static, + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, { match self.process_request(request.clone(), state).await { Ok(response) => response, @@ -127,10 +129,10 @@ impl RelayerApi { } } - async fn process_request( + async fn process_request( &self, request: Request, - state: &ThinDataAppState, + state: &ThinDataAppState, ) -> Result where J: JobProducerTrait + 'static, @@ -145,6 +147,7 @@ impl RelayerApi { SR: Repository + Send + Sync + 'static, TCR: TransactionCounterTrait + Send + Sync + 'static, PR: PluginRepositoryTrait + Send + Sync + 'static, + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, { match request.method { PluginMethod::SendTransaction => self.handle_send_transaction(request, state).await, @@ -152,10 +155,10 @@ impl RelayerApi { } } - async fn handle_send_transaction( + async fn handle_send_transaction( &self, request: Request, - state: &ThinDataAppState, + state: &ThinDataAppState, ) -> Result where J: JobProducerTrait + 'static, @@ -170,6 +173,7 @@ impl RelayerApi { SR: Repository + Send + Sync + 'static, TCR: TransactionCounterTrait + Send + Sync + 'static, PR: PluginRepositoryTrait + Send + Sync + 'static, + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, { let relayer_repo_model = get_relayer_by_id(request.relayer_id.clone(), state) .await @@ -209,10 +213,10 @@ impl RelayerApi { }) } - async fn handle_get_transaction( + async fn handle_get_transaction( &self, request: Request, - state: &ThinDataAppState, + state: &ThinDataAppState, ) -> Result where J: JobProducerTrait + 'static, @@ -227,6 +231,7 @@ impl RelayerApi { SR: Repository + Send + Sync + 'static, TCR: TransactionCounterTrait + Send + Sync + 'static, PR: PluginRepositoryTrait + Send + Sync + 'static, + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, { // validation purpose only, checks if relayer exists get_relayer_by_id(request.relayer_id.clone(), state) @@ -255,7 +260,7 @@ impl RelayerApi { } #[async_trait] -impl RelayerApiTrait +impl RelayerApiTrait for RelayerApi where J: JobProducerTrait + 'static, @@ -266,11 +271,12 @@ where SR: Repository + Send + Sync + 'static, TCR: TransactionCounterTrait + Send + Sync + 'static, PR: PluginRepositoryTrait + Send + Sync + 'static, + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, { async fn handle_request( &self, request: Request, - state: &ThinDataAppState, + state: &ThinDataAppState, ) -> Response { self.handle_request(request, state).await } @@ -278,7 +284,7 @@ where async fn process_request( &self, request: Request, - state: &ThinDataAppState, + state: &ThinDataAppState, ) -> Result { self.process_request(request, state).await } @@ -286,7 +292,7 @@ where async fn handle_send_transaction( &self, request: Request, - state: &ThinDataAppState, + state: &ThinDataAppState, ) -> Result { self.handle_send_transaction(request, state).await } @@ -294,7 +300,7 @@ where async fn handle_get_transaction( &self, request: Request, - state: &ThinDataAppState, + state: &ThinDataAppState, ) -> Result { self.handle_get_transaction(request, state).await } @@ -321,6 +327,7 @@ mod tests { async fn test_handle_request() { setup_test_env(); let state = create_mock_app_state( + None, Some(vec![create_mock_relayer("test".to_string(), false)]), Some(vec![create_mock_signer()]), Some(vec![create_mock_network()]), @@ -350,6 +357,7 @@ mod tests { setup_test_env(); let paused = true; let state = create_mock_app_state( + None, Some(vec![create_mock_relayer("test".to_string(), paused)]), Some(vec![create_mock_signer()]), Some(vec![create_mock_network()]), @@ -379,6 +387,7 @@ mod tests { async fn test_handle_request_using_trait() { setup_test_env(); let state = create_mock_app_state( + None, Some(vec![create_mock_relayer("test".to_string(), false)]), Some(vec![create_mock_signer()]), Some(vec![create_mock_network()]), @@ -418,6 +427,7 @@ mod tests { async fn test_handle_get_transaction() { setup_test_env(); let state = create_mock_app_state( + None, Some(vec![create_mock_relayer("test".to_string(), false)]), Some(vec![create_mock_signer()]), Some(vec![create_mock_network()]), @@ -448,6 +458,7 @@ mod tests { async fn test_handle_get_transaction_error_relayer_not_found() { setup_test_env(); let state = create_mock_app_state( + None, None, Some(vec![create_mock_signer()]), Some(vec![create_mock_network()]), @@ -479,6 +490,7 @@ mod tests { async fn test_handle_get_transaction_error_transaction_not_found() { setup_test_env(); let state = create_mock_app_state( + None, Some(vec![create_mock_relayer("test".to_string(), false)]), Some(vec![create_mock_signer()]), Some(vec![create_mock_network()]), diff --git a/src/services/plugins/runner.rs b/src/services/plugins/runner.rs index e952ddb97..063f45efb 100644 --- a/src/services/plugins/runner.rs +++ b/src/services/plugins/runner.rs @@ -16,8 +16,8 @@ use crate::{ ThinDataAppState, TransactionRepoModel, }, repositories::{ - NetworkRepository, PluginRepositoryTrait, RelayerRepository, Repository, - TransactionCounterTrait, TransactionRepository, + ApiKeyRepositoryTrait, NetworkRepository, PluginRepositoryTrait, RelayerRepository, + Repository, TransactionCounterTrait, TransactionRepository, }, }; @@ -32,13 +32,13 @@ use mockall::automock; #[async_trait] pub trait PluginRunnerTrait { #[allow(clippy::type_complexity)] - async fn run( + async fn run( &self, socket_path: &str, script_path: String, timeout_duration: Duration, script_params: String, - state: Arc>, + state: Arc>, ) -> Result where J: JobProducerTrait + Send + Sync + 'static, @@ -52,7 +52,8 @@ pub trait PluginRunnerTrait { NFR: Repository + Send + Sync + 'static, SR: Repository + Send + Sync + 'static, TCR: TransactionCounterTrait + Send + Sync + 'static, - PR: PluginRepositoryTrait + Send + Sync + 'static; + PR: PluginRepositoryTrait + Send + Sync + 'static, + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static; } #[derive(Default)] @@ -60,13 +61,13 @@ pub struct PluginRunner; #[allow(clippy::type_complexity)] impl PluginRunner { - async fn run( + async fn run( &self, socket_path: &str, script_path: String, timeout_duration: Duration, script_params: String, - state: Arc>, + state: Arc>, ) -> Result where J: JobProducerTrait + 'static, @@ -81,6 +82,7 @@ impl PluginRunner { SR: Repository + Send + Sync + 'static, TCR: TransactionCounterTrait + Send + Sync + 'static, PR: PluginRepositoryTrait + Send + Sync + 'static, + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, { let socket_service = SocketService::new(socket_path)?; let socket_path_clone = socket_service.socket_path().to_string(); @@ -127,13 +129,13 @@ impl PluginRunner { #[async_trait] impl PluginRunnerTrait for PluginRunner { - async fn run( + async fn run( &self, socket_path: &str, script_path: String, timeout_duration: Duration, script_params: String, - state: Arc>, + state: Arc>, ) -> Result where J: JobProducerTrait + Send + Sync + 'static, @@ -148,6 +150,7 @@ impl PluginRunnerTrait for PluginRunner { SR: Repository + Send + Sync + 'static, TCR: TransactionCounterTrait + Send + Sync + 'static, PR: PluginRepositoryTrait + Send + Sync + 'static, + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, { self.run( socket_path, @@ -168,9 +171,9 @@ mod tests { use crate::{ jobs::MockJobProducerTrait, repositories::{ - NetworkRepositoryStorage, NotificationRepositoryStorage, PluginRepositoryStorage, - RelayerRepositoryStorage, SignerRepositoryStorage, TransactionCounterRepositoryStorage, - TransactionRepositoryStorage, + ApiKeyRepositoryStorage, NetworkRepositoryStorage, NotificationRepositoryStorage, + PluginRepositoryStorage, RelayerRepositoryStorage, SignerRepositoryStorage, + TransactionCounterRepositoryStorage, TransactionRepositoryStorage, }, services::plugins::LogLevel, utils::mocks::mockutils::create_mock_app_state, @@ -207,11 +210,11 @@ mod tests { fs::write(script_path.clone(), content).unwrap(); fs::write(ts_config.clone(), TS_CONFIG.as_bytes()).unwrap(); - let state = create_mock_app_state(None, None, None, None, None).await; + let state = create_mock_app_state(None, None, None, None, None, None).await; let plugin_runner = PluginRunner; let result = plugin_runner - .run::( + .run::( &socket_path.display().to_string(), script_path.display().to_string(), Duration::from_secs(10), @@ -253,12 +256,12 @@ mod tests { fs::write(script_path.clone(), content).unwrap(); fs::write(ts_config.clone(), TS_CONFIG.as_bytes()).unwrap(); - let state = create_mock_app_state(None, None, None, None, None).await; + let state = create_mock_app_state(None, None, None, None, None, None).await; let plugin_runner = PluginRunner; // Use 100ms timeout for a 200ms script let result = plugin_runner - .run::( + .run::( &socket_path.display().to_string(), script_path.display().to_string(), Duration::from_millis(100), // 100ms timeout diff --git a/src/services/plugins/socket.rs b/src/services/plugins/socket.rs index 3b356eac1..8d6100a66 100644 --- a/src/services/plugins/socket.rs +++ b/src/services/plugins/socket.rs @@ -54,7 +54,7 @@ use crate::models::{ TransactionRepoModel, }; use crate::repositories::{ - NetworkRepository, PluginRepositoryTrait, RelayerRepository, Repository, + ApiKeyRepositoryTrait, NetworkRepository, PluginRepositoryTrait, RelayerRepository, Repository, TransactionCounterTrait, TransactionRepository, }; use std::sync::Arc; @@ -107,14 +107,14 @@ impl SocketService { /// /// A vector of traces. #[allow(clippy::type_complexity)] - pub async fn listen( + pub async fn listen( self, shutdown_rx: oneshot::Receiver<()>, - state: Arc>, + state: Arc>, relayer_api: Arc, ) -> Result, PluginError> where - RA: RelayerApiTrait + 'static + Send + Sync, + RA: RelayerApiTrait + 'static + Send + Sync, J: JobProducerTrait + Send + Sync + 'static, RR: RelayerRepository + Repository + Send + Sync + 'static, TR: TransactionRepository @@ -127,6 +127,7 @@ impl SocketService { SR: Repository + Send + Sync + 'static, TCR: TransactionCounterTrait + Send + Sync + 'static, PR: PluginRepositoryTrait + Send + Sync + 'static, + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, { let mut shutdown = shutdown_rx; @@ -137,7 +138,7 @@ impl SocketService { let relayer_api = Arc::clone(&relayer_api); tokio::select! { Ok((stream, _)) = self.listener.accept() => { - let result = tokio::spawn(Self::handle_connection::(stream, state, relayer_api)) + let result = tokio::spawn(Self::handle_connection::(stream, state, relayer_api)) .await .map_err(|e| PluginError::SocketError(e.to_string()))?; @@ -168,13 +169,13 @@ impl SocketService { /// /// A vector of traces. #[allow(clippy::type_complexity)] - async fn handle_connection( + async fn handle_connection( stream: UnixStream, - state: Arc>, + state: Arc>, relayer_api: Arc, ) -> Result, PluginError> where - RA: RelayerApiTrait + 'static + Send + Sync, + RA: RelayerApiTrait + 'static + Send + Sync, J: JobProducerTrait + 'static, RR: RelayerRepository + Repository + Send + Sync + 'static, TR: TransactionRepository @@ -187,6 +188,7 @@ impl SocketService { SR: Repository + Send + Sync + 'static, TCR: TransactionCounterTrait + Send + Sync + 'static, PR: PluginRepositoryTrait + Send + Sync + 'static, + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, { let (r, mut w) = stream.into_split(); let mut reader = BufReader::new(r).lines(); @@ -239,7 +241,7 @@ mod tests { let service = SocketService::new(socket_path.to_str().unwrap()).unwrap(); - let state = create_mock_app_state(None, None, None, None, None).await; + let state = create_mock_app_state(None, None, None, None, None, None).await; let (shutdown_tx, shutdown_rx) = oneshot::channel(); let listen_handle = tokio::spawn(async move { @@ -278,7 +280,7 @@ mod tests { let service = SocketService::new(socket_path.to_str().unwrap()).unwrap(); - let state = create_mock_app_state(None, None, None, None, None).await; + let state = create_mock_app_state(None, None, None, None, None, None).await; let (shutdown_tx, shutdown_rx) = oneshot::channel(); let listen_handle = tokio::spawn(async move { diff --git a/src/utils/mocks.rs b/src/utils/mocks.rs index f691146a5..557c7a94a 100644 --- a/src/utils/mocks.rs +++ b/src/utils/mocks.rs @@ -10,14 +10,15 @@ pub mod mockutils { config::{EvmNetworkConfig, NetworkConfigCommon}, jobs::MockJobProducerTrait, models::{ - AppState, EvmTransactionData, EvmTransactionRequest, LocalSignerConfig, + ApiKeyModel, AppState, EvmTransactionData, EvmTransactionRequest, LocalSignerConfig, NetworkRepoModel, NetworkTransactionData, NetworkType, PluginModel, RelayerEvmPolicy, RelayerNetworkPolicy, RelayerRepoModel, SignerConfig, SignerRepoModel, TransactionRepoModel, TransactionStatus, }, repositories::{ - NetworkRepositoryStorage, NotificationRepositoryStorage, PluginRepositoryStorage, - PluginRepositoryTrait, RelayerRepositoryStorage, Repository, SignerRepositoryStorage, + ApiKeyRepositoryStorage, ApiKeyRepositoryTrait, NetworkRepositoryStorage, + NotificationRepositoryStorage, PluginRepositoryStorage, PluginRepositoryTrait, + RelayerRepositoryStorage, Repository, SignerRepositoryStorage, TransactionCounterRepositoryStorage, TransactionRepositoryStorage, }, }; @@ -97,6 +98,7 @@ pub mod mockutils { } pub async fn create_mock_app_state( + api_keys: Option>, relayers: Option>, signers: Option>, networks: Option>, @@ -111,6 +113,7 @@ pub mod mockutils { SignerRepositoryStorage, TransactionCounterRepositoryStorage, PluginRepositoryStorage, + ApiKeyRepositoryStorage, > { let relayer_repository = Arc::new(RelayerRepositoryStorage::new_in_memory()); if let Some(relayers) = relayers { @@ -147,6 +150,13 @@ pub mod mockutils { } } + let api_key_repository = Arc::new(ApiKeyRepositoryStorage::new_in_memory()); + if let Some(api_keys) = api_keys { + for api_key in api_keys { + api_key_repository.create(api_key).await.unwrap(); + } + } + let mut mock_job_producer = MockJobProducerTrait::new(); mock_job_producer @@ -180,6 +190,7 @@ pub mod mockutils { ), job_producer: Arc::new(mock_job_producer), plugin_repository, + api_key_repository, } } From 58c3f4c0ec91a197c73b65687f52f3519af8eb32 Mon Sep 17 00:00:00 2001 From: Marcos Date: Thu, 24 Jul 2025 16:30:38 -0300 Subject: [PATCH 06/22] fix: Examoles linting --- .../config/config.json | 2 +- .../config/networks/evm.json | 2 +- plugins/pnpm-lock.yaml | 791 +----------------- 3 files changed, 15 insertions(+), 780 deletions(-) diff --git a/examples/network-configuration-config-file/config/config.json b/examples/network-configuration-config-file/config/config.json index c28a56692..78a0681fb 100644 --- a/examples/network-configuration-config-file/config/config.json +++ b/examples/network-configuration-config-file/config/config.json @@ -68,7 +68,7 @@ "https://ethereum-sepolia-public.nodies.app" ], "symbol": "ETH", - "chain_id": 343434324, + "chain_id": 343434324, "tags": ["ethereum", "testnet", "custom"] } ], diff --git a/examples/network-configuration-json-file/config/networks/evm.json b/examples/network-configuration-json-file/config/networks/evm.json index 86c63a549..6f7952395 100644 --- a/examples/network-configuration-json-file/config/networks/evm.json +++ b/examples/network-configuration-json-file/config/networks/evm.json @@ -29,7 +29,7 @@ "https://api-sepolia.etherscan.io/api", "https://sepolia.etherscan.io" ], - + "is_testnet": true, "network": "sepolia", "required_confirmations": 6, diff --git a/plugins/pnpm-lock.yaml b/plugins/pnpm-lock.yaml index faef8cbf3..58be8bf32 100644 --- a/plugins/pnpm-lock.yaml +++ b/plugins/pnpm-lock.yaml @@ -1,11 +1,9 @@ +--- lockfileVersion: '9.0' - settings: autoInstallPeers: true excludeLinksFromLockfile: false - importers: - .: dependencies: '@openzeppelin/relayer-sdk': @@ -39,203 +37,158 @@ importers: typescript: specifier: ^5.3.3 version: 5.8.3 - packages: - '@actions/exec@1.1.1': resolution: {integrity: sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==} - '@actions/io@1.1.3': resolution: {integrity: sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==} - '@adraffy/ens-normalize@1.10.1': resolution: {integrity: sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==} - '@ampproject/remapping@2.3.0': resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@babel/code-frame@7.27.1': resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.28.0': resolution: {integrity: sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==} engines: {node: '>=6.9.0'} - '@babel/core@7.28.0': resolution: {integrity: sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==} engines: {node: '>=6.9.0'} - '@babel/generator@7.28.0': resolution: {integrity: sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.27.2': resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} engines: {node: '>=6.9.0'} - '@babel/helper-globals@7.28.0': resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.27.1': resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} engines: {node: '>=6.9.0'} - '@babel/helper-module-transforms@7.27.3': resolution: {integrity: sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-plugin-utils@7.27.1': resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.27.1': resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.27.1': resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.27.1': resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.27.6': resolution: {integrity: sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==} engines: {node: '>=6.9.0'} - '@babel/parser@7.28.0': resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/plugin-syntax-async-generators@7.8.4': resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-bigint@7.8.3': resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-class-properties@7.12.13': resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-class-static-block@7.14.5': resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-import-attributes@7.27.1': resolution: {integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-import-meta@7.10.4': resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-json-strings@7.8.3': resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-jsx@7.27.1': resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-logical-assignment-operators@7.10.4': resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-numeric-separator@7.10.4': resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-object-rest-spread@7.8.3': resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-optional-catch-binding@7.8.3': resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-optional-chaining@7.8.3': resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-private-property-in-object@7.14.5': resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-top-level-await@7.14.5': resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-typescript@7.27.1': resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/runtime@7.27.6': resolution: {integrity: sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==} engines: {node: '>=6.9.0'} - '@babel/template@7.27.2': resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.28.0': resolution: {integrity: sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==} engines: {node: '>=6.9.0'} - '@babel/types@7.28.0': resolution: {integrity: sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==} engines: {node: '>=6.9.0'} - '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - '@istanbuljs/load-nyc-config@1.1.0': resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} engines: {node: '>=8'} - '@istanbuljs/schema@0.1.3': resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} - '@jest/console@29.7.0': resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/core@29.7.0': resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -244,27 +197,21 @@ packages: peerDependenciesMeta: node-notifier: optional: true - '@jest/environment@29.7.0': resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/expect-utils@29.7.0': resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/expect@29.7.0': resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/fake-timers@29.7.0': resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/globals@29.7.0': resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/reporters@29.7.0': resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -273,430 +220,317 @@ packages: peerDependenciesMeta: node-notifier: optional: true - '@jest/schemas@29.6.3': resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/source-map@29.6.3': resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/test-result@29.7.0': resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/test-sequencer@29.7.0': resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/transform@29.7.0': resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/types@29.6.3': resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jridgewell/gen-mapping@0.3.12': resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==} - '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/sourcemap-codec@1.5.4': resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==} - '@jridgewell/trace-mapping@0.3.29': resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==} - '@noble/curves@1.2.0': resolution: {integrity: sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==} - '@noble/curves@1.9.2': resolution: {integrity: sha512-HxngEd2XUcg9xi20JkwlLCtYwfoFw4JGkuZpT+WlsPD4gB/cxkvTD8fSsoAnphGZhFdZYKeQIPCuFlWPm1uE0g==} engines: {node: ^14.21.3 || >=16} - '@noble/hashes@1.3.2': resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==} engines: {node: '>= 16'} - '@noble/hashes@1.8.0': resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} engines: {node: ^14.21.3 || >=16} - '@openzeppelin/relayer-sdk@1.1.0': resolution: {integrity: sha512-wQKHE8wtcBOigOQMFUAZ21r+AtSlmGgCN5/bq0QAQy/HJu9qHOTko04Q1sA//OnGK3u/WYluN0IYSjvAKvZYJQ==} engines: {node: 22.14.0, npm: use pnpm, pnpm: '>=9', yarn: use pnpm} - '@sinclair/typebox@0.27.8': resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} - '@sinonjs/commons@3.0.1': resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} - '@sinonjs/fake-timers@10.3.0': resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} - '@solana/buffer-layout-utils@0.2.0': resolution: {integrity: sha512-szG4sxgJGktbuZYDg2FfNmkMi0DYQoVjN2h7ta1W1hPrwzarcFLBq9UpX1UjNXsNpT9dn+chgprtWGioUAr4/g==} engines: {node: '>= 10'} - '@solana/buffer-layout@4.0.1': resolution: {integrity: sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==} engines: {node: '>=5.10'} - '@solana/codecs-core@2.0.0-rc.1': resolution: {integrity: sha512-bauxqMfSs8EHD0JKESaNmNuNvkvHSuN3bbWAF5RjOfDu2PugxHrvRebmYauvSumZ3cTfQ4HJJX6PG5rN852qyQ==} peerDependencies: typescript: '>=5' - '@solana/codecs-core@2.1.1': resolution: {integrity: sha512-iPQW3UZ2Vi7QFBo2r9tw0NubtH8EdrhhmZulx6lC8V5a+qjaxovtM/q/UW2BTNpqqHLfO0tIcLyBLrNH4HTWPg==} engines: {node: '>=20.18.0'} peerDependencies: typescript: '>=5.3.3' - '@solana/codecs-data-structures@2.0.0-rc.1': resolution: {integrity: sha512-rinCv0RrAVJ9rE/rmaibWJQxMwC5lSaORSZuwjopSUE6T0nb/MVg6Z1siNCXhh/HFTOg0l8bNvZHgBcN/yvXog==} peerDependencies: typescript: '>=5' - '@solana/codecs-numbers@2.0.0-rc.1': resolution: {integrity: sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ==} peerDependencies: typescript: '>=5' - '@solana/codecs-numbers@2.1.1': resolution: {integrity: sha512-m20IUPJhPUmPkHSlZ2iMAjJ7PaYUvlMtFhCQYzm9BEBSI6OCvXTG3GAPpAnSGRBfg5y+QNqqmKn4QHU3B6zzCQ==} engines: {node: '>=20.18.0'} peerDependencies: typescript: '>=5.3.3' - '@solana/codecs-strings@2.0.0-rc.1': resolution: {integrity: sha512-9/wPhw8TbGRTt6mHC4Zz1RqOnuPTqq1Nb4EyuvpZ39GW6O2t2Q7Q0XxiB3+BdoEjwA2XgPw6e2iRfvYgqty44g==} peerDependencies: fastestsmallesttextencoderdecoder: ^1.0.22 typescript: '>=5' - '@solana/codecs@2.0.0-rc.1': resolution: {integrity: sha512-qxoR7VybNJixV51L0G1RD2boZTcxmwUWnKCaJJExQ5qNKwbpSyDdWfFJfM5JhGyKe9DnPVOZB+JHWXnpbZBqrQ==} peerDependencies: typescript: '>=5' - '@solana/errors@2.0.0-rc.1': resolution: {integrity: sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ==} hasBin: true peerDependencies: typescript: '>=5' - '@solana/errors@2.1.1': resolution: {integrity: sha512-sj6DaWNbSJFvLzT8UZoabMefQUfSW/8tXK7NTiagsDmh+Q87eyQDDC9L3z+mNmx9b6dEf6z660MOIplDD2nfEw==} engines: {node: '>=20.18.0'} hasBin: true peerDependencies: typescript: '>=5.3.3' - '@solana/options@2.0.0-rc.1': resolution: {integrity: sha512-mLUcR9mZ3qfHlmMnREdIFPf9dpMc/Bl66tLSOOWxw4ml5xMT2ohFn7WGqoKcu/UHkT9CrC6+amEdqCNvUqI7AA==} peerDependencies: typescript: '>=5' - '@solana/spl-token-group@0.0.7': resolution: {integrity: sha512-V1N/iX7Cr7H0uazWUT2uk27TMqlqedpXHRqqAbVO2gvmJyT0E0ummMEAVQeXZ05ZhQ/xF39DLSdBp90XebWEug==} engines: {node: '>=16'} peerDependencies: '@solana/web3.js': ^1.95.3 - '@solana/spl-token-metadata@0.1.6': resolution: {integrity: sha512-7sMt1rsm/zQOQcUWllQX9mD2O6KhSAtY1hFR2hfFwgqfFWzSY9E9GDvFVNYUI1F0iQKcm6HmePU9QbKRXTEBiA==} engines: {node: '>=16'} peerDependencies: '@solana/web3.js': ^1.95.3 - '@solana/spl-token@0.4.13': resolution: {integrity: sha512-cite/pYWQZZVvLbg5lsodSovbetK/eA24gaR0eeUeMuBAMNrT8XFCwaygKy0N2WSg3gSyjjNpIeAGBAKZaY/1w==} engines: {node: '>=16'} peerDependencies: '@solana/web3.js': ^1.95.5 - '@solana/web3.js@1.98.2': resolution: {integrity: sha512-BqVwEG+TaG2yCkBMbD3C4hdpustR4FpuUFRPUmqRZYYlPI9Hg4XMWxHWOWRzHE9Lkc9NDjzXFX7lDXSgzC7R1A==} - '@swc/helpers@0.5.17': resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==} - '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} - '@types/babel__generator@7.27.0': resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} - '@types/babel__template@7.4.4': resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} - '@types/babel__traverse@7.20.7': resolution: {integrity: sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==} - '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} - '@types/graceful-fs@4.1.9': resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} - '@types/istanbul-lib-coverage@2.0.6': resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} - '@types/istanbul-lib-report@3.0.3': resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} - '@types/istanbul-reports@3.0.4': resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} - '@types/jest@29.5.14': resolution: {integrity: sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==} - '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - '@types/node@22.7.5': resolution: {integrity: sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==} - '@types/node@24.0.3': resolution: {integrity: sha512-R4I/kzCYAdRLzfiCabn9hxWfbuHS573x+r0dJMkkzThEa7pbrcDWK+9zu3e7aBOouf+rQAciqPFMnxwr0aWgKg==} - '@types/stack-utils@2.0.3': resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} - '@types/uuid@10.0.0': resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==} - '@types/uuid@8.3.4': resolution: {integrity: sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==} - '@types/ws@7.4.7': resolution: {integrity: sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==} - '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} - '@types/yargs-parser@21.0.3': resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} - '@types/yargs@17.0.33': resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} - aes-js@4.0.0-beta.5: resolution: {integrity: sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==} - agentkeepalive@4.6.0: resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} engines: {node: '>= 8.0.0'} - ansi-escapes@4.3.2: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} - ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@5.2.0: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} - anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} - argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - async@3.2.6: resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} - asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - axios@1.11.0: resolution: {integrity: sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==} - babel-jest@29.7.0: resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: '@babel/core': ^7.8.0 - babel-plugin-istanbul@6.1.1: resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} engines: {node: '>=8'} - babel-plugin-jest-hoist@29.6.3: resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - babel-preset-current-node-syntax@1.1.0: resolution: {integrity: sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==} peerDependencies: '@babel/core': ^7.0.0 - babel-preset-jest@29.6.3: resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: '@babel/core': ^7.0.0 - balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - base-x@3.0.11: resolution: {integrity: sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==} - base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - bigint-buffer@1.1.5: resolution: {integrity: sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA==} engines: {node: '>= 10.0.0'} - bignumber.js@9.3.0: resolution: {integrity: sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA==} - bindings@1.5.0: resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} - bn.js@5.2.2: resolution: {integrity: sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==} - borsh@0.7.0: resolution: {integrity: sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==} - brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} - brace-expansion@2.0.2: resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} - braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.25.1: resolution: {integrity: sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - bs-logger@0.2.6: resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} engines: {node: '>= 6'} - bs58@4.0.1: resolution: {integrity: sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==} - bser@2.1.1: resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} - buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - bufferutil@4.0.9: resolution: {integrity: sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==} engines: {node: '>=6.14.2'} - call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} - callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - camelcase@5.3.1: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} engines: {node: '>=6'} - camelcase@6.3.0: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001726: resolution: {integrity: sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw==} - chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - chalk@5.4.1: resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - char-regex@1.0.2: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} - ci-info@3.9.0: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} - cjs-module-lexer@1.4.3: resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} - cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} - co@4.6.0: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} - collect-v8-coverage@1.0.2: resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} - color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} - color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} - commander@12.1.0: resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} engines: {node: '>=18'} - commander@13.1.0: resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} engines: {node: '>=18'} - commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - create-jest@29.7.0: resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true - cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} - debug@4.4.1: resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} engines: {node: '>=6.0'} @@ -705,7 +539,6 @@ packages: peerDependenciesMeta: supports-color: optional: true - dedent@1.6.0: resolution: {integrity: sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==} peerDependencies: @@ -713,133 +546,98 @@ packages: peerDependenciesMeta: babel-plugin-macros: optional: true - deepmerge@4.3.1: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} - delay@5.0.0: resolution: {integrity: sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==} engines: {node: '>=10'} - delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} - detect-newline@3.1.0: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} - diff-sequences@29.6.3: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} - ejs@3.1.10: resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} engines: {node: '>=0.10.0'} hasBin: true - electron-to-chromium@1.5.178: resolution: {integrity: sha512-wObbz/ar3Bc6e4X5vf0iO8xTN8YAjN/tgiAOJLr7yjYFtP9wAjq8Mb5h0yn6kResir+VYx2DXBj9NNobs0ETSA==} - emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} engines: {node: '>=12'} - emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} - es-errors@1.3.0: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} - es-set-tostringtag@2.1.0: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} - es6-promise@4.2.8: resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==} - es6-promisify@5.0.0: resolution: {integrity: sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==} - escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} - escape-string-regexp@2.0.0: resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} engines: {node: '>=8'} - esprima@4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} engines: {node: '>=4'} hasBin: true - ethers@6.14.3: resolution: {integrity: sha512-qq7ft/oCJohoTcsNPFaXSQUm457MA5iWqkf1Mb11ujONdg7jBI6sAOrHaTi3j0CBqIGFSCeR/RMc+qwRRub7IA==} engines: {node: '>=14.0.0'} - eventemitter3@5.0.1: resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} - execa@5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} - exit@0.1.2: resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} engines: {node: '>= 0.8.0'} - expect@29.7.0: resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - eyes@0.1.8: resolution: {integrity: sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==} engines: {node: '> 0.1.90'} - fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - fast-stable-stringify@1.0.0: resolution: {integrity: sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==} - fastestsmallesttextencoderdecoder@1.0.22: resolution: {integrity: sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==} - fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} - file-uri-to-path@1.0.0: resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} - filelist@1.0.4: resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} - fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} - find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} - follow-redirects@1.15.9: resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} engines: {node: '>=4.0'} @@ -848,180 +646,136 @@ packages: peerDependenciesMeta: debug: optional: true - form-data@4.0.4: resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} engines: {node: '>= 6'} - fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - + os: + - darwin function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} - get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} - get-package-type@0.1.0: resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} engines: {node: '>=8.0.0'} - get-proto@1.0.1: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} - get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} - glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Glob versions prior to v9 are no longer supported - gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} - graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - has-symbols@1.1.0: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} - has-tostringtag@1.0.2: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} - hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} - html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} - human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} - humanize-ms@1.2.1: resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} - husky@9.1.7: resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} engines: {node: '>=18'} hasBin: true - ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - import-local@3.2.0: resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} engines: {node: '>=8'} hasBin: true - imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} - inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. - inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - is-core-module@2.16.1: resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} engines: {node: '>= 0.4'} - is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - is-generator-fn@2.1.0: resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} engines: {node: '>=6'} - is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} - isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - isomorphic-ws@4.0.1: resolution: {integrity: sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==} peerDependencies: ws: '*' - istanbul-lib-coverage@3.2.2: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} - istanbul-lib-instrument@5.2.1: resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} engines: {node: '>=8'} - istanbul-lib-instrument@6.0.3: resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} engines: {node: '>=10'} - istanbul-lib-report@3.0.1: resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} engines: {node: '>=10'} - istanbul-lib-source-maps@4.0.1: resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} engines: {node: '>=10'} - istanbul-reports@3.1.7: resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} engines: {node: '>=8'} - jake@10.9.2: resolution: {integrity: sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==} engines: {node: '>=10'} hasBin: true - jayson@4.2.0: resolution: {integrity: sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg==} engines: {node: '>=8'} hasBin: true - jest-changed-files@29.7.0: resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-circus@29.7.0: resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-cli@29.7.0: resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1031,7 +785,6 @@ packages: peerDependenciesMeta: node-notifier: optional: true - jest-config@29.7.0: resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1043,47 +796,36 @@ packages: optional: true ts-node: optional: true - jest-diff@29.7.0: resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-docblock@29.7.0: resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-each@29.7.0: resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-environment-node@29.7.0: resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-get-type@29.6.3: resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-haste-map@29.7.0: resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-leak-detector@29.7.0: resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-matcher-utils@29.7.0: resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-message-util@29.7.0: resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-mock@29.7.0: resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-pnp-resolver@1.2.3: resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} engines: {node: '>=6'} @@ -1092,47 +834,36 @@ packages: peerDependenciesMeta: jest-resolve: optional: true - jest-regex-util@29.6.3: resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-resolve-dependencies@29.7.0: resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-resolve@29.7.0: resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-runner@29.7.0: resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-runtime@29.7.0: resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-snapshot@29.7.0: resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-util@29.7.0: resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-validate@29.7.0: resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-watcher@29.7.0: resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-worker@29.7.0: resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest@29.7.0: resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1142,97 +873,71 @@ packages: peerDependenciesMeta: node-notifier: optional: true - js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@3.14.1: resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} hasBin: true - jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} hasBin: true - json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - json-stringify-safe@5.0.1: resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} - json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} hasBin: true - kleur@3.0.3: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} - leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} - lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} - lodash.memoize@4.1.2: resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} - lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} - make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - makeerror@1.0.12: resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} - math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} - merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - micromatch@4.0.8: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} - mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} - mime-types@2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} - mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} - minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - minimatch@5.1.6: resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} engines: {node: '>=10'} - ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} @@ -1241,230 +946,169 @@ packages: peerDependenciesMeta: encoding: optional: true - node-gyp-build@4.8.4: resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} hasBin: true - node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - node-releases@2.0.19: resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} - normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - npm-run-path@4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} - once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} - p-limit@2.3.0: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} engines: {node: '>=6'} - p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} - p-locate@4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} - p-try@2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} - parse-json@5.2.0: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} - path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} - path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} - path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - pirates@4.0.7: resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} - pkg-dir@4.2.0: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} - pretty-format@29.7.0: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - prompts@2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} - proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - pure-rand@6.1.0: resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} - react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} - resolve-cwd@3.0.0: resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} engines: {node: '>=8'} - resolve-from@5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} - resolve.exports@2.0.3: resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} engines: {node: '>=10'} - resolve@1.22.10: resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} engines: {node: '>= 0.4'} hasBin: true - rpc-websockets@9.1.1: resolution: {integrity: sha512-1IXGM/TfPT6nfYMIXkJdzn+L4JEsmb0FL1O2OBjaH03V3yuUDdKFulGLMFG6ErV+8pZ5HVC0limve01RyO+saA==} - safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.7.2: resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} engines: {node: '>=10'} hasBin: true - shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} - shebang-regex@3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} - source-map-support@0.5.13: resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} - source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} - sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - stack-utils@2.0.6: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} - stream-chain@2.2.5: resolution: {integrity: sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==} - stream-json@1.9.1: resolution: {integrity: sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw==} - string-length@4.0.2: resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} engines: {node: '>=10'} - string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} - strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} - strip-bom@4.0.0: resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} engines: {node: '>=8'} - strip-final-newline@2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} - strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - superstruct@2.0.2: resolution: {integrity: sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==} engines: {node: '>=14.0.0'} - supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} - supports-color@8.1.1: resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} engines: {node: '>=10'} - supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - test-exclude@6.0.0: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} - text-encoding-utf-8@1.0.2: resolution: {integrity: sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==} - tmpl@1.0.5: resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} - to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - ts-jest@29.4.0: resolution: {integrity: sha512-d423TJMnJGu80/eSgfQ5w/R+0zFJvdtTxwtF9KzFFunOpSeD+79lHJQIiAhluJoyGRbvj9NZJsl9WjCUo0ND7Q==} engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} @@ -1491,83 +1135,62 @@ packages: optional: true jest-util: optional: true - tslib@2.7.0: resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} - tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - type-detect@4.0.8: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} - type-fest@0.21.3: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} - type-fest@4.41.0: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} - typescript@5.8.3: resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} engines: {node: '>=14.17'} hasBin: true - undici-types@6.19.8: resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} - undici-types@7.8.0: resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==} - update-browserslist-db@1.1.3: resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' - utf-8-validate@5.0.10: resolution: {integrity: sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==} engines: {node: '>=6.14.2'} - uuid@11.1.0: resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} hasBin: true - uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true - v8-to-istanbul@9.3.0: resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} engines: {node: '>=10.12.0'} - walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} - webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} hasBin: true - wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} - wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - write-file-atomic@4.0.2: resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - ws@7.5.10: resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} engines: {node: '>=8.3.0'} @@ -1579,7 +1202,6 @@ packages: optional: true utf-8-validate: optional: true - ws@8.17.1: resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} engines: {node: '>=10.0.0'} @@ -1591,49 +1213,36 @@ packages: optional: true utf-8-validate: optional: true - y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} - yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} - yargs@17.7.2: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} - yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - snapshots: - '@actions/exec@1.1.1': dependencies: '@actions/io': 1.1.3 - '@actions/io@1.1.3': {} - '@adraffy/ens-normalize@1.10.1': {} - '@ampproject/remapping@2.3.0': dependencies: '@jridgewell/gen-mapping': 0.3.12 '@jridgewell/trace-mapping': 0.3.29 - '@babel/code-frame@7.27.1': dependencies: '@babel/helper-validator-identifier': 7.27.1 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.28.0': {} - '@babel/core@7.28.0': dependencies: '@ampproject/remapping': 2.3.0 @@ -1653,7 +1262,6 @@ snapshots: semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/generator@7.28.0': dependencies: '@babel/parser': 7.28.0 @@ -1661,7 +1269,6 @@ snapshots: '@jridgewell/gen-mapping': 0.3.12 '@jridgewell/trace-mapping': 0.3.29 jsesc: 3.1.0 - '@babel/helper-compilation-targets@7.27.2': dependencies: '@babel/compat-data': 7.28.0 @@ -1669,16 +1276,13 @@ snapshots: browserslist: 4.25.1 lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-globals@7.28.0': {} - '@babel/helper-module-imports@7.27.1': dependencies: '@babel/traverse': 7.28.0 '@babel/types': 7.28.0 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.27.3(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 @@ -1687,117 +1291,91 @@ snapshots: '@babel/traverse': 7.28.0 transitivePeerDependencies: - supports-color - '@babel/helper-plugin-utils@7.27.1': {} - '@babel/helper-string-parser@7.27.1': {} - '@babel/helper-validator-identifier@7.27.1': {} - '@babel/helper-validator-option@7.27.1': {} - '@babel/helpers@7.27.6': dependencies: '@babel/template': 7.27.2 '@babel/types': 7.28.0 - '@babel/parser@7.28.0': dependencies: '@babel/types': 7.28.0 - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/runtime@7.27.6': {} - '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 '@babel/parser': 7.28.0 '@babel/types': 7.28.0 - '@babel/traverse@7.28.0': dependencies: '@babel/code-frame': 7.27.1 @@ -1809,14 +1387,11 @@ snapshots: debug: 4.4.1 transitivePeerDependencies: - supports-color - '@babel/types@7.28.0': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@bcoe/v8-coverage@0.2.3': {} - '@istanbuljs/load-nyc-config@1.1.0': dependencies: camelcase: 5.3.1 @@ -1824,9 +1399,7 @@ snapshots: get-package-type: 0.1.0 js-yaml: 3.14.1 resolve-from: 5.0.0 - '@istanbuljs/schema@0.1.3': {} - '@jest/console@29.7.0': dependencies: '@jest/types': 29.6.3 @@ -1835,7 +1408,6 @@ snapshots: jest-message-util: 29.7.0 jest-util: 29.7.0 slash: 3.0.0 - '@jest/core@29.7.0': dependencies: '@jest/console': 29.7.0 @@ -1870,25 +1442,21 @@ snapshots: - babel-plugin-macros - supports-color - ts-node - '@jest/environment@29.7.0': dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 '@types/node': 24.0.3 jest-mock: 29.7.0 - '@jest/expect-utils@29.7.0': dependencies: jest-get-type: 29.6.3 - '@jest/expect@29.7.0': dependencies: expect: 29.7.0 jest-snapshot: 29.7.0 transitivePeerDependencies: - supports-color - '@jest/fake-timers@29.7.0': dependencies: '@jest/types': 29.6.3 @@ -1897,7 +1465,6 @@ snapshots: jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 - '@jest/globals@29.7.0': dependencies: '@jest/environment': 29.7.0 @@ -1906,7 +1473,6 @@ snapshots: jest-mock: 29.7.0 transitivePeerDependencies: - supports-color - '@jest/reporters@29.7.0': dependencies: '@bcoe/v8-coverage': 0.2.3 @@ -1935,31 +1501,26 @@ snapshots: v8-to-istanbul: 9.3.0 transitivePeerDependencies: - supports-color - '@jest/schemas@29.6.3': dependencies: '@sinclair/typebox': 0.27.8 - '@jest/source-map@29.6.3': dependencies: '@jridgewell/trace-mapping': 0.3.29 callsites: 3.1.0 graceful-fs: 4.2.11 - '@jest/test-result@29.7.0': dependencies: '@jest/console': 29.7.0 '@jest/types': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 collect-v8-coverage: 1.0.2 - '@jest/test-sequencer@29.7.0': dependencies: '@jest/test-result': 29.7.0 graceful-fs: 4.2.11 jest-haste-map: 29.7.0 slash: 3.0.0 - '@jest/transform@29.7.0': dependencies: '@babel/core': 7.28.0 @@ -1979,7 +1540,6 @@ snapshots: write-file-atomic: 4.0.2 transitivePeerDependencies: - supports-color - '@jest/types@29.6.3': dependencies: '@jest/schemas': 29.6.3 @@ -1988,35 +1548,26 @@ snapshots: '@types/node': 24.0.3 '@types/yargs': 17.0.33 chalk: 4.1.2 - '@jridgewell/gen-mapping@0.3.12': dependencies: '@jridgewell/sourcemap-codec': 1.5.4 '@jridgewell/trace-mapping': 0.3.29 - '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/sourcemap-codec@1.5.4': {} - '@jridgewell/trace-mapping@0.3.29': dependencies: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.4 - '@noble/curves@1.2.0': dependencies: '@noble/hashes': 1.3.2 - '@noble/curves@1.9.2': dependencies: '@noble/hashes': 1.8.0 - '@noble/hashes@1.3.2': {} - '@noble/hashes@1.8.0': {} - - '@openzeppelin/relayer-sdk@1.1.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)': - dependencies: + ? '@openzeppelin/relayer-sdk@1.1.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)' + : dependencies: '@actions/exec': 1.1.1 '@solana/spl-token': 0.4.13(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) @@ -2028,17 +1579,13 @@ snapshots: - fastestsmallesttextencoderdecoder - typescript - utf-8-validate - '@sinclair/typebox@0.27.8': {} - '@sinonjs/commons@3.0.1': dependencies: type-detect: 4.0.8 - '@sinonjs/fake-timers@10.3.0': dependencies: '@sinonjs/commons': 3.0.1 - '@solana/buffer-layout-utils@0.2.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': dependencies: '@solana/buffer-layout': 4.0.1 @@ -2050,40 +1597,33 @@ snapshots: - encoding - typescript - utf-8-validate - '@solana/buffer-layout@4.0.1': dependencies: buffer: 6.0.3 - '@solana/codecs-core@2.0.0-rc.1(typescript@5.8.3)': dependencies: '@solana/errors': 2.0.0-rc.1(typescript@5.8.3) typescript: 5.8.3 - '@solana/codecs-core@2.1.1(typescript@5.8.3)': dependencies: '@solana/errors': 2.1.1(typescript@5.8.3) typescript: 5.8.3 - '@solana/codecs-data-structures@2.0.0-rc.1(typescript@5.8.3)': dependencies: '@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.3) '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.8.3) '@solana/errors': 2.0.0-rc.1(typescript@5.8.3) typescript: 5.8.3 - '@solana/codecs-numbers@2.0.0-rc.1(typescript@5.8.3)': dependencies: '@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.3) '@solana/errors': 2.0.0-rc.1(typescript@5.8.3) typescript: 5.8.3 - '@solana/codecs-numbers@2.1.1(typescript@5.8.3)': dependencies: '@solana/codecs-core': 2.1.1(typescript@5.8.3) '@solana/errors': 2.1.1(typescript@5.8.3) typescript: 5.8.3 - '@solana/codecs-strings@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: '@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.3) @@ -2091,7 +1631,6 @@ snapshots: '@solana/errors': 2.0.0-rc.1(typescript@5.8.3) fastestsmallesttextencoderdecoder: 1.0.22 typescript: 5.8.3 - '@solana/codecs@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: '@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.3) @@ -2102,19 +1641,16 @@ snapshots: typescript: 5.8.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/errors@2.0.0-rc.1(typescript@5.8.3)': dependencies: chalk: 5.4.1 commander: 12.1.0 typescript: 5.8.3 - '@solana/errors@2.1.1(typescript@5.8.3)': dependencies: chalk: 5.4.1 commander: 13.1.0 typescript: 5.8.3 - '@solana/options@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: '@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.3) @@ -2125,25 +1661,22 @@ snapshots: typescript: 5.8.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - - '@solana/spl-token-group@0.0.7(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': - dependencies: + ? '@solana/spl-token-group@0.0.7(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)' + : dependencies: '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) transitivePeerDependencies: - fastestsmallesttextencoderdecoder - typescript - - '@solana/spl-token-metadata@0.1.6(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': - dependencies: + ? '@solana/spl-token-metadata@0.1.6(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)' + : dependencies: '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) transitivePeerDependencies: - fastestsmallesttextencoderdecoder - typescript - - '@solana/spl-token@0.4.13(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)': - dependencies: + ? '@solana/spl-token@0.4.13(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)' + : dependencies: '@solana/buffer-layout': 4.0.1 '@solana/buffer-layout-utils': 0.2.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) '@solana/spl-token-group': 0.0.7(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) @@ -2156,7 +1689,6 @@ snapshots: - fastestsmallesttextencoderdecoder - typescript - utf-8-validate - '@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': dependencies: '@babel/runtime': 7.27.6 @@ -2179,11 +1711,9 @@ snapshots: - encoding - typescript - utf-8-validate - '@swc/helpers@0.5.17': dependencies: tslib: 2.8.1 - '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.28.0 @@ -2191,104 +1721,74 @@ snapshots: '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.20.7 - '@types/babel__generator@7.27.0': dependencies: '@babel/types': 7.28.0 - '@types/babel__template@7.4.4': dependencies: '@babel/parser': 7.28.0 '@babel/types': 7.28.0 - '@types/babel__traverse@7.20.7': dependencies: '@babel/types': 7.28.0 - '@types/connect@3.4.38': dependencies: '@types/node': 24.0.3 - '@types/graceful-fs@4.1.9': dependencies: '@types/node': 24.0.3 - '@types/istanbul-lib-coverage@2.0.6': {} - '@types/istanbul-lib-report@3.0.3': dependencies: '@types/istanbul-lib-coverage': 2.0.6 - '@types/istanbul-reports@3.0.4': dependencies: '@types/istanbul-lib-report': 3.0.3 - '@types/jest@29.5.14': dependencies: expect: 29.7.0 pretty-format: 29.7.0 - '@types/node@12.20.55': {} - '@types/node@22.7.5': dependencies: undici-types: 6.19.8 - '@types/node@24.0.3': dependencies: undici-types: 7.8.0 - '@types/stack-utils@2.0.3': {} - '@types/uuid@10.0.0': {} - '@types/uuid@8.3.4': {} - '@types/ws@7.4.7': dependencies: '@types/node': 24.0.3 - '@types/ws@8.18.1': dependencies: '@types/node': 24.0.3 - '@types/yargs-parser@21.0.3': {} - '@types/yargs@17.0.33': dependencies: '@types/yargs-parser': 21.0.3 - aes-js@4.0.0-beta.5: {} - agentkeepalive@4.6.0: dependencies: humanize-ms: 1.2.1 - ansi-escapes@4.3.2: dependencies: type-fest: 0.21.3 - ansi-regex@5.0.1: {} - ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 - ansi-styles@5.2.0: {} - anymatch@3.1.3: dependencies: normalize-path: 3.0.0 picomatch: 2.3.1 - argparse@1.0.10: dependencies: sprintf-js: 1.0.3 - async@3.2.6: {} - asynckit@0.4.0: {} - axios@1.11.0: dependencies: follow-redirects: 1.15.9 @@ -2296,7 +1796,6 @@ snapshots: proxy-from-env: 1.1.0 transitivePeerDependencies: - debug - babel-jest@29.7.0(@babel/core@7.28.0): dependencies: '@babel/core': 7.28.0 @@ -2309,7 +1808,6 @@ snapshots: slash: 3.0.0 transitivePeerDependencies: - supports-color - babel-plugin-istanbul@6.1.1: dependencies: '@babel/helper-plugin-utils': 7.27.1 @@ -2319,14 +1817,12 @@ snapshots: test-exclude: 6.0.0 transitivePeerDependencies: - supports-color - babel-plugin-jest-hoist@29.6.3: dependencies: '@babel/template': 7.27.2 '@babel/types': 7.28.0 '@types/babel__core': 7.20.5 '@types/babel__traverse': 7.20.7 - babel-preset-current-node-syntax@1.1.0(@babel/core@7.28.0): dependencies: '@babel/core': 7.28.0 @@ -2345,139 +1841,98 @@ snapshots: '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.28.0) '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.28.0) '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.28.0) - babel-preset-jest@29.6.3(@babel/core@7.28.0): dependencies: '@babel/core': 7.28.0 babel-plugin-jest-hoist: 29.6.3 babel-preset-current-node-syntax: 1.1.0(@babel/core@7.28.0) - balanced-match@1.0.2: {} - base-x@3.0.11: dependencies: safe-buffer: 5.2.1 - base64-js@1.5.1: {} - bigint-buffer@1.1.5: dependencies: bindings: 1.5.0 - bignumber.js@9.3.0: {} - bindings@1.5.0: dependencies: file-uri-to-path: 1.0.0 - bn.js@5.2.2: {} - borsh@0.7.0: dependencies: bn.js: 5.2.2 bs58: 4.0.1 text-encoding-utf-8: 1.0.2 - brace-expansion@1.1.12: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.0.2: dependencies: balanced-match: 1.0.2 - braces@3.0.3: dependencies: fill-range: 7.1.1 - browserslist@4.25.1: dependencies: caniuse-lite: 1.0.30001726 electron-to-chromium: 1.5.178 node-releases: 2.0.19 update-browserslist-db: 1.1.3(browserslist@4.25.1) - bs-logger@0.2.6: dependencies: fast-json-stable-stringify: 2.1.0 - bs58@4.0.1: dependencies: base-x: 3.0.11 - bser@2.1.1: dependencies: node-int64: 0.4.0 - buffer-from@1.1.2: {} - buffer@6.0.3: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 - bufferutil@4.0.9: dependencies: node-gyp-build: 4.8.4 optional: true - call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 function-bind: 1.1.2 - callsites@3.1.0: {} - camelcase@5.3.1: {} - camelcase@6.3.0: {} - caniuse-lite@1.0.30001726: {} - chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - chalk@5.4.1: {} - char-regex@1.0.2: {} - ci-info@3.9.0: {} - cjs-module-lexer@1.4.3: {} - cliui@8.0.1: dependencies: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - co@4.6.0: {} - collect-v8-coverage@1.0.2: {} - color-convert@2.0.1: dependencies: color-name: 1.1.4 - color-name@1.1.4: {} - combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 - commander@12.1.0: {} - commander@13.1.0: {} - commander@2.20.3: {} - concat-map@0.0.1: {} - convert-source-map@2.0.0: {} - create-jest@29.7.0(@types/node@24.0.3): dependencies: '@jest/types': 29.6.3 @@ -2492,76 +1947,52 @@ snapshots: - babel-plugin-macros - supports-color - ts-node - cross-spawn@7.0.6: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 - debug@4.4.1: dependencies: ms: 2.1.3 - dedent@1.6.0: {} - deepmerge@4.3.1: {} - delay@5.0.0: {} - delayed-stream@1.0.0: {} - detect-newline@3.1.0: {} - diff-sequences@29.6.3: {} - dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 es-errors: 1.3.0 gopd: 1.2.0 - ejs@3.1.10: dependencies: jake: 10.9.2 - electron-to-chromium@1.5.178: {} - emittery@0.13.1: {} - emoji-regex@8.0.0: {} - error-ex@1.3.2: dependencies: is-arrayish: 0.2.1 - es-define-property@1.0.1: {} - es-errors@1.3.0: {} - es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 - es-set-tostringtag@2.1.0: dependencies: es-errors: 1.3.0 get-intrinsic: 1.3.0 has-tostringtag: 1.0.2 hasown: 2.0.2 - es6-promise@4.2.8: {} - es6-promisify@5.0.0: dependencies: es6-promise: 4.2.8 - escalade@3.2.0: {} - escape-string-regexp@2.0.0: {} - esprima@4.0.1: {} - ethers@6.14.3(bufferutil@4.0.9)(utf-8-validate@5.0.10): dependencies: '@adraffy/ens-normalize': 1.10.1 @@ -2574,9 +2005,7 @@ snapshots: transitivePeerDependencies: - bufferutil - utf-8-validate - eventemitter3@5.0.1: {} - execa@5.1.1: dependencies: cross-spawn: 7.0.6 @@ -2588,9 +2017,7 @@ snapshots: onetime: 5.1.2 signal-exit: 3.0.7 strip-final-newline: 2.0.0 - exit@0.1.2: {} - expect@29.7.0: dependencies: '@jest/expect-utils': 29.7.0 @@ -2598,36 +2025,25 @@ snapshots: jest-matcher-utils: 29.7.0 jest-message-util: 29.7.0 jest-util: 29.7.0 - eyes@0.1.8: {} - fast-json-stable-stringify@2.1.0: {} - fast-stable-stringify@1.0.0: {} - fastestsmallesttextencoderdecoder@1.0.22: {} - fb-watchman@2.0.2: dependencies: bser: 2.1.1 - file-uri-to-path@1.0.0: {} - filelist@1.0.4: dependencies: minimatch: 5.1.6 - fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 - find-up@4.1.0: dependencies: locate-path: 5.0.0 path-exists: 4.0.0 - follow-redirects@1.15.9: {} - form-data@4.0.4: dependencies: asynckit: 0.4.0 @@ -2635,18 +2051,12 @@ snapshots: es-set-tostringtag: 2.1.0 hasown: 2.0.2 mime-types: 2.1.35 - fs.realpath@1.0.0: {} - fsevents@2.3.3: optional: true - function-bind@1.1.2: {} - gensync@1.0.0-beta.2: {} - get-caller-file@2.0.5: {} - get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -2659,16 +2069,12 @@ snapshots: has-symbols: 1.1.0 hasown: 2.0.2 math-intrinsics: 1.1.0 - get-package-type@0.1.0: {} - get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 - get-stream@6.0.1: {} - glob@7.2.3: dependencies: fs.realpath: 1.0.0 @@ -2677,71 +2083,46 @@ snapshots: minimatch: 3.1.2 once: 1.4.0 path-is-absolute: 1.0.1 - gopd@1.2.0: {} - graceful-fs@4.2.11: {} - has-flag@4.0.0: {} - has-symbols@1.1.0: {} - has-tostringtag@1.0.2: dependencies: has-symbols: 1.1.0 - hasown@2.0.2: dependencies: function-bind: 1.1.2 - html-escaper@2.0.2: {} - human-signals@2.1.0: {} - humanize-ms@1.2.1: dependencies: ms: 2.1.3 - husky@9.1.7: {} - ieee754@1.2.1: {} - import-local@3.2.0: dependencies: pkg-dir: 4.2.0 resolve-cwd: 3.0.0 - imurmurhash@0.1.4: {} - inflight@1.0.6: dependencies: once: 1.4.0 wrappy: 1.0.2 - inherits@2.0.4: {} - is-arrayish@0.2.1: {} - is-core-module@2.16.1: dependencies: hasown: 2.0.2 - is-fullwidth-code-point@3.0.0: {} - is-generator-fn@2.1.0: {} - is-number@7.0.0: {} - is-stream@2.0.1: {} - isexe@2.0.0: {} - isomorphic-ws@4.0.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)): dependencies: ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) - istanbul-lib-coverage@3.2.2: {} - istanbul-lib-instrument@5.2.1: dependencies: '@babel/core': 7.28.0 @@ -2751,7 +2132,6 @@ snapshots: semver: 6.3.1 transitivePeerDependencies: - supports-color - istanbul-lib-instrument@6.0.3: dependencies: '@babel/core': 7.28.0 @@ -2761,13 +2141,11 @@ snapshots: semver: 7.7.2 transitivePeerDependencies: - supports-color - istanbul-lib-report@3.0.1: dependencies: istanbul-lib-coverage: 3.2.2 make-dir: 4.0.0 supports-color: 7.2.0 - istanbul-lib-source-maps@4.0.1: dependencies: debug: 4.4.1 @@ -2775,19 +2153,16 @@ snapshots: source-map: 0.6.1 transitivePeerDependencies: - supports-color - istanbul-reports@3.1.7: dependencies: html-escaper: 2.0.2 istanbul-lib-report: 3.0.1 - jake@10.9.2: dependencies: async: 3.2.6 chalk: 4.1.2 filelist: 1.0.4 minimatch: 3.1.2 - jayson@4.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10): dependencies: '@types/connect': 3.4.38 @@ -2805,13 +2180,11 @@ snapshots: transitivePeerDependencies: - bufferutil - utf-8-validate - jest-changed-files@29.7.0: dependencies: execa: 5.1.1 jest-util: 29.7.0 p-limit: 3.1.0 - jest-circus@29.7.0: dependencies: '@jest/environment': 29.7.0 @@ -2837,7 +2210,6 @@ snapshots: transitivePeerDependencies: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@24.0.3): dependencies: '@jest/core': 29.7.0 @@ -2856,7 +2228,6 @@ snapshots: - babel-plugin-macros - supports-color - ts-node - jest-config@29.7.0(@types/node@24.0.3): dependencies: '@babel/core': 7.28.0 @@ -2886,18 +2257,15 @@ snapshots: transitivePeerDependencies: - babel-plugin-macros - supports-color - jest-diff@29.7.0: dependencies: chalk: 4.1.2 diff-sequences: 29.6.3 jest-get-type: 29.6.3 pretty-format: 29.7.0 - jest-docblock@29.7.0: dependencies: detect-newline: 3.1.0 - jest-each@29.7.0: dependencies: '@jest/types': 29.6.3 @@ -2905,7 +2273,6 @@ snapshots: jest-get-type: 29.6.3 jest-util: 29.7.0 pretty-format: 29.7.0 - jest-environment-node@29.7.0: dependencies: '@jest/environment': 29.7.0 @@ -2914,9 +2281,7 @@ snapshots: '@types/node': 24.0.3 jest-mock: 29.7.0 jest-util: 29.7.0 - jest-get-type@29.6.3: {} - jest-haste-map@29.7.0: dependencies: '@jest/types': 29.6.3 @@ -2932,19 +2297,16 @@ snapshots: walker: 1.0.8 optionalDependencies: fsevents: 2.3.3 - jest-leak-detector@29.7.0: dependencies: jest-get-type: 29.6.3 pretty-format: 29.7.0 - jest-matcher-utils@29.7.0: dependencies: chalk: 4.1.2 jest-diff: 29.7.0 jest-get-type: 29.6.3 pretty-format: 29.7.0 - jest-message-util@29.7.0: dependencies: '@babel/code-frame': 7.27.1 @@ -2956,26 +2318,21 @@ snapshots: pretty-format: 29.7.0 slash: 3.0.0 stack-utils: 2.0.6 - jest-mock@29.7.0: dependencies: '@jest/types': 29.6.3 '@types/node': 24.0.3 jest-util: 29.7.0 - jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): optionalDependencies: jest-resolve: 29.7.0 - jest-regex-util@29.6.3: {} - jest-resolve-dependencies@29.7.0: dependencies: jest-regex-util: 29.6.3 jest-snapshot: 29.7.0 transitivePeerDependencies: - supports-color - jest-resolve@29.7.0: dependencies: chalk: 4.1.2 @@ -2987,7 +2344,6 @@ snapshots: resolve: 1.22.10 resolve.exports: 2.0.3 slash: 3.0.0 - jest-runner@29.7.0: dependencies: '@jest/console': 29.7.0 @@ -3013,7 +2369,6 @@ snapshots: source-map-support: 0.5.13 transitivePeerDependencies: - supports-color - jest-runtime@29.7.0: dependencies: '@jest/environment': 29.7.0 @@ -3040,7 +2395,6 @@ snapshots: strip-bom: 4.0.0 transitivePeerDependencies: - supports-color - jest-snapshot@29.7.0: dependencies: '@babel/core': 7.28.0 @@ -3065,7 +2419,6 @@ snapshots: semver: 7.7.2 transitivePeerDependencies: - supports-color - jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 @@ -3074,7 +2427,6 @@ snapshots: ci-info: 3.9.0 graceful-fs: 4.2.11 picomatch: 2.3.1 - jest-validate@29.7.0: dependencies: '@jest/types': 29.6.3 @@ -3083,7 +2435,6 @@ snapshots: jest-get-type: 29.6.3 leven: 3.1.0 pretty-format: 29.7.0 - jest-watcher@29.7.0: dependencies: '@jest/test-result': 29.7.0 @@ -3094,14 +2445,12 @@ snapshots: emittery: 0.13.1 jest-util: 29.7.0 string-length: 4.0.2 - jest-worker@29.7.0: dependencies: '@types/node': 24.0.3 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@24.0.3): dependencies: '@jest/core': 29.7.0 @@ -3113,174 +2462,117 @@ snapshots: - babel-plugin-macros - supports-color - ts-node - js-tokens@4.0.0: {} - js-yaml@3.14.1: dependencies: argparse: 1.0.10 esprima: 4.0.1 - jsesc@3.1.0: {} - json-parse-even-better-errors@2.3.1: {} - json-stringify-safe@5.0.1: {} - json5@2.2.3: {} - kleur@3.0.3: {} - leven@3.1.0: {} - lines-and-columns@1.2.4: {} - locate-path@5.0.0: dependencies: p-locate: 4.1.0 - lodash.memoize@4.1.2: {} - lru-cache@5.1.1: dependencies: yallist: 3.1.1 - make-dir@4.0.0: dependencies: semver: 7.7.2 - make-error@1.3.6: {} - makeerror@1.0.12: dependencies: tmpl: 1.0.5 - math-intrinsics@1.1.0: {} - merge-stream@2.0.0: {} - micromatch@4.0.8: dependencies: braces: 3.0.3 picomatch: 2.3.1 - mime-db@1.52.0: {} - mime-types@2.1.35: dependencies: mime-db: 1.52.0 - mimic-fn@2.1.0: {} - minimatch@3.1.2: dependencies: brace-expansion: 1.1.12 - minimatch@5.1.6: dependencies: brace-expansion: 2.0.2 - ms@2.1.3: {} - natural-compare@1.4.0: {} - node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 - node-gyp-build@4.8.4: optional: true - node-int64@0.4.0: {} - node-releases@2.0.19: {} - normalize-path@3.0.0: {} - npm-run-path@4.0.1: dependencies: path-key: 3.1.1 - once@1.4.0: dependencies: wrappy: 1.0.2 - onetime@5.1.2: dependencies: mimic-fn: 2.1.0 - p-limit@2.3.0: dependencies: p-try: 2.2.0 - p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 - p-locate@4.1.0: dependencies: p-limit: 2.3.0 - p-try@2.2.0: {} - parse-json@5.2.0: dependencies: '@babel/code-frame': 7.27.1 error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 - path-exists@4.0.0: {} - path-is-absolute@1.0.1: {} - path-key@3.1.1: {} - path-parse@1.0.7: {} - picocolors@1.1.1: {} - picomatch@2.3.1: {} - pirates@4.0.7: {} - pkg-dir@4.2.0: dependencies: find-up: 4.1.0 - pretty-format@29.7.0: dependencies: '@jest/schemas': 29.6.3 ansi-styles: 5.2.0 react-is: 18.3.1 - prompts@2.4.2: dependencies: kleur: 3.0.3 sisteransi: 1.0.5 - proxy-from-env@1.1.0: {} - pure-rand@6.1.0: {} - react-is@18.3.1: {} - require-directory@2.1.1: {} - resolve-cwd@3.0.0: dependencies: resolve-from: 5.0.0 - resolve-from@5.0.0: {} - resolve.exports@2.0.3: {} - resolve@1.22.10: dependencies: is-core-module: 2.16.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - rpc-websockets@9.1.1: dependencies: '@swc/helpers': 0.5.17 @@ -3293,95 +2585,65 @@ snapshots: optionalDependencies: bufferutil: 4.0.9 utf-8-validate: 5.0.10 - safe-buffer@5.2.1: {} - semver@6.3.1: {} - semver@7.7.2: {} - shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 - shebang-regex@3.0.0: {} - signal-exit@3.0.7: {} - sisteransi@1.0.5: {} - slash@3.0.0: {} - source-map-support@0.5.13: dependencies: buffer-from: 1.1.2 source-map: 0.6.1 - source-map@0.6.1: {} - sprintf-js@1.0.3: {} - stack-utils@2.0.6: dependencies: escape-string-regexp: 2.0.0 - stream-chain@2.2.5: {} - stream-json@1.9.1: dependencies: stream-chain: 2.2.5 - string-length@4.0.2: dependencies: char-regex: 1.0.2 strip-ansi: 6.0.1 - string-width@4.2.3: dependencies: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 - strip-bom@4.0.0: {} - strip-final-newline@2.0.0: {} - strip-json-comments@3.1.1: {} - superstruct@2.0.2: {} - supports-color@7.2.0: dependencies: has-flag: 4.0.0 - supports-color@8.1.1: dependencies: has-flag: 4.0.0 - supports-preserve-symlinks-flag@1.0.0: {} - test-exclude@6.0.0: dependencies: '@istanbuljs/schema': 0.1.3 glob: 7.2.3 minimatch: 3.1.2 - text-encoding-utf-8@1.0.2: {} - tmpl@1.0.5: {} - to-regex-range@5.0.1: dependencies: is-number: 7.0.0 - tr46@0.0.3: {} - - ts-jest@29.4.0(@babel/core@7.28.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.0))(jest-util@29.7.0)(jest@29.7.0(@types/node@24.0.3))(typescript@5.8.3): - dependencies: + ? ts-jest@29.4.0(@babel/core@7.28.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.0))(jest-util@29.7.0)(jest@29.7.0(@types/node@24.0.3))(typescript@5.8.3) + : dependencies: bs-logger: 0.2.6 ejs: 3.1.10 fast-json-stable-stringify: 2.1.0 @@ -3399,88 +2661,62 @@ snapshots: '@jest/types': 29.6.3 babel-jest: 29.7.0(@babel/core@7.28.0) jest-util: 29.7.0 - tslib@2.7.0: {} - tslib@2.8.1: {} - type-detect@4.0.8: {} - type-fest@0.21.3: {} - type-fest@4.41.0: {} - typescript@5.8.3: {} - undici-types@6.19.8: {} - undici-types@7.8.0: {} - update-browserslist-db@1.1.3(browserslist@4.25.1): dependencies: browserslist: 4.25.1 escalade: 3.2.0 picocolors: 1.1.1 - utf-8-validate@5.0.10: dependencies: node-gyp-build: 4.8.4 optional: true - uuid@11.1.0: {} - uuid@8.3.2: {} - v8-to-istanbul@9.3.0: dependencies: '@jridgewell/trace-mapping': 0.3.29 '@types/istanbul-lib-coverage': 2.0.6 convert-source-map: 2.0.0 - walker@1.0.8: dependencies: makeerror: 1.0.12 - webidl-conversions@3.0.1: {} - whatwg-url@5.0.0: dependencies: tr46: 0.0.3 webidl-conversions: 3.0.1 - which@2.0.2: dependencies: isexe: 2.0.0 - wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - wrappy@1.0.2: {} - write-file-atomic@4.0.2: dependencies: imurmurhash: 0.1.4 signal-exit: 3.0.7 - ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10): optionalDependencies: bufferutil: 4.0.9 utf-8-validate: 5.0.10 - ws@8.17.1(bufferutil@4.0.9)(utf-8-validate@5.0.10): optionalDependencies: bufferutil: 4.0.9 utf-8-validate: 5.0.10 - y18n@5.0.8: {} - yallist@3.1.1: {} - yargs-parser@21.1.1: {} - yargs@17.7.2: dependencies: cliui: 8.0.1 @@ -3490,5 +2726,4 @@ snapshots: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 21.1.1 - yocto-queue@0.1.0: {} From 86b53d89fe92f6ebf1cfa2825cc555a8fd5ef002 Mon Sep 17 00:00:00 2001 From: Marcos Date: Thu, 24 Jul 2025 16:45:30 -0300 Subject: [PATCH 07/22] chore: Hide API key feature under feature flag --- Cargo.toml | 3 +++ src/api/controllers/api_key.rs | 9 +++++++++ src/api/routes/api_keys.rs | 13 +++++++++++-- src/api/routes/mod.rs | 6 ++++-- 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 87ac4310f..ee04e1313 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -121,3 +121,6 @@ path = "helpers/generate_openapi.rs" [lib] path = "src/lib.rs" + +[features] +api-keys = [] diff --git a/src/api/controllers/api_key.rs b/src/api/controllers/api_key.rs index 05d1d26ee..73a6b54e7 100644 --- a/src/api/controllers/api_key.rs +++ b/src/api/controllers/api_key.rs @@ -4,6 +4,7 @@ //! - Create api keys //! - List api keys //! - Delete api keys +#[cfg(feature = "api-keys")] use crate::{ jobs::JobProducerTrait, models::{ @@ -16,9 +17,13 @@ use crate::{ Repository, TransactionCounterTrait, TransactionRepository, }, }; +#[cfg(feature = "api-keys")] use actix_web::HttpResponse; +#[cfg(feature = "api-keys")] use chrono::Utc; +#[cfg(feature = "api-keys")] use eyre::Result; +#[cfg(feature = "api-keys")] use uuid::Uuid; /// Create api key @@ -34,6 +39,7 @@ use uuid::Uuid; /// # Returns /// /// The result of the plugin call. +#[cfg(feature = "api-keys")] pub async fn create_api_key( api_key_request: ApiKeyRequest, state: ThinDataAppState, @@ -77,6 +83,7 @@ where /// # Returns /// /// The result of the api key list. +#[cfg(feature = "api-keys")] pub async fn list_api_keys( query: PaginationQuery, state: ThinDataAppState, @@ -125,6 +132,7 @@ where /// * `api_key_id` - The id of the api key. /// * `state` - The application state containing the api key repository. /// +#[cfg(feature = "api-keys")] pub async fn get_api_key_permissions( api_key_id: String, state: ThinDataAppState, @@ -157,6 +165,7 @@ where /// /// If the API key is the last Admin API key in the system, it will return an error. /// +#[cfg(feature = "api-keys")] pub async fn delete_api_key( api_key_id: String, state: ThinDataAppState, diff --git a/src/api/routes/api_keys.rs b/src/api/routes/api_keys.rs index 1dd896552..95951833c 100644 --- a/src/api/routes/api_keys.rs +++ b/src/api/routes/api_keys.rs @@ -1,10 +1,15 @@ //! This module defines the HTTP routes for api keys operations. //! The routes are integrated with the Actix-web framework and interact with the api key controller. -use crate::api::controllers::api_key; -use crate::models::{ApiKeyRequest, DefaultAppState, PaginationQuery}; +#[cfg(feature = "api-keys")] +use crate::{ + api::controllers::api_key, + models::{ApiKeyRequest, DefaultAppState, PaginationQuery}, +}; +#[cfg(feature = "api-keys")] use actix_web::{delete, get, post, web, Responder}; /// List plugins +#[cfg(feature = "api-keys")] #[get("/api-keys")] async fn list_api_keys( query: web::Query, @@ -13,6 +18,7 @@ async fn list_api_keys( api_key::list_api_keys(query.into_inner(), data).await } +#[cfg(feature = "api-keys")] #[get("/api-keys/{api_key_id}/permissions")] async fn get_api_key_permissions( api_key_id: web::Path, @@ -22,6 +28,7 @@ async fn get_api_key_permissions( } /// Calls a plugin method. +#[cfg(feature = "api-keys")] #[post("/api-keys")] async fn create_api_key( req: web::Json, @@ -30,6 +37,7 @@ async fn create_api_key( api_key::create_api_key(req.into_inner(), data).await } +#[cfg(feature = "api-keys")] #[delete("/api-keys/{api_key_id}")] async fn delete_api_key( api_key_id: web::Path, @@ -39,6 +47,7 @@ async fn delete_api_key( } /// Initializes the routes for api keys. +#[cfg(feature = "api-keys")] pub fn init(cfg: &mut web::ServiceConfig) { // Register routes with literal segments before routes with path parameters cfg.service(create_api_key); // /api-keys diff --git a/src/api/routes/mod.rs b/src/api/routes/mod.rs index da02a3d54..129f35ee4 100644 --- a/src/api/routes/mod.rs +++ b/src/api/routes/mod.rs @@ -19,6 +19,8 @@ pub fn configure_routes(cfg: &mut web::ServiceConfig) { cfg.configure(health::init) .configure(relayer::init) .configure(plugin::init) - .configure(metrics::init) - .configure(api_keys::init); + .configure(metrics::init); + + #[cfg(feature = "api-keys")] + cfg.configure(api_keys::init); } From 85072c1ccaaa5f1b26dd17603910809956b4aae9 Mon Sep 17 00:00:00 2001 From: Marcos Date: Fri, 25 Jul 2025 09:30:14 -0300 Subject: [PATCH 08/22] fix: Rename flag to authV2 --- Cargo.toml | 2 +- src/api/controllers/api_key.rs | 18 +++++++++--------- src/api/routes/api_keys.rs | 14 +++++++------- src/api/routes/mod.rs | 2 +- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ee04e1313..56b17e89e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -123,4 +123,4 @@ path = "helpers/generate_openapi.rs" path = "src/lib.rs" [features] -api-keys = [] +authV2 = [] diff --git a/src/api/controllers/api_key.rs b/src/api/controllers/api_key.rs index 73a6b54e7..7b2ddbdb8 100644 --- a/src/api/controllers/api_key.rs +++ b/src/api/controllers/api_key.rs @@ -4,7 +4,7 @@ //! - Create api keys //! - List api keys //! - Delete api keys -#[cfg(feature = "api-keys")] +#[cfg(feature = "authV2")] use crate::{ jobs::JobProducerTrait, models::{ @@ -17,13 +17,13 @@ use crate::{ Repository, TransactionCounterTrait, TransactionRepository, }, }; -#[cfg(feature = "api-keys")] +#[cfg(feature = "authV2")] use actix_web::HttpResponse; -#[cfg(feature = "api-keys")] +#[cfg(feature = "authV2")] use chrono::Utc; -#[cfg(feature = "api-keys")] +#[cfg(feature = "authV2")] use eyre::Result; -#[cfg(feature = "api-keys")] +#[cfg(feature = "authV2")] use uuid::Uuid; /// Create api key @@ -39,7 +39,7 @@ use uuid::Uuid; /// # Returns /// /// The result of the plugin call. -#[cfg(feature = "api-keys")] +#[cfg(feature = "authV2")] pub async fn create_api_key( api_key_request: ApiKeyRequest, state: ThinDataAppState, @@ -83,7 +83,7 @@ where /// # Returns /// /// The result of the api key list. -#[cfg(feature = "api-keys")] +#[cfg(feature = "authV2")] pub async fn list_api_keys( query: PaginationQuery, state: ThinDataAppState, @@ -132,7 +132,7 @@ where /// * `api_key_id` - The id of the api key. /// * `state` - The application state containing the api key repository. /// -#[cfg(feature = "api-keys")] +#[cfg(feature = "authV2")] pub async fn get_api_key_permissions( api_key_id: String, state: ThinDataAppState, @@ -165,7 +165,7 @@ where /// /// If the API key is the last Admin API key in the system, it will return an error. /// -#[cfg(feature = "api-keys")] +#[cfg(feature = "authV2")] pub async fn delete_api_key( api_key_id: String, state: ThinDataAppState, diff --git a/src/api/routes/api_keys.rs b/src/api/routes/api_keys.rs index 95951833c..c162302f9 100644 --- a/src/api/routes/api_keys.rs +++ b/src/api/routes/api_keys.rs @@ -1,15 +1,15 @@ //! This module defines the HTTP routes for api keys operations. //! The routes are integrated with the Actix-web framework and interact with the api key controller. -#[cfg(feature = "api-keys")] +#[cfg(feature = "authV2")] use crate::{ api::controllers::api_key, models::{ApiKeyRequest, DefaultAppState, PaginationQuery}, }; -#[cfg(feature = "api-keys")] +#[cfg(feature = "authV2")] use actix_web::{delete, get, post, web, Responder}; /// List plugins -#[cfg(feature = "api-keys")] +#[cfg(feature = "authV2")] #[get("/api-keys")] async fn list_api_keys( query: web::Query, @@ -18,7 +18,7 @@ async fn list_api_keys( api_key::list_api_keys(query.into_inner(), data).await } -#[cfg(feature = "api-keys")] +#[cfg(feature = "authV2")] #[get("/api-keys/{api_key_id}/permissions")] async fn get_api_key_permissions( api_key_id: web::Path, @@ -28,7 +28,7 @@ async fn get_api_key_permissions( } /// Calls a plugin method. -#[cfg(feature = "api-keys")] +#[cfg(feature = "authV2")] #[post("/api-keys")] async fn create_api_key( req: web::Json, @@ -37,7 +37,7 @@ async fn create_api_key( api_key::create_api_key(req.into_inner(), data).await } -#[cfg(feature = "api-keys")] +#[cfg(feature = "authV2")] #[delete("/api-keys/{api_key_id}")] async fn delete_api_key( api_key_id: web::Path, @@ -47,7 +47,7 @@ async fn delete_api_key( } /// Initializes the routes for api keys. -#[cfg(feature = "api-keys")] +#[cfg(feature = "authV2")] pub fn init(cfg: &mut web::ServiceConfig) { // Register routes with literal segments before routes with path parameters cfg.service(create_api_key); // /api-keys diff --git a/src/api/routes/mod.rs b/src/api/routes/mod.rs index 129f35ee4..ed872d2f1 100644 --- a/src/api/routes/mod.rs +++ b/src/api/routes/mod.rs @@ -21,6 +21,6 @@ pub fn configure_routes(cfg: &mut web::ServiceConfig) { .configure(plugin::init) .configure(metrics::init); - #[cfg(feature = "api-keys")] + #[cfg(feature = "authV2")] cfg.configure(api_keys::init); } From 9761344ad77acd968e5b6028399a46c55cfed93e Mon Sep 17 00:00:00 2001 From: Marcos Date: Fri, 25 Jul 2025 10:26:39 -0300 Subject: [PATCH 09/22] test: Add redis tests + docs --- README.md | 19 ++ src/repositories/api_key/api_key_in_memory.rs | 11 + src/repositories/api_key/api_key_redis.rs | 248 +++++++++++++++++- src/repositories/api_key/mod.rs | 16 ++ 4 files changed, 293 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b058d7fcd..87f80ca2a 100644 --- a/README.md +++ b/README.md @@ -270,6 +270,25 @@ cargo test properties cargo test integration ``` +#### Run tests against Redis + +1. You can start a Redis instance using the following command: + +```bash +docker run -d \ + --name redis \ + -p 6379:6379 \ + redis:latest +``` + +2. Then remove the `#[ignore = "Requires active Redis instance"]` attribute from the tests you want to run. + +3. Run the tests using single thread to avoid race conditions within suites: + +```bash +cargo test your_test_regex -- --test-threads=1 +``` + ### Config files Create `config/config.json` file. You can use `config/config.example.json` as a starting point: diff --git a/src/repositories/api_key/api_key_in_memory.rs b/src/repositories/api_key/api_key_in_memory.rs index e5d772cc8..166478bce 100644 --- a/src/repositories/api_key/api_key_in_memory.rs +++ b/src/repositories/api_key/api_key_in_memory.rs @@ -109,6 +109,17 @@ impl ApiKeyRepositoryTrait for InMemoryApiKeyRepository { store.remove(api_key_id); Ok(()) } + + async fn has_entries(&self) -> Result { + let store = Self::acquire_lock(&self.store).await?; + Ok(!store.is_empty()) + } + + async fn drop_all_entries(&self) -> Result<(), RepositoryError> { + let mut store = Self::acquire_lock(&self.store).await?; + store.clear(); + Ok(()) + } } #[cfg(test)] diff --git a/src/repositories/api_key/api_key_redis.rs b/src/repositories/api_key/api_key_redis.rs index 4fdcec6e3..1f6804a2c 100644 --- a/src/repositories/api_key/api_key_redis.rs +++ b/src/repositories/api_key/api_key_redis.rs @@ -122,6 +122,7 @@ impl ApiKeyRepositoryTrait for RedisApiKeyRepository { } let key = self.api_key_key(&entity.id); + let list_key = self.api_key_list_key(); let json = self.serialize_entity(&entity, |a| &a.id, "apikey")?; let mut conn = self.client.as_ref().clone(); @@ -142,6 +143,7 @@ impl ApiKeyRepositoryTrait for RedisApiKeyRepository { let mut pipe = redis::pipe(); pipe.atomic(); pipe.set(&key, json); + pipe.sadd(&list_key, &entity.id); pipe.exec_async(&mut conn) .await @@ -295,7 +297,251 @@ impl ApiKeyRepositoryTrait for RedisApiKeyRepository { Ok(count as usize) } + + async fn has_entries(&self) -> Result { + let mut conn = self.client.as_ref().clone(); + let plugin_list_key = self.api_key_list_key(); + + debug!("Checking if plugin entries exist"); + + let exists: bool = conn + .exists(&plugin_list_key) + .await + .map_err(|e| self.map_redis_error(e, "has_entries_check"))?; + + debug!("Plugin entries exist: {}", exists); + Ok(exists) + } + + async fn drop_all_entries(&self) -> Result<(), RepositoryError> { + let mut conn = self.client.as_ref().clone(); + let plugin_list_key = self.api_key_list_key(); + + debug!("Dropping all plugin entries"); + + // Get all plugin IDs first + let plugin_ids: Vec = conn + .smembers(&plugin_list_key) + .await + .map_err(|e| self.map_redis_error(e, "drop_all_entries_get_ids"))?; + + if plugin_ids.is_empty() { + debug!("No plugin entries to drop"); + return Ok(()); + } + + // Use pipeline for atomic operations + let mut pipe = redis::pipe(); + pipe.atomic(); + + // Delete all individual plugin entries + for plugin_id in &plugin_ids { + let plugin_key = self.api_key_key(plugin_id); + pipe.del(&plugin_key); + } + + // Delete the plugin list key + pipe.del(&plugin_list_key); + + pipe.exec_async(&mut conn) + .await + .map_err(|e| self.map_redis_error(e, "drop_all_entries_pipeline"))?; + + debug!("Dropped {} plugin entries", plugin_ids.len()); + Ok(()) + } } #[cfg(test)] -mod tests {} +mod tests { + use super::*; + use chrono::Utc; + + fn create_test_api_key(id: &str) -> ApiKeyModel { + ApiKeyModel { + id: id.to_string(), + value: "test-value".to_string(), + name: "test-name".to_string(), + allowed_origins: vec!["*".to_string()], + permissions: vec!["relayer:all:execute".to_string()], + created_at: Utc::now().to_string(), + } + } + + async fn setup_test_repo() -> RedisApiKeyRepository { + let redis_url = + std::env::var("REDIS_URL").unwrap_or_else(|_| "redis://127.0.0.1:6379/".to_string()); + let client = redis::Client::open(redis_url).expect("Failed to create Redis client"); + let mut connection_manager = ConnectionManager::new(client) + .await + .expect("Failed to create Redis connection manager"); + + // Clear the api key list + connection_manager + .del::<&str, ()>("test_api_key:apikey_list") + .await + .unwrap(); + + RedisApiKeyRepository::new(Arc::new(connection_manager), "test_api_key".to_string()) + .expect("Failed to create Redis api key repository") + } + + #[tokio::test] + #[ignore = "Requires active Redis instance"] + async fn test_new_repository_creation() { + let repo = setup_test_repo().await; + assert_eq!(repo.key_prefix, "test_api_key"); + } + + #[tokio::test] + #[ignore = "Requires active Redis instance"] + async fn test_new_repository_empty_prefix_fails() { + let client = + redis::Client::open("redis://127.0.0.1:6379/").expect("Failed to create Redis client"); + let connection_manager = redis::aio::ConnectionManager::new(client) + .await + .expect("Failed to create Redis connection manager"); + + let result = RedisApiKeyRepository::new(Arc::new(connection_manager), "".to_string()); + assert!(result.is_err()); + assert!(result + .unwrap_err() + .to_string() + .contains("key prefix cannot be empty")); + } + + #[tokio::test] + #[ignore = "Requires active Redis instance"] + async fn test_key_generation() { + let repo = setup_test_repo().await; + + let api_key_key = repo.api_key_key("test-api-key"); + assert_eq!(api_key_key, "test_api_key:apikey:test-api-key"); + + let list_key = repo.api_key_list_key(); + assert_eq!(list_key, "test_api_key:apikey_list"); + } + + #[tokio::test] + #[ignore = "Requires active Redis instance"] + async fn test_serialize_deserialize_api_key() { + let repo = setup_test_repo().await; + let api_key = create_test_api_key("test-api-key"); + + let json = repo + .serialize_entity(&api_key, |a| &a.id, "apikey") + .unwrap(); + let deserialized: ApiKeyModel = repo + .deserialize_entity(&json, &api_key.id, "apikey") + .unwrap(); + + assert_eq!(api_key.id, deserialized.id); + assert_eq!(api_key.value, deserialized.value); + assert_eq!(api_key.name, deserialized.name); + assert_eq!(api_key.allowed_origins, deserialized.allowed_origins); + assert_eq!(api_key.permissions, deserialized.permissions); + assert_eq!(api_key.created_at, deserialized.created_at); + } + + #[tokio::test] + #[ignore = "Requires active Redis instance"] + async fn test_create_api_key() { + let repo = setup_test_repo().await; + let api_key_id = uuid::Uuid::new_v4().to_string(); + let api_key = create_test_api_key(&api_key_id); + + let result = repo.create(api_key.clone()).await; + assert!(result.is_ok()); + + let retrieved = repo.get_by_id(&api_key_id).await.unwrap(); + assert!(retrieved.is_some()); + let retrieved = retrieved.unwrap(); + assert_eq!(retrieved.id, api_key.id); + assert_eq!(retrieved.value, api_key.value); + } + + #[tokio::test] + #[ignore = "Requires active Redis instance"] + async fn test_get_nonexistent_api_key() { + let repo = setup_test_repo().await; + + let result = repo.get_by_id("nonexistent-api-key").await; + assert!(matches!(result, Ok(None))); + } + + #[tokio::test] + #[ignore = "Requires active Redis instance"] + async fn test_error_handling_empty_id() { + let repo = setup_test_repo().await; + + let result = repo.get_by_id("").await; + assert!(result.is_err()); + assert!(result + .unwrap_err() + .to_string() + .contains("ID cannot be empty")); + } + + #[tokio::test] + #[ignore = "Requires active Redis instance"] + async fn test_get_by_ids_api_keys() { + let repo = setup_test_repo().await; + let api_key_id1 = uuid::Uuid::new_v4().to_string(); + let api_key_id2 = uuid::Uuid::new_v4().to_string(); + let api_key1 = create_test_api_key(&api_key_id1); + let api_key2 = create_test_api_key(&api_key_id2); + + repo.create(api_key1.clone()).await.unwrap(); + repo.create(api_key2.clone()).await.unwrap(); + + let retrieved = repo + .get_by_ids(&[api_key1.id.clone(), api_key2.id.clone()]) + .await + .unwrap(); + assert!(retrieved.results.len() == 2); + assert_eq!(retrieved.results[0].id, api_key1.id); + assert_eq!(retrieved.results[1].id, api_key2.id); + assert_eq!(retrieved.failed_ids.len(), 0); + } + + #[tokio::test] + #[ignore = "Requires active Redis instance"] + async fn test_list_paginated_api_keys() { + let repo = setup_test_repo().await; + + let api_key_id1 = uuid::Uuid::new_v4().to_string(); + let api_key_id2 = uuid::Uuid::new_v4().to_string(); + let api_key_id3 = uuid::Uuid::new_v4().to_string(); + let api_key1 = create_test_api_key(&api_key_id1); + let api_key2 = create_test_api_key(&api_key_id2); + let api_key3 = create_test_api_key(&api_key_id3); + + repo.create(api_key1.clone()).await.unwrap(); + repo.create(api_key2.clone()).await.unwrap(); + repo.create(api_key3.clone()).await.unwrap(); + + let query = PaginationQuery { + page: 1, + per_page: 2, + }; + + let result = repo.list_paginated(query).await; + assert!(result.is_ok()); + let result = result.unwrap(); + println!("result: {:?}", result); + assert!(result.items.len() == 2); + } + + #[tokio::test] + #[ignore = "Requires active Redis instance"] + async fn test_has_entries() { + let repo = setup_test_repo().await; + assert!(!repo.has_entries().await.unwrap()); + repo.create(create_test_api_key("test-api-key")) + .await + .unwrap(); + assert!(repo.has_entries().await.unwrap()); + repo.drop_all_entries().await.unwrap(); + assert!(!repo.has_entries().await.unwrap()); + } +} diff --git a/src/repositories/api_key/mod.rs b/src/repositories/api_key/mod.rs index 8e25c991d..2b2bde47a 100644 --- a/src/repositories/api_key/mod.rs +++ b/src/repositories/api_key/mod.rs @@ -46,6 +46,8 @@ pub trait ApiKeyRepositoryTrait { async fn count(&self) -> Result; async fn list_permissions(&self, api_key_id: &str) -> Result, RepositoryError>; async fn delete_by_id(&self, api_key_id: &str) -> Result<(), RepositoryError>; + async fn has_entries(&self) -> Result; + async fn drop_all_entries(&self) -> Result<(), RepositoryError>; } /// Enum wrapper for different plugin repository implementations @@ -115,6 +117,20 @@ impl ApiKeyRepositoryTrait for ApiKeyRepositoryStorage { ApiKeyRepositoryStorage::Redis(repo) => repo.count().await, } } + + async fn has_entries(&self) -> Result { + match self { + ApiKeyRepositoryStorage::InMemory(repo) => repo.has_entries().await, + ApiKeyRepositoryStorage::Redis(repo) => repo.has_entries().await, + } + } + + async fn drop_all_entries(&self) -> Result<(), RepositoryError> { + match self { + ApiKeyRepositoryStorage::InMemory(repo) => repo.drop_all_entries().await, + ApiKeyRepositoryStorage::Redis(repo) => repo.drop_all_entries().await, + } + } } impl PartialEq for ApiKeyModel { From 68462d46b8649131cc8d7d1cc819acb31ec94663 Mon Sep 17 00:00:00 2001 From: Marcos Date: Fri, 25 Jul 2025 10:58:46 -0300 Subject: [PATCH 10/22] test: Add repository tests --- src/repositories/api_key/api_key_in_memory.rs | 179 +++++++++- src/repositories/api_key/mod.rs | 321 +++++++++++++++++- 2 files changed, 498 insertions(+), 2 deletions(-) diff --git a/src/repositories/api_key/api_key_in_memory.rs b/src/repositories/api_key/api_key_in_memory.rs index 166478bce..42300993d 100644 --- a/src/repositories/api_key/api_key_in_memory.rs +++ b/src/repositories/api_key/api_key_in_memory.rs @@ -123,4 +123,181 @@ impl ApiKeyRepositoryTrait for InMemoryApiKeyRepository { } #[cfg(test)] -mod tests {} +mod tests { + use chrono::Utc; + use std::sync::Arc; + + use super::*; + + #[tokio::test] + async fn test_in_memory_api_key_repository() { + let api_key_repository = Arc::new(InMemoryApiKeyRepository::new()); + + // Test add and get_by_id + let api_key = ApiKeyModel { + id: "test-api-key".to_string(), + value: "test-value".to_string(), + name: "test-name".to_string(), + allowed_origins: vec!["*".to_string()], + permissions: vec!["relayer:all:execute".to_string()], + created_at: Utc::now().to_string(), + }; + api_key_repository.create(api_key.clone()).await.unwrap(); + assert_eq!( + api_key_repository.get_by_id("test-api-key").await.unwrap(), + Some(api_key) + ); + } + + #[tokio::test] + async fn test_get_nonexistent_api_key() { + let api_key_repository = Arc::new(InMemoryApiKeyRepository::new()); + + let result = api_key_repository.get_by_id("test-api-key").await; + assert!(matches!(result, Ok(None))); + } + + #[tokio::test] + async fn test_get_by_id() { + let api_key_repository = Arc::new(InMemoryApiKeyRepository::new()); + + let api_key = ApiKeyModel { + id: "test-api-key".to_string(), + value: "test-value".to_string(), + name: "test-name".to_string(), + allowed_origins: vec!["*".to_string()], + permissions: vec!["relayer:all:execute".to_string()], + created_at: Utc::now().to_string(), + }; + api_key_repository.create(api_key.clone()).await.unwrap(); + assert_eq!( + api_key_repository.get_by_id("test-api-key").await.unwrap(), + Some(api_key) + ); + } + + #[tokio::test] + async fn test_list_paginated_api_keys() { + let api_key_repository = Arc::new(InMemoryApiKeyRepository::new()); + + let api_key1 = ApiKeyModel { + id: "test-api-key1".to_string(), + value: "test-value1".to_string(), + name: "test-name1".to_string(), + allowed_origins: vec!["*".to_string()], + permissions: vec!["relayer:all:execute".to_string()], + created_at: Utc::now().to_string(), + }; + + let api_key2 = ApiKeyModel { + id: "test-api-key2".to_string(), + value: "test-value2".to_string(), + name: "test-name2".to_string(), + allowed_origins: vec!["*".to_string()], + permissions: vec!["relayer:all:execute".to_string()], + created_at: Utc::now().to_string(), + }; + + api_key_repository.create(api_key1.clone()).await.unwrap(); + api_key_repository.create(api_key2.clone()).await.unwrap(); + + let query = PaginationQuery { + page: 1, + per_page: 2, + }; + + let result = api_key_repository.list_paginated(query).await; + assert!(result.is_ok()); + let result = result.unwrap(); + assert_eq!(result.items.len(), 2); + } + + #[tokio::test] + async fn test_has_entries() { + let api_key_repository = Arc::new(InMemoryApiKeyRepository::new()); + assert!(!api_key_repository.has_entries().await.unwrap()); + api_key_repository + .create(ApiKeyModel { + id: "test-api-key".to_string(), + value: "test-value".to_string(), + name: "test-name".to_string(), + allowed_origins: vec!["*".to_string()], + permissions: vec!["relayer:all:execute".to_string()], + created_at: Utc::now().to_string(), + }) + .await + .unwrap(); + + assert!(api_key_repository.has_entries().await.unwrap()); + api_key_repository.drop_all_entries().await.unwrap(); + assert!(!api_key_repository.has_entries().await.unwrap()); + } + + #[tokio::test] + async fn test_delete_by_id_api_key() { + let api_key_repository = Arc::new(InMemoryApiKeyRepository::new()); + api_key_repository + .create(ApiKeyModel { + id: "test-api-key".to_string(), + value: "test-value".to_string(), + name: "test-name".to_string(), + allowed_origins: vec!["*".to_string()], + permissions: vec!["relayer:all:execute".to_string()], + created_at: Utc::now().to_string(), + }) + .await + .unwrap(); + + assert!(api_key_repository.has_entries().await.unwrap()); + api_key_repository + .delete_by_id("test-api-key") + .await + .unwrap(); + assert!(!api_key_repository.has_entries().await.unwrap()); + } + + #[tokio::test] + async fn test_list_permissions_api_key() { + let api_key_repository = Arc::new(InMemoryApiKeyRepository::new()); + api_key_repository + .create(ApiKeyModel { + id: "test-api-key".to_string(), + value: "test-value".to_string(), + name: "test-name".to_string(), + allowed_origins: vec!["*".to_string()], + permissions: vec![ + "relayer:all:execute".to_string(), + "relayer:all:read".to_string(), + ], + created_at: Utc::now().to_string(), + }) + .await + .unwrap(); + + let permissions = api_key_repository + .list_permissions("test-api-key") + .await + .unwrap(); + assert_eq!(permissions, vec!["relayer:all:execute", "relayer:all:read"]); + } + + #[tokio::test] + async fn test_drop_all_entries() { + let api_key_repository = Arc::new(InMemoryApiKeyRepository::new()); + api_key_repository + .create(ApiKeyModel { + id: "test-api-key".to_string(), + value: "test-value".to_string(), + name: "test-name".to_string(), + allowed_origins: vec!["*".to_string()], + permissions: vec!["relayer:all:execute".to_string()], + created_at: Utc::now().to_string(), + }) + .await + .unwrap(); + + assert!(api_key_repository.has_entries().await.unwrap()); + api_key_repository.drop_all_entries().await.unwrap(); + assert!(!api_key_repository.has_entries().await.unwrap()); + } +} diff --git a/src/repositories/api_key/mod.rs b/src/repositories/api_key/mod.rs index 2b2bde47a..e95c4e028 100644 --- a/src/repositories/api_key/mod.rs +++ b/src/repositories/api_key/mod.rs @@ -143,4 +143,323 @@ impl PartialEq for ApiKeyModel { } #[cfg(test)] -mod tests {} +mod tests { + use super::*; + + use chrono::Utc; + + // Helper function to create a test api key + fn create_test_api_key( + id: &str, + name: &str, + value: &str, + allowed_origins: &[&str], + permissions: &[&str], + ) -> ApiKeyModel { + ApiKeyModel { + id: id.to_string(), + name: name.to_string(), + value: value.to_string(), + allowed_origins: allowed_origins.iter().map(|s| s.to_string()).collect(), + permissions: permissions.iter().map(|s| s.to_string()).collect(), + created_at: Utc::now().to_string(), + } + } + + #[tokio::test] + async fn test_api_key_repository_storage_get_by_id_existing() { + let storage = ApiKeyRepositoryStorage::new_in_memory(); + let api_key = create_test_api_key( + "test-api-key", + "test-name", + "test-value", + &["*"], + &["relayer:all:execute"], + ); + + // Add the api key first + storage.create(api_key.clone()).await.unwrap(); + + // Get the api key + let result = storage.get_by_id("test-api-key").await.unwrap(); + assert_eq!(result, Some(api_key)); + } + + #[tokio::test] + async fn test_api_key_repository_storage_get_by_id_non_existing() { + let storage = ApiKeyRepositoryStorage::new_in_memory(); + + let result = storage.get_by_id("non-existent").await.unwrap(); + assert_eq!(result, None); + } + + #[tokio::test] + async fn test_api_key_repository_storage_add_success() { + let storage = ApiKeyRepositoryStorage::new_in_memory(); + let api_key = create_test_api_key( + "test-api-key", + "test-name", + "test-value", + &["*"], + &["relayer:all:execute"], + ); + + let result = storage.create(api_key).await; + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_api_key_repository_storage_add_duplicate() { + let storage = ApiKeyRepositoryStorage::new_in_memory(); + let api_key = create_test_api_key( + "test-api-key", + "test-name", + "test-value", + &["*"], + &["relayer:all:execute"], + ); + + // Add the api key first time + storage.create(api_key.clone()).await.unwrap(); + + // Try to add the same api key again - should succeed (overwrite) + let result = storage.create(api_key).await; + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_api_key_repository_storage_count_empty() { + let storage = ApiKeyRepositoryStorage::new_in_memory(); + + let count = storage.count().await.unwrap(); + assert_eq!(count, 0); + } + + #[tokio::test] + async fn test_api_key_repository_storage_count_with_api_keys() { + let storage = ApiKeyRepositoryStorage::new_in_memory(); + + // Add multiple plugins + storage + .create(create_test_api_key( + "api-key1", + "test-name1", + "test-value1", + &["*"], + &["relayer:all:execute"], + )) + .await + .unwrap(); + storage + .create(create_test_api_key( + "api-key2", + "test-name2", + "test-value2", + &["*"], + &["relayer:all:execute"], + )) + .await + .unwrap(); + storage + .create(create_test_api_key( + "api-key3", + "test-name3", + "test-value3", + &["*"], + &["relayer:all:execute"], + )) + .await + .unwrap(); + + let count = storage.count().await.unwrap(); + assert_eq!(count, 3); + } + + #[tokio::test] + async fn test_api_key_repository_storage_has_entries_empty() { + let storage = ApiKeyRepositoryStorage::new_in_memory(); + + let has_entries = storage.has_entries().await.unwrap(); + assert!(!has_entries); + } + + #[tokio::test] + async fn test_api_key_repository_storage_has_entries_with_api_keys() { + let storage = ApiKeyRepositoryStorage::new_in_memory(); + + storage + .create(create_test_api_key( + "api-key1", + "test-name1", + "test-value1", + &["*"], + &["relayer:all:execute"], + )) + .await + .unwrap(); + + let has_entries = storage.has_entries().await.unwrap(); + assert!(has_entries); + } + + #[tokio::test] + async fn test_api_key_repository_storage_drop_all_entries_empty() { + let storage = ApiKeyRepositoryStorage::new_in_memory(); + + let result = storage.drop_all_entries().await; + assert!(result.is_ok()); + + let count = storage.count().await.unwrap(); + assert_eq!(count, 0); + } + + #[tokio::test] + async fn test_api_key_repository_storage_drop_all_entries_with_api_keys() { + let storage = ApiKeyRepositoryStorage::new_in_memory(); + + // Add multiple plugins + storage + .create(create_test_api_key( + "api-key1", + "test-name1", + "test-value1", + &["*"], + &["relayer:all:execute"], + )) + .await + .unwrap(); + storage + .create(create_test_api_key( + "api-key2", + "test-name2", + "test-value2", + &["*"], + &["relayer:all:execute"], + )) + .await + .unwrap(); + + let result = storage.drop_all_entries().await; + assert!(result.is_ok()); + + let count = storage.count().await.unwrap(); + assert_eq!(count, 0); + + let has_entries = storage.has_entries().await.unwrap(); + assert!(!has_entries); + } + + #[tokio::test] + async fn test_api_key_repository_storage_list_paginated_empty() { + let storage = ApiKeyRepositoryStorage::new_in_memory(); + + let query = PaginationQuery { + page: 1, + per_page: 10, + }; + let result = storage.list_paginated(query).await.unwrap(); + + assert_eq!(result.items.len(), 0); + assert_eq!(result.total, 0); + assert_eq!(result.page, 1); + assert_eq!(result.per_page, 10); + } + + #[tokio::test] + async fn test_api_key_repository_storage_list_paginated_with_api_keys() { + let storage = ApiKeyRepositoryStorage::new_in_memory(); + + // Add multiple plugins + storage + .create(create_test_api_key( + "api-key1", + "test-name1", + "test-value1", + &["*"], + &["relayer:all:execute"], + )) + .await + .unwrap(); + storage + .create(create_test_api_key( + "api-key2", + "test-name2", + "test-value2", + &["*"], + &["relayer:all:execute"], + )) + .await + .unwrap(); + storage + .create(create_test_api_key( + "api-key3", + "test-name3", + "test-value3", + &["*"], + &["relayer:all:execute"], + )) + .await + .unwrap(); + + let query = PaginationQuery { + page: 1, + per_page: 2, + }; + let result = storage.list_paginated(query).await.unwrap(); + + assert_eq!(result.items.len(), 2); + assert_eq!(result.total, 3); + assert_eq!(result.page, 1); + assert_eq!(result.per_page, 2); + } + + #[tokio::test] + async fn test_api_key_repository_storage_workflow() { + let storage = ApiKeyRepositoryStorage::new_in_memory(); + + // Initially empty + assert!(!storage.has_entries().await.unwrap()); + assert_eq!(storage.count().await.unwrap(), 0); + + // Add plugins + let api_key1 = create_test_api_key( + "api-key1", + "test-name1", + "test-value1", + &["*"], + &["relayer:all:execute"], + ); + let api_key2 = create_test_api_key( + "api-key2", + "test-name2", + "test-value2", + &["*"], + &["relayer:all:execute"], + ); + + storage.create(api_key1.clone()).await.unwrap(); + storage.create(api_key2.clone()).await.unwrap(); + + // Check state + assert!(storage.has_entries().await.unwrap()); + assert_eq!(storage.count().await.unwrap(), 2); + + // Retrieve specific plugin + let retrieved = storage.get_by_id("api-key1").await.unwrap(); + assert_eq!(retrieved, Some(api_key1)); + + // List all plugins + let query = PaginationQuery { + page: 1, + per_page: 10, + }; + let result = storage.list_paginated(query).await.unwrap(); + assert_eq!(result.items.len(), 2); + assert_eq!(result.total, 2); + + // Clear all plugins + storage.drop_all_entries().await.unwrap(); + assert!(!storage.has_entries().await.unwrap()); + assert_eq!(storage.count().await.unwrap(), 0); + } +} From 3d00037ad7ed77c630cafb383e92f03c828e3712 Mon Sep 17 00:00:00 2001 From: Marcos Date: Fri, 25 Jul 2025 11:09:21 -0300 Subject: [PATCH 11/22] test: Add remaining tests --- src/bootstrap/initialize_app_state.rs | 14 ++++++++++++-- src/models/app_state.rs | 10 ++++++++++ src/utils/mocks.rs | 11 +++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/initialize_app_state.rs b/src/bootstrap/initialize_app_state.rs index 31f1a63fd..c4c5f2552 100644 --- a/src/bootstrap/initialize_app_state.rs +++ b/src/bootstrap/initialize_app_state.rs @@ -131,9 +131,10 @@ mod tests { use super::*; use crate::{ config::RepositoryStorageType, - repositories::Repository, + repositories::{ApiKeyRepositoryTrait, Repository}, utils::mocks::mockutils::{ - create_mock_network, create_mock_relayer, create_mock_signer, create_test_server_config, + create_mock_api_key, create_mock_network, create_mock_relayer, create_mock_signer, + create_test_server_config, }, }; use std::sync::Arc; @@ -154,6 +155,7 @@ mod tests { assert!(Arc::strong_count(&repositories.network) >= 1); assert!(Arc::strong_count(&repositories.transaction_counter) >= 1); assert!(Arc::strong_count(&repositories.plugin) >= 1); + assert!(Arc::strong_count(&repositories.api_key) >= 1); } #[tokio::test] @@ -165,11 +167,13 @@ mod tests { let relayer = create_mock_relayer("test-relayer".to_string(), false); let signer = create_mock_signer(); let network = create_mock_network(); + let api_key = create_mock_api_key(); // Test creating and retrieving items repositories.relayer.create(relayer.clone()).await.unwrap(); repositories.signer.create(signer.clone()).await.unwrap(); repositories.network.create(network.clone()).await.unwrap(); + repositories.api_key.create(api_key.clone()).await.unwrap(); let retrieved_relayer = repositories .relayer @@ -186,10 +190,16 @@ mod tests { .get_by_id("test".to_string()) .await .unwrap(); + let retrieved_api_key = repositories + .api_key + .get_by_id("test-api-key") + .await + .unwrap(); assert_eq!(retrieved_relayer.id, "test-relayer"); assert_eq!(retrieved_signer.id, "test"); assert_eq!(retrieved_network.id, "test"); + assert_eq!(retrieved_api_key.unwrap().id, "test-api-key"); } #[tokio::test] diff --git a/src/models/app_state.rs b/src/models/app_state.rs index 46dcb11c0..d122badd6 100644 --- a/src/models/app_state.rs +++ b/src/models/app_state.rs @@ -287,4 +287,14 @@ mod tests { assert!(Arc::ptr_eq(&store1, &store2)); assert!(Arc::ptr_eq(&store1, &app_state.plugin_repository)); } + + #[test] + fn test_api_key_repository_getter() { + let app_state = create_test_app_state(); + let repo1 = app_state.api_key_repository(); + let repo2 = app_state.api_key_repository(); + + assert!(Arc::ptr_eq(&repo1, &repo2)); + assert!(Arc::ptr_eq(&repo1, &app_state.api_key_repository)); + } } diff --git a/src/utils/mocks.rs b/src/utils/mocks.rs index 35ce0473b..30f8e6bda 100644 --- a/src/utils/mocks.rs +++ b/src/utils/mocks.rs @@ -217,6 +217,17 @@ pub mod mockutils { } } + pub fn create_mock_api_key() -> ApiKeyModel { + ApiKeyModel { + id: "test-api-key".to_string(), + name: "test-name".to_string(), + value: "test-value".to_string(), + allowed_origins: vec!["*".to_string()], + permissions: vec!["relayer:all:execute".to_string()], + created_at: Utc::now().to_string(), + } + } + pub fn create_test_server_config(storage_type: RepositoryStorageType) -> ServerConfig { ServerConfig { host: "localhost".to_string(), From fc3205c2ab7a7e22443c0a8f2292db00b8bfd20c Mon Sep 17 00:00:00 2001 From: Marcos Date: Mon, 25 Aug 2025 15:11:28 -0300 Subject: [PATCH 12/22] chore: Update changelog --- CHANGELOG.md | 6 +- .../launchtube/pnpm-lock.yaml | 847 +----------------- 2 files changed, 20 insertions(+), 833 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b08bea644..ef7ae5af6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ * add base models ([#5](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/5)) ([55db42b](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/55db42b16d88e95ca8f6927e3b4d07c939e677c8)) * Add CLA assistant bot ([#130](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/130)) ([4ad5733](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/4ad5733daadefe5e52bd617eaa47039677443745)) * add directory structure and example ([d946c10](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/d946c10fd96ee2d1ce2e373ba4ccfced31f985f9)) -* add evm intristic gas_limit validation ([dd1b2d6](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/dd1b2d6768d09f051791d0db68c912a38d273715)) +* add evm intrinsic gas_limit validation ([dd1b2d6](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/dd1b2d6768d09f051791d0db68c912a38d273715)) * Add get_status method for EVM and Stellar ([#229](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/229)) ([e84217e](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/e84217e0fa941fcd580ad6b84ab6bfac939dd5f4)) * Add Launctube plugin example ([#414](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/414)) ([5bda763](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/5bda7635f304923fcd4031f855009228eeefee4b)) * Add logging improvements ([#28](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/28)) ([bb6751a](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/bb6751a4f868eb82787e7763a7995d3974ecfd49)) @@ -52,7 +52,7 @@ * initial repo setup ([d8815b6](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/d8815b6752931003536aa427370ca8fb1c57231c)) * Integrate Netlify with antora ([#74](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/74)) ([09e3d48](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/09e3d4894b54c58754b373da239e9d564df69aa9)) * Local signing for stellar ([#178](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/178)) ([f69270a](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/f69270ade4c9a9239bba874ac74858c8e7375298)) -* Pass arbitrary payloads to script exectution ([#312](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/312)) ([adecaf5](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/adecaf5d73c3df9083c6a3fcf62ed669bc90b25c)) +* Pass arbitrary payloads to script execution ([#312](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/312)) ([adecaf5](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/adecaf5d73c3df9083c6a3fcf62ed669bc90b25c)) * Plat 5744 implement an api key authentication mechanism ([#11](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/11)) ([8891887](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/88918872d51ab10632ec6d590689d52e59dfd640)) * Plat 5768 setup metrics endpoint ([#50](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/50)) ([7c292a5](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/7c292a572a7aef8213969fc72cadca74f9016fe8)) * Plat 6434 improve authorization header validation ([#122](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/122)) ([eed7c31](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/eed7c31e938c7b6ecaa82774ca5d3a508bb89281)) @@ -162,7 +162,7 @@ * Plat 6286 write tests for metrics and middleware functions ([#70](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/70)) ([18124fb](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/18124fbbfbc26f300648a7a4050ebf9be72465ac)) * PLAT-6426 Increase test coverage ([#118](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/118)) ([1fa41f0](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/1fa41f0f225c9d515690738e960073396dce66ce)) * PLAT-6478 create unit test for use of on relayers dotenv ([#139](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/139)) ([509e166](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/509e1664518823ef3844e52e818707f3371ddbff)) -* plat-6480 allow transfering wrapped sol tokens ([#132](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/132)) ([f04e66a](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/f04e66a568c877c2a4c5c5378fb6017c2e41d2c6)) +* plat-6480 allow transferring wrapped sol tokens ([#132](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/132)) ([f04e66a](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/f04e66a568c877c2a4c5c5378fb6017c2e41d2c6)) * Plat-6815 resubmission bug ([#353](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/353)) ([72ac174](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/72ac17471e3a0a6ac35e9a9bb9ff8fe5e8b94bf2)) * plat-6888 aws kms signer issue ([#411](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/411)) ([3c12c88](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/3c12c88703c92526fe975eabba6ba0ffa9ca9c79)) * Plugin result + adds tests for plugin ts lib ([#336](https://github.com/OpenZeppelin/openzeppelin-relayer/issues/336)) ([b30246e](https://github.com/OpenZeppelin/openzeppelin-relayer/commit/b30246e8922d3cb5bd3c5b92a7678f7591db5b97)) diff --git a/examples/launchtube-plugin-example/launchtube/pnpm-lock.yaml b/examples/launchtube-plugin-example/launchtube/pnpm-lock.yaml index 1aa01a5a8..12c847a47 100644 --- a/examples/launchtube-plugin-example/launchtube/pnpm-lock.yaml +++ b/examples/launchtube-plugin-example/launchtube/pnpm-lock.yaml @@ -1,11 +1,9 @@ +--- lockfileVersion: '9.0' - settings: autoInstallPeers: true excludeLinksFromLockfile: false - importers: - .: dependencies: '@openzeppelin/relayer-plugin-launchtube': @@ -36,200 +34,156 @@ importers: typescript: specifier: ^5.3.3 version: 5.9.2 - packages: - '@actions/exec@1.1.1': resolution: {integrity: sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==} - '@actions/io@1.1.3': resolution: {integrity: sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==} - '@ampproject/remapping@2.3.0': resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@babel/code-frame@7.27.1': resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.28.0': resolution: {integrity: sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==} engines: {node: '>=6.9.0'} - '@babel/core@7.28.0': resolution: {integrity: sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==} engines: {node: '>=6.9.0'} - '@babel/generator@7.28.0': resolution: {integrity: sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.27.2': resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} engines: {node: '>=6.9.0'} - '@babel/helper-globals@7.28.0': resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.27.1': resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} engines: {node: '>=6.9.0'} - '@babel/helper-module-transforms@7.27.3': resolution: {integrity: sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-plugin-utils@7.27.1': resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.27.1': resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.27.1': resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.27.1': resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.28.2': resolution: {integrity: sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==} engines: {node: '>=6.9.0'} - '@babel/parser@7.28.0': resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/plugin-syntax-async-generators@7.8.4': resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-bigint@7.8.3': resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-class-properties@7.12.13': resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-class-static-block@7.14.5': resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-import-attributes@7.27.1': resolution: {integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-import-meta@7.10.4': resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-json-strings@7.8.3': resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-jsx@7.27.1': resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-logical-assignment-operators@7.10.4': resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-numeric-separator@7.10.4': resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-object-rest-spread@7.8.3': resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-optional-catch-binding@7.8.3': resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-optional-chaining@7.8.3': resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-private-property-in-object@7.14.5': resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-top-level-await@7.14.5': resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-typescript@7.27.1': resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/runtime@7.28.2': resolution: {integrity: sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==} engines: {node: '>=6.9.0'} - '@babel/template@7.27.2': resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.28.0': resolution: {integrity: sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==} engines: {node: '>=6.9.0'} - '@babel/types@7.28.2': resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==} engines: {node: '>=6.9.0'} - '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - '@istanbuljs/load-nyc-config@1.1.0': resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} engines: {node: '>=8'} - '@istanbuljs/schema@0.1.3': resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} - '@jest/console@29.7.0': resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/core@29.7.0': resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -238,27 +192,21 @@ packages: peerDependenciesMeta: node-notifier: optional: true - '@jest/environment@29.7.0': resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/expect-utils@29.7.0': resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/expect@29.7.0': resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/fake-timers@29.7.0': resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/globals@29.7.0': resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/reporters@29.7.0': resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -267,292 +215,218 @@ packages: peerDependenciesMeta: node-notifier: optional: true - '@jest/schemas@29.6.3': resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/source-map@29.6.3': resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/test-result@29.7.0': resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/test-sequencer@29.7.0': resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/transform@29.7.0': resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jest/types@29.6.3': resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jridgewell/gen-mapping@0.3.12': resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==} - '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/sourcemap-codec@1.5.4': resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==} - '@jridgewell/trace-mapping@0.3.29': resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==} - '@noble/curves@1.9.6': resolution: {integrity: sha512-GIKz/j99FRthB8icyJQA51E8Uk5hXmdyThjgQXRKiv9h0zeRlzSCLIzFw6K1LotZ3XuB7yzlf76qk7uBmTdFqA==} engines: {node: ^14.21.3 || >=16} - '@noble/hashes@1.8.0': resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} engines: {node: ^14.21.3 || >=16} - '@openzeppelin/relayer-plugin-launchtube@0.2.0': resolution: {integrity: sha512-nhD5ZHDngjeoqdBArzKR1nzbK1R4Gv49fY+cuF3l+QrZ1IVX6PciBqUFenC5kCaKnyBqvMMJx+XDIBFqY7/bgg==} engines: {node: '>=22.18.0', npm: use pnpm, pnpm: '>=9', yarn: use pnpm} - '@openzeppelin/relayer-sdk@1.2.1': resolution: {integrity: sha512-Qbz+/Gspln4hAJ/ANOukqDgOQ7tkD52PBYr/L3AsNFvt0fQh9jjTg7ncQxHDe7sglz9VVhlKOgAMDQ8qjlBSEQ==} engines: {node: 22.14.0, npm: use pnpm, pnpm: '>=9', yarn: use pnpm} - '@openzeppelin/relayer-sdk@1.4.0': resolution: {integrity: sha512-TlPv/Gx7o9jH406C1/TGEe5k8kY4fpJ6Btn7y332d82RDKY+2AAByDAY8OrLFF+R+2zfo0b2SwhmuBh6ZzHjYQ==} engines: {node: 22.14.0, npm: use pnpm, pnpm: '>=9', yarn: use pnpm} - '@sinclair/typebox@0.27.8': resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} - '@sinonjs/commons@3.0.1': resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} - '@sinonjs/fake-timers@10.3.0': resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} - '@solana/buffer-layout-utils@0.2.0': resolution: {integrity: sha512-szG4sxgJGktbuZYDg2FfNmkMi0DYQoVjN2h7ta1W1hPrwzarcFLBq9UpX1UjNXsNpT9dn+chgprtWGioUAr4/g==} engines: {node: '>= 10'} - '@solana/buffer-layout@4.0.1': resolution: {integrity: sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==} engines: {node: '>=5.10'} - '@solana/codecs-core@2.0.0-rc.1': resolution: {integrity: sha512-bauxqMfSs8EHD0JKESaNmNuNvkvHSuN3bbWAF5RjOfDu2PugxHrvRebmYauvSumZ3cTfQ4HJJX6PG5rN852qyQ==} peerDependencies: typescript: '>=5' - '@solana/codecs-core@2.3.0': resolution: {integrity: sha512-oG+VZzN6YhBHIoSKgS5ESM9VIGzhWjEHEGNPSibiDTxFhsFWxNaz8LbMDPjBUE69r9wmdGLkrQ+wVPbnJcZPvw==} engines: {node: '>=20.18.0'} peerDependencies: typescript: '>=5.3.3' - '@solana/codecs-data-structures@2.0.0-rc.1': resolution: {integrity: sha512-rinCv0RrAVJ9rE/rmaibWJQxMwC5lSaORSZuwjopSUE6T0nb/MVg6Z1siNCXhh/HFTOg0l8bNvZHgBcN/yvXog==} peerDependencies: typescript: '>=5' - '@solana/codecs-numbers@2.0.0-rc.1': resolution: {integrity: sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ==} peerDependencies: typescript: '>=5' - '@solana/codecs-numbers@2.3.0': resolution: {integrity: sha512-jFvvwKJKffvG7Iz9dmN51OGB7JBcy2CJ6Xf3NqD/VP90xak66m/Lg48T01u5IQ/hc15mChVHiBm+HHuOFDUrQg==} engines: {node: '>=20.18.0'} peerDependencies: typescript: '>=5.3.3' - '@solana/codecs-strings@2.0.0-rc.1': resolution: {integrity: sha512-9/wPhw8TbGRTt6mHC4Zz1RqOnuPTqq1Nb4EyuvpZ39GW6O2t2Q7Q0XxiB3+BdoEjwA2XgPw6e2iRfvYgqty44g==} peerDependencies: fastestsmallesttextencoderdecoder: ^1.0.22 typescript: '>=5' - '@solana/codecs@2.0.0-rc.1': resolution: {integrity: sha512-qxoR7VybNJixV51L0G1RD2boZTcxmwUWnKCaJJExQ5qNKwbpSyDdWfFJfM5JhGyKe9DnPVOZB+JHWXnpbZBqrQ==} peerDependencies: typescript: '>=5' - '@solana/errors@2.0.0-rc.1': resolution: {integrity: sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ==} hasBin: true peerDependencies: typescript: '>=5' - '@solana/errors@2.3.0': resolution: {integrity: sha512-66RI9MAbwYV0UtP7kGcTBVLxJgUxoZGm8Fbc0ah+lGiAw17Gugco6+9GrJCV83VyF2mDWyYnYM9qdI3yjgpnaQ==} engines: {node: '>=20.18.0'} hasBin: true peerDependencies: typescript: '>=5.3.3' - '@solana/options@2.0.0-rc.1': resolution: {integrity: sha512-mLUcR9mZ3qfHlmMnREdIFPf9dpMc/Bl66tLSOOWxw4ml5xMT2ohFn7WGqoKcu/UHkT9CrC6+amEdqCNvUqI7AA==} peerDependencies: typescript: '>=5' - '@solana/spl-token-group@0.0.7': resolution: {integrity: sha512-V1N/iX7Cr7H0uazWUT2uk27TMqlqedpXHRqqAbVO2gvmJyT0E0ummMEAVQeXZ05ZhQ/xF39DLSdBp90XebWEug==} engines: {node: '>=16'} peerDependencies: '@solana/web3.js': ^1.95.3 - '@solana/spl-token-metadata@0.1.6': resolution: {integrity: sha512-7sMt1rsm/zQOQcUWllQX9mD2O6KhSAtY1hFR2hfFwgqfFWzSY9E9GDvFVNYUI1F0iQKcm6HmePU9QbKRXTEBiA==} engines: {node: '>=16'} peerDependencies: '@solana/web3.js': ^1.95.3 - '@solana/spl-token@0.4.13': resolution: {integrity: sha512-cite/pYWQZZVvLbg5lsodSovbetK/eA24gaR0eeUeMuBAMNrT8XFCwaygKy0N2WSg3gSyjjNpIeAGBAKZaY/1w==} engines: {node: '>=16'} peerDependencies: '@solana/web3.js': ^1.95.5 - '@solana/web3.js@1.98.4': resolution: {integrity: sha512-vv9lfnvjUsRiq//+j5pBdXig0IQdtzA0BRZ3bXEP4KaIyF1CcaydWqgyzQgfZMNIsWNWmG+AUHwPy4AHOD6gpw==} - '@stellar/js-xdr@3.1.2': resolution: {integrity: sha512-VVolPL5goVEIsvuGqDc5uiKxV03lzfWdvYg1KikvwheDmTBO68CKDji3bAZ/kppZrx5iTA8z3Ld5yuytcvhvOQ==} - '@stellar/stellar-base@11.0.1': resolution: {integrity: sha512-VQh+1KEtFjegD6spx08+lENt8tQOkQQQZoLtqExjpRXyWlqDhEe+bXMlBTYKDc5MIynHyD42RPEib27UG17trA==} - '@stellar/stellar-sdk@11.3.0': resolution: {integrity: sha512-i+heopibJNRA7iM8rEPz0AXphBPYvy2HDo8rxbDwWpozwCfw8kglP9cLkkhgJe8YicgLrdExz/iQZaLpqLC+6w==} - '@swc/helpers@0.5.17': resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==} - '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} - '@types/babel__generator@7.27.0': resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} - '@types/babel__template@7.4.4': resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} - '@types/babel__traverse@7.28.0': resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} - '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} - '@types/graceful-fs@4.1.9': resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} - '@types/istanbul-lib-coverage@2.0.6': resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} - '@types/istanbul-lib-report@3.0.3': resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} - '@types/istanbul-reports@3.0.4': resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} - '@types/jest@29.5.14': resolution: {integrity: sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==} - '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - '@types/node@24.2.0': resolution: {integrity: sha512-3xyG3pMCq3oYCNg7/ZP+E1ooTaGB4cG8JWRsqqOYQdbWNY4zbaV0Ennrd7stjiJEFZCaybcIgpTjJWHRfBSIDw==} - '@types/stack-utils@2.0.3': resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} - '@types/uuid@10.0.0': resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==} - '@types/uuid@8.3.4': resolution: {integrity: sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==} - '@types/ws@7.4.7': resolution: {integrity: sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==} - '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} - '@types/yargs-parser@21.0.3': resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} - '@types/yargs@17.0.33': resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} - agentkeepalive@4.6.0: resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} engines: {node: '>= 8.0.0'} - ansi-escapes@4.3.2: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} - ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@5.2.0: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} - anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} - argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - axios@1.11.0: resolution: {integrity: sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==} - babel-jest@29.7.0: resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: '@babel/core': ^7.8.0 - babel-plugin-istanbul@6.1.1: resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} engines: {node: '>=8'} - babel-plugin-jest-hoist@29.6.3: resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - babel-preset-current-node-syntax@1.2.0: resolution: {integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==} peerDependencies: '@babel/core': ^7.0.0 || ^8.0.0-0 - babel-preset-jest@29.6.3: resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: '@babel/core': ^7.0.0 - balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - bare-addon-resolve@1.9.4: resolution: {integrity: sha512-unn6Vy/Yke6F99vg/7tcrvM2KUvIhTNniaSqDbam4AWkd4NhvDVSrQiRYVlNzUV2P7SPobkCK7JFVxrJk9btCg==} peerDependencies: @@ -560,7 +434,6 @@ packages: peerDependenciesMeta: bare-url: optional: true - bare-module-resolve@1.11.1: resolution: {integrity: sha512-DCxeT9i8sTs3vUMA3w321OX/oXtNEu5EjObQOnTmCdNp5RXHBAvAaBDHvAi9ta0q/948QPz+co6SsGi6aQMYRg==} peerDependencies: @@ -568,172 +441,125 @@ packages: peerDependenciesMeta: bare-url: optional: true - bare-os@3.6.1: resolution: {integrity: sha512-uaIjxokhFidJP+bmmvKSgiMzj2sV5GPHaZVAIktcxcpCyBFFWO+YlikVAdhmUo2vYFvFhOXIAlldqV29L8126g==} engines: {bare: '>=1.14.0'} - bare-path@3.0.0: resolution: {integrity: sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==} - bare-semver@1.0.1: resolution: {integrity: sha512-UtggzHLiTrmFOC/ogQ+Hy7VfoKoIwrP1UFcYtTxoCUdLtsIErT8+SWtOC2DH/snT9h+xDrcBEPcwKei1mzemgg==} - bare-url@2.2.1: resolution: {integrity: sha512-5Ms88Fgq8eMQqwxet7fqGJoOwFdj8k+NDYCjEkL2OdS/WCeeo7j5TsZCDGkE8VrAd4ss8uQUC1u1d5khpujZEw==} - base-x@3.0.11: resolution: {integrity: sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==} - base32.js@0.1.0: resolution: {integrity: sha512-n3TkB02ixgBOhTvANakDb4xaMXnYUVkNoRFJjQflcqMQhyEKxEHdj3E6N8t8sUQ0mjH/3/JxzlXuz3ul/J90pQ==} engines: {node: '>=0.12.0'} - base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - bigint-buffer@1.1.5: resolution: {integrity: sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA==} engines: {node: '>= 10.0.0'} - bignumber.js@9.3.1: resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} - bindings@1.5.0: resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} - bn.js@5.2.2: resolution: {integrity: sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==} - borsh@0.7.0: resolution: {integrity: sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==} - brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} - braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.25.1: resolution: {integrity: sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - bs-logger@0.2.6: resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} engines: {node: '>= 6'} - bs58@4.0.1: resolution: {integrity: sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==} - bser@2.1.1: resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} - buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - bufferutil@4.0.9: resolution: {integrity: sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==} engines: {node: '>=6.14.2'} - call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} - call-bind@1.0.8: resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} engines: {node: '>= 0.4'} - call-bound@1.0.4: resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} - callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - camelcase@5.3.1: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} engines: {node: '>=6'} - camelcase@6.3.0: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001731: resolution: {integrity: sha512-lDdp2/wrOmTRWuoB5DpfNkC0rJDU8DqRa6nYL6HK6sytw70QMopt/NIc/9SM7ylItlBWfACXk0tEn37UWM/+mg==} - chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - chalk@5.5.0: resolution: {integrity: sha512-1tm8DTaJhPBG3bIkVeZt1iZM9GfSX2lzOeDVZH9R9ffRHpmHvxZ/QhgQH/aDTkswQVt+YHdXAdS/In/30OjCbg==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - char-regex@1.0.2: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} - ci-info@3.9.0: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} - cjs-module-lexer@1.4.3: resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} - cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} - co@4.6.0: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} - collect-v8-coverage@1.0.2: resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} - color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} - color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} - commander@12.1.0: resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} engines: {node: '>=18'} - commander@14.0.0: resolution: {integrity: sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==} engines: {node: '>=20'} - commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - create-jest@29.7.0: resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true - cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} - debug@4.4.1: resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} engines: {node: '>=6.0'} @@ -742,7 +568,6 @@ packages: peerDependenciesMeta: supports-color: optional: true - dedent@1.6.0: resolution: {integrity: sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==} peerDependencies: @@ -750,129 +575,95 @@ packages: peerDependenciesMeta: babel-plugin-macros: optional: true - deepmerge@4.3.1: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} - define-data-property@1.1.4: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} - delay@5.0.0: resolution: {integrity: sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==} engines: {node: '>=10'} - delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} - detect-newline@3.1.0: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} - diff-sequences@29.6.3: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} - electron-to-chromium@1.5.194: resolution: {integrity: sha512-SdnWJwSUot04UR51I2oPD8kuP2VI37/CADR1OHsFOUzZIvfWJBO6q11k5P/uKNyTT3cdOsnyjkrZ+DDShqYqJA==} - emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} engines: {node: '>=12'} - emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} - es-errors@1.3.0: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} - es-set-tostringtag@2.1.0: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} - es6-promise@4.2.8: resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==} - es6-promisify@5.0.0: resolution: {integrity: sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==} - escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} - escape-string-regexp@2.0.0: resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} engines: {node: '>=8'} - esprima@4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} engines: {node: '>=4'} hasBin: true - eventemitter3@5.0.1: resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} - eventsource@2.0.2: resolution: {integrity: sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==} engines: {node: '>=12.0.0'} - execa@5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} - exit@0.1.2: resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} engines: {node: '>= 0.8.0'} - expect@29.7.0: resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - eyes@0.1.8: resolution: {integrity: sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==} engines: {node: '> 0.1.90'} - fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - fast-stable-stringify@1.0.0: resolution: {integrity: sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==} - fastestsmallesttextencoderdecoder@1.0.22: resolution: {integrity: sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==} - fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} - file-uri-to-path@1.0.0: resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} - fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} - find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} - follow-redirects@1.15.11: resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} engines: {node: '>=4.0'} @@ -881,198 +672,149 @@ packages: peerDependenciesMeta: debug: optional: true - for-each@0.3.5: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} - form-data@4.0.4: resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} engines: {node: '>= 6'} - fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - + os: + - darwin function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} - get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} - get-package-type@0.1.0: resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} engines: {node: '>=8.0.0'} - get-proto@1.0.1: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} - get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} - glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Glob versions prior to v9 are no longer supported - gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} - graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - handlebars@4.7.8: resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} engines: {node: '>=0.4.7'} hasBin: true - has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - has-property-descriptors@1.0.2: resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - has-symbols@1.1.0: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} - has-tostringtag@1.0.2: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} - hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} - html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} - human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} - humanize-ms@1.2.1: resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} - husky@9.1.7: resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} engines: {node: '>=18'} hasBin: true - ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - import-local@3.2.0: resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} engines: {node: '>=8'} hasBin: true - imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} - inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. - inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - is-callable@1.2.7: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} - is-core-module@2.16.1: resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} engines: {node: '>= 0.4'} - is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - is-generator-fn@2.1.0: resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} engines: {node: '>=6'} - is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} - is-typed-array@1.1.15: resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} engines: {node: '>= 0.4'} - isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - isomorphic-ws@4.0.1: resolution: {integrity: sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==} peerDependencies: ws: '*' - istanbul-lib-coverage@3.2.2: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} - istanbul-lib-instrument@5.2.1: resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} engines: {node: '>=8'} - istanbul-lib-instrument@6.0.3: resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} engines: {node: '>=10'} - istanbul-lib-report@3.0.1: resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} engines: {node: '>=10'} - istanbul-lib-source-maps@4.0.1: resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} engines: {node: '>=10'} - istanbul-reports@3.1.7: resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} engines: {node: '>=8'} - jayson@4.2.0: resolution: {integrity: sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg==} engines: {node: '>=8'} hasBin: true - jest-changed-files@29.7.0: resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-circus@29.7.0: resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-cli@29.7.0: resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1082,7 +824,6 @@ packages: peerDependenciesMeta: node-notifier: optional: true - jest-config@29.7.0: resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1094,47 +835,36 @@ packages: optional: true ts-node: optional: true - jest-diff@29.7.0: resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-docblock@29.7.0: resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-each@29.7.0: resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-environment-node@29.7.0: resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-get-type@29.6.3: resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-haste-map@29.7.0: resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-leak-detector@29.7.0: resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-matcher-utils@29.7.0: resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-message-util@29.7.0: resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-mock@29.7.0: resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-pnp-resolver@1.2.3: resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} engines: {node: '>=6'} @@ -1143,47 +873,36 @@ packages: peerDependenciesMeta: jest-resolve: optional: true - jest-regex-util@29.6.3: resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-resolve-dependencies@29.7.0: resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-resolve@29.7.0: resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-runner@29.7.0: resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-runtime@29.7.0: resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-snapshot@29.7.0: resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-util@29.7.0: resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-validate@29.7.0: resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-watcher@29.7.0: resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest-worker@29.7.0: resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - jest@29.7.0: resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1193,99 +912,72 @@ packages: peerDependenciesMeta: node-notifier: optional: true - js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@3.14.1: resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} hasBin: true - jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} hasBin: true - json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - json-stringify-safe@5.0.1: resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} - json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} hasBin: true - kleur@3.0.3: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} - leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} - lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} - lodash.memoize@4.1.2: resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} - lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} - make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - makeerror@1.0.12: resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} - math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} - merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - micromatch@4.0.8: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} - mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} - mime-types@2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} - mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} - minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} - node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} @@ -1294,260 +986,191 @@ packages: peerDependenciesMeta: encoding: optional: true - node-gyp-build@4.8.4: resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} hasBin: true - node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - node-releases@2.0.19: resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} - normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - npm-run-path@4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} - once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} - p-limit@2.3.0: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} engines: {node: '>=6'} - p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} - p-locate@4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} - p-try@2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} - parse-json@5.2.0: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} - path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} - path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} - path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - pirates@4.0.7: resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} - pkg-dir@4.2.0: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} - possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} - pretty-format@29.7.0: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - prompts@2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} - proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - pure-rand@6.1.0: resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} - randombytes@2.1.0: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} - react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - require-addon@1.1.0: resolution: {integrity: sha512-KbXAD5q2+v1GJnkzd8zzbOxchTkStSyJZ9QwoCq3QwEXAaIlG3wDYRZGzVD357jmwaGY7hr5VaoEAL0BkF0Kvg==} engines: {bare: '>=1.10.0'} - require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} - resolve-cwd@3.0.0: resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} engines: {node: '>=8'} - resolve-from@5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} - resolve.exports@2.0.3: resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} engines: {node: '>=10'} - resolve@1.22.10: resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} engines: {node: '>= 0.4'} hasBin: true - rpc-websockets@9.1.3: resolution: {integrity: sha512-I+kNjW0udB4Fetr3vvtRuYZJS0PcSPyyvBcH5sDdoV8DFs5E4W2pTr7aiMlKfPxANTClP9RlqCPolj9dd5MsEA==} - safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.7.2: resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} engines: {node: '>=10'} hasBin: true - set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} - sha.js@2.4.12: resolution: {integrity: sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==} engines: {node: '>= 0.10'} hasBin: true - shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} - shebang-regex@3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} - sodium-native@4.3.3: resolution: {integrity: sha512-OnxSlN3uyY8D0EsLHpmm2HOFmKddQVvEMmsakCrXUzSd8kjjbzL413t4ZNF3n0UxSwNgwTyUvkmZHTfuCeiYSw==} - source-map-support@0.5.13: resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} - source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} - sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - stack-utils@2.0.6: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} - stream-chain@2.2.5: resolution: {integrity: sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==} - stream-json@1.9.1: resolution: {integrity: sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw==} - string-length@4.0.2: resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} engines: {node: '>=10'} - string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} - strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} - strip-bom@4.0.0: resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} engines: {node: '>=8'} - strip-final-newline@2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} - strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - superstruct@2.0.2: resolution: {integrity: sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==} engines: {node: '>=14.0.0'} - supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} - supports-color@8.1.1: resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} engines: {node: '>=10'} - supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - test-exclude@6.0.0: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} - text-encoding-utf-8@1.0.2: resolution: {integrity: sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==} - tmpl@1.0.5: resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} - to-buffer@1.2.1: resolution: {integrity: sha512-tB82LpAIWjhLYbqjx3X4zEeHN6M8CiuOEy2JY8SEQVdYRe3CCHOFaqrBW1doLDrfpWhplcW7BL+bO3/6S3pcDQ==} engines: {node: '>= 0.4'} - to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - toml@3.0.0: resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==} - tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - ts-jest@29.4.1: resolution: {integrity: sha512-SaeUtjfpg9Uqu8IbeDKtdaS0g8lS6FT6OzM3ezrDfErPJPHNDo/Ey+VFGP1bQIDfagYDLyRpd7O15XpG1Es2Uw==} engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} @@ -1574,95 +1197,71 @@ packages: optional: true jest-util: optional: true - tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tweetnacl@1.0.3: resolution: {integrity: sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==} - type-detect@4.0.8: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} - type-fest@0.21.3: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} - type-fest@4.41.0: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} - typed-array-buffer@1.0.3: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} - typescript@5.9.2: resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} engines: {node: '>=14.17'} hasBin: true - uglify-js@3.19.3: resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} engines: {node: '>=0.8.0'} hasBin: true - undici-types@7.10.0: resolution: {integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==} - update-browserslist-db@1.1.3: resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' - urijs@1.19.11: resolution: {integrity: sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==} - utf-8-validate@5.0.10: resolution: {integrity: sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==} engines: {node: '>=6.14.2'} - uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true - v8-to-istanbul@9.3.0: resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} engines: {node: '>=10.12.0'} - walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} - webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - which-typed-array@1.1.19: resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} engines: {node: '>= 0.4'} - which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} hasBin: true - wordwrap@1.0.0: resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} - wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} - wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - write-file-atomic@4.0.2: resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - ws@7.5.10: resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} engines: {node: '>=8.3.0'} @@ -1674,7 +1273,6 @@ packages: optional: true utf-8-validate: optional: true - ws@8.18.3: resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} engines: {node: '>=10.0.0'} @@ -1686,47 +1284,35 @@ packages: optional: true utf-8-validate: optional: true - y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} - yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} - yargs@17.7.2: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} - yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - snapshots: - '@actions/exec@1.1.1': dependencies: '@actions/io': 1.1.3 - '@actions/io@1.1.3': {} - '@ampproject/remapping@2.3.0': dependencies: '@jridgewell/gen-mapping': 0.3.12 '@jridgewell/trace-mapping': 0.3.29 - '@babel/code-frame@7.27.1': dependencies: '@babel/helper-validator-identifier': 7.27.1 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.28.0': {} - '@babel/core@7.28.0': dependencies: '@ampproject/remapping': 2.3.0 @@ -1746,7 +1332,6 @@ snapshots: semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/generator@7.28.0': dependencies: '@babel/parser': 7.28.0 @@ -1754,7 +1339,6 @@ snapshots: '@jridgewell/gen-mapping': 0.3.12 '@jridgewell/trace-mapping': 0.3.29 jsesc: 3.1.0 - '@babel/helper-compilation-targets@7.27.2': dependencies: '@babel/compat-data': 7.28.0 @@ -1762,16 +1346,13 @@ snapshots: browserslist: 4.25.1 lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-globals@7.28.0': {} - '@babel/helper-module-imports@7.27.1': dependencies: '@babel/traverse': 7.28.0 '@babel/types': 7.28.2 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.27.3(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 @@ -1780,117 +1361,91 @@ snapshots: '@babel/traverse': 7.28.0 transitivePeerDependencies: - supports-color - '@babel/helper-plugin-utils@7.27.1': {} - '@babel/helper-string-parser@7.27.1': {} - '@babel/helper-validator-identifier@7.27.1': {} - '@babel/helper-validator-option@7.27.1': {} - '@babel/helpers@7.28.2': dependencies: '@babel/template': 7.27.2 '@babel/types': 7.28.2 - '@babel/parser@7.28.0': dependencies: '@babel/types': 7.28.2 - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/runtime@7.28.2': {} - '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 '@babel/parser': 7.28.0 '@babel/types': 7.28.2 - '@babel/traverse@7.28.0': dependencies: '@babel/code-frame': 7.27.1 @@ -1902,14 +1457,11 @@ snapshots: debug: 4.4.1 transitivePeerDependencies: - supports-color - '@babel/types@7.28.2': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@bcoe/v8-coverage@0.2.3': {} - '@istanbuljs/load-nyc-config@1.1.0': dependencies: camelcase: 5.3.1 @@ -1917,9 +1469,7 @@ snapshots: get-package-type: 0.1.0 js-yaml: 3.14.1 resolve-from: 5.0.0 - '@istanbuljs/schema@0.1.3': {} - '@jest/console@29.7.0': dependencies: '@jest/types': 29.6.3 @@ -1928,7 +1478,6 @@ snapshots: jest-message-util: 29.7.0 jest-util: 29.7.0 slash: 3.0.0 - '@jest/core@29.7.0': dependencies: '@jest/console': 29.7.0 @@ -1963,25 +1512,21 @@ snapshots: - babel-plugin-macros - supports-color - ts-node - '@jest/environment@29.7.0': dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 '@types/node': 24.2.0 jest-mock: 29.7.0 - '@jest/expect-utils@29.7.0': dependencies: jest-get-type: 29.6.3 - '@jest/expect@29.7.0': dependencies: expect: 29.7.0 jest-snapshot: 29.7.0 transitivePeerDependencies: - supports-color - '@jest/fake-timers@29.7.0': dependencies: '@jest/types': 29.6.3 @@ -1990,7 +1535,6 @@ snapshots: jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 - '@jest/globals@29.7.0': dependencies: '@jest/environment': 29.7.0 @@ -1999,7 +1543,6 @@ snapshots: jest-mock: 29.7.0 transitivePeerDependencies: - supports-color - '@jest/reporters@29.7.0': dependencies: '@bcoe/v8-coverage': 0.2.3 @@ -2028,31 +1571,26 @@ snapshots: v8-to-istanbul: 9.3.0 transitivePeerDependencies: - supports-color - '@jest/schemas@29.6.3': dependencies: '@sinclair/typebox': 0.27.8 - '@jest/source-map@29.6.3': dependencies: '@jridgewell/trace-mapping': 0.3.29 callsites: 3.1.0 graceful-fs: 4.2.11 - '@jest/test-result@29.7.0': dependencies: '@jest/console': 29.7.0 '@jest/types': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 collect-v8-coverage: 1.0.2 - '@jest/test-sequencer@29.7.0': dependencies: '@jest/test-result': 29.7.0 graceful-fs: 4.2.11 jest-haste-map: 29.7.0 slash: 3.0.0 - '@jest/transform@29.7.0': dependencies: '@babel/core': 7.28.0 @@ -2072,7 +1610,6 @@ snapshots: write-file-atomic: 4.0.2 transitivePeerDependencies: - supports-color - '@jest/types@29.6.3': dependencies: '@jest/schemas': 29.6.3 @@ -2081,29 +1618,22 @@ snapshots: '@types/node': 24.2.0 '@types/yargs': 17.0.33 chalk: 4.1.2 - '@jridgewell/gen-mapping@0.3.12': dependencies: '@jridgewell/sourcemap-codec': 1.5.4 '@jridgewell/trace-mapping': 0.3.29 - '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/sourcemap-codec@1.5.4': {} - '@jridgewell/trace-mapping@0.3.29': dependencies: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.4 - '@noble/curves@1.9.6': dependencies: '@noble/hashes': 1.8.0 - '@noble/hashes@1.8.0': {} - - '@openzeppelin/relayer-plugin-launchtube@0.2.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)': - dependencies: + ? '@openzeppelin/relayer-plugin-launchtube@0.2.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)' + : dependencies: '@actions/exec': 1.1.1 '@openzeppelin/relayer-sdk': 1.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10) '@stellar/stellar-sdk': 11.3.0 @@ -2114,9 +1644,8 @@ snapshots: - fastestsmallesttextencoderdecoder - typescript - utf-8-validate - - '@openzeppelin/relayer-sdk@1.2.1(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)': - dependencies: + ? '@openzeppelin/relayer-sdk@1.2.1(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)' + : dependencies: '@actions/exec': 1.1.1 '@solana/spl-token': 0.4.13(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10) '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) @@ -2128,9 +1657,8 @@ snapshots: - fastestsmallesttextencoderdecoder - typescript - utf-8-validate - - '@openzeppelin/relayer-sdk@1.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)': - dependencies: + ? '@openzeppelin/relayer-sdk@1.4.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)' + : dependencies: '@actions/exec': 1.1.1 '@solana/spl-token': 0.4.13(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10) '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) @@ -2142,17 +1670,13 @@ snapshots: - fastestsmallesttextencoderdecoder - typescript - utf-8-validate - '@sinclair/typebox@0.27.8': {} - '@sinonjs/commons@3.0.1': dependencies: type-detect: 4.0.8 - '@sinonjs/fake-timers@10.3.0': dependencies: '@sinonjs/commons': 3.0.1 - '@solana/buffer-layout-utils@0.2.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)': dependencies: '@solana/buffer-layout': 4.0.1 @@ -2164,40 +1688,33 @@ snapshots: - encoding - typescript - utf-8-validate - '@solana/buffer-layout@4.0.1': dependencies: buffer: 6.0.3 - '@solana/codecs-core@2.0.0-rc.1(typescript@5.9.2)': dependencies: '@solana/errors': 2.0.0-rc.1(typescript@5.9.2) typescript: 5.9.2 - '@solana/codecs-core@2.3.0(typescript@5.9.2)': dependencies: '@solana/errors': 2.3.0(typescript@5.9.2) typescript: 5.9.2 - '@solana/codecs-data-structures@2.0.0-rc.1(typescript@5.9.2)': dependencies: '@solana/codecs-core': 2.0.0-rc.1(typescript@5.9.2) '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.9.2) '@solana/errors': 2.0.0-rc.1(typescript@5.9.2) typescript: 5.9.2 - '@solana/codecs-numbers@2.0.0-rc.1(typescript@5.9.2)': dependencies: '@solana/codecs-core': 2.0.0-rc.1(typescript@5.9.2) '@solana/errors': 2.0.0-rc.1(typescript@5.9.2) typescript: 5.9.2 - '@solana/codecs-numbers@2.3.0(typescript@5.9.2)': dependencies: '@solana/codecs-core': 2.3.0(typescript@5.9.2) '@solana/errors': 2.3.0(typescript@5.9.2) typescript: 5.9.2 - '@solana/codecs-strings@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)': dependencies: '@solana/codecs-core': 2.0.0-rc.1(typescript@5.9.2) @@ -2205,7 +1722,6 @@ snapshots: '@solana/errors': 2.0.0-rc.1(typescript@5.9.2) fastestsmallesttextencoderdecoder: 1.0.22 typescript: 5.9.2 - '@solana/codecs@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)': dependencies: '@solana/codecs-core': 2.0.0-rc.1(typescript@5.9.2) @@ -2216,19 +1732,16 @@ snapshots: typescript: 5.9.2 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/errors@2.0.0-rc.1(typescript@5.9.2)': dependencies: chalk: 5.5.0 commander: 12.1.0 typescript: 5.9.2 - '@solana/errors@2.3.0(typescript@5.9.2)': dependencies: chalk: 5.5.0 commander: 14.0.0 typescript: 5.9.2 - '@solana/options@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)': dependencies: '@solana/codecs-core': 2.0.0-rc.1(typescript@5.9.2) @@ -2239,25 +1752,22 @@ snapshots: typescript: 5.9.2 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - - '@solana/spl-token-group@0.0.7(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)': - dependencies: + ? '@solana/spl-token-group@0.0.7(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)' + : dependencies: '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) transitivePeerDependencies: - fastestsmallesttextencoderdecoder - typescript - - '@solana/spl-token-metadata@0.1.6(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)': - dependencies: + ? '@solana/spl-token-metadata@0.1.6(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)' + : dependencies: '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) transitivePeerDependencies: - fastestsmallesttextencoderdecoder - typescript - - '@solana/spl-token@0.4.13(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)': - dependencies: + ? '@solana/spl-token@0.4.13(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)' + : dependencies: '@solana/buffer-layout': 4.0.1 '@solana/buffer-layout-utils': 0.2.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) '@solana/spl-token-group': 0.0.7(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) @@ -2270,7 +1780,6 @@ snapshots: - fastestsmallesttextencoderdecoder - typescript - utf-8-validate - '@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)': dependencies: '@babel/runtime': 7.28.2 @@ -2293,9 +1802,7 @@ snapshots: - encoding - typescript - utf-8-validate - '@stellar/js-xdr@3.1.2': {} - '@stellar/stellar-base@11.0.1': dependencies: '@stellar/js-xdr': 3.1.2 @@ -2306,7 +1813,6 @@ snapshots: tweetnacl: 1.0.3 optionalDependencies: sodium-native: 4.3.3 - '@stellar/stellar-sdk@11.3.0': dependencies: '@stellar/stellar-base': 11.0.1 @@ -2318,11 +1824,9 @@ snapshots: urijs: 1.19.11 transitivePeerDependencies: - debug - '@swc/helpers@0.5.17': dependencies: tslib: 2.8.1 - '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.28.0 @@ -2330,100 +1834,72 @@ snapshots: '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.28.0 - '@types/babel__generator@7.27.0': dependencies: '@babel/types': 7.28.2 - '@types/babel__template@7.4.4': dependencies: '@babel/parser': 7.28.0 '@babel/types': 7.28.2 - '@types/babel__traverse@7.28.0': dependencies: '@babel/types': 7.28.2 - '@types/connect@3.4.38': dependencies: '@types/node': 24.2.0 - '@types/graceful-fs@4.1.9': dependencies: '@types/node': 24.2.0 - '@types/istanbul-lib-coverage@2.0.6': {} - '@types/istanbul-lib-report@3.0.3': dependencies: '@types/istanbul-lib-coverage': 2.0.6 - '@types/istanbul-reports@3.0.4': dependencies: '@types/istanbul-lib-report': 3.0.3 - '@types/jest@29.5.14': dependencies: expect: 29.7.0 pretty-format: 29.7.0 - '@types/node@12.20.55': {} - '@types/node@24.2.0': dependencies: undici-types: 7.10.0 - '@types/stack-utils@2.0.3': {} - '@types/uuid@10.0.0': {} - '@types/uuid@8.3.4': {} - '@types/ws@7.4.7': dependencies: '@types/node': 24.2.0 - '@types/ws@8.18.1': dependencies: '@types/node': 24.2.0 - '@types/yargs-parser@21.0.3': {} - '@types/yargs@17.0.33': dependencies: '@types/yargs-parser': 21.0.3 - agentkeepalive@4.6.0: dependencies: humanize-ms: 1.2.1 - ansi-escapes@4.3.2: dependencies: type-fest: 0.21.3 - ansi-regex@5.0.1: {} - ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 - ansi-styles@5.2.0: {} - anymatch@3.1.3: dependencies: normalize-path: 3.0.0 picomatch: 2.3.1 - argparse@1.0.10: dependencies: sprintf-js: 1.0.3 - asynckit@0.4.0: {} - available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.1.0 - axios@1.11.0: dependencies: follow-redirects: 1.15.11 @@ -2431,7 +1907,6 @@ snapshots: proxy-from-env: 1.1.0 transitivePeerDependencies: - debug - babel-jest@29.7.0(@babel/core@7.28.0): dependencies: '@babel/core': 7.28.0 @@ -2444,7 +1919,6 @@ snapshots: slash: 3.0.0 transitivePeerDependencies: - supports-color - babel-plugin-istanbul@6.1.1: dependencies: '@babel/helper-plugin-utils': 7.27.1 @@ -2454,14 +1928,12 @@ snapshots: test-exclude: 6.0.0 transitivePeerDependencies: - supports-color - babel-plugin-jest-hoist@29.6.3: dependencies: '@babel/template': 7.27.2 '@babel/types': 7.28.2 '@types/babel__core': 7.20.5 '@types/babel__traverse': 7.28.0 - babel-preset-current-node-syntax@1.2.0(@babel/core@7.28.0): dependencies: '@babel/core': 7.28.0 @@ -2480,15 +1952,12 @@ snapshots: '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.28.0) '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.28.0) '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.28.0) - babel-preset-jest@29.6.3(@babel/core@7.28.0): dependencies: '@babel/core': 7.28.0 babel-plugin-jest-hoist: 29.6.3 babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.0) - balanced-match@1.0.2: {} - bare-addon-resolve@1.9.4(bare-url@2.2.1): dependencies: bare-module-resolve: 1.11.1(bare-url@2.2.1) @@ -2496,164 +1965,118 @@ snapshots: optionalDependencies: bare-url: 2.2.1 optional: true - bare-module-resolve@1.11.1(bare-url@2.2.1): dependencies: bare-semver: 1.0.1 optionalDependencies: bare-url: 2.2.1 optional: true - bare-os@3.6.1: optional: true - bare-path@3.0.0: dependencies: bare-os: 3.6.1 optional: true - bare-semver@1.0.1: optional: true - bare-url@2.2.1: dependencies: bare-path: 3.0.0 optional: true - base-x@3.0.11: dependencies: safe-buffer: 5.2.1 - base32.js@0.1.0: {} - base64-js@1.5.1: {} - bigint-buffer@1.1.5: dependencies: bindings: 1.5.0 - bignumber.js@9.3.1: {} - bindings@1.5.0: dependencies: file-uri-to-path: 1.0.0 - bn.js@5.2.2: {} - borsh@0.7.0: dependencies: bn.js: 5.2.2 bs58: 4.0.1 text-encoding-utf-8: 1.0.2 - brace-expansion@1.1.12: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - braces@3.0.3: dependencies: fill-range: 7.1.1 - browserslist@4.25.1: dependencies: caniuse-lite: 1.0.30001731 electron-to-chromium: 1.5.194 node-releases: 2.0.19 update-browserslist-db: 1.1.3(browserslist@4.25.1) - bs-logger@0.2.6: dependencies: fast-json-stable-stringify: 2.1.0 - bs58@4.0.1: dependencies: base-x: 3.0.11 - bser@2.1.1: dependencies: node-int64: 0.4.0 - buffer-from@1.1.2: {} - buffer@6.0.3: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 - bufferutil@4.0.9: dependencies: node-gyp-build: 4.8.4 optional: true - call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 function-bind: 1.1.2 - call-bind@1.0.8: dependencies: call-bind-apply-helpers: 1.0.2 es-define-property: 1.0.1 get-intrinsic: 1.3.0 set-function-length: 1.2.2 - call-bound@1.0.4: dependencies: call-bind-apply-helpers: 1.0.2 get-intrinsic: 1.3.0 - callsites@3.1.0: {} - camelcase@5.3.1: {} - camelcase@6.3.0: {} - caniuse-lite@1.0.30001731: {} - chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - chalk@5.5.0: {} - char-regex@1.0.2: {} - ci-info@3.9.0: {} - cjs-module-lexer@1.4.3: {} - cliui@8.0.1: dependencies: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - co@4.6.0: {} - collect-v8-coverage@1.0.2: {} - color-convert@2.0.1: dependencies: color-name: 1.1.4 - color-name@1.1.4: {} - combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 - commander@12.1.0: {} - commander@14.0.0: {} - commander@2.20.3: {} - concat-map@0.0.1: {} - convert-source-map@2.0.0: {} - create-jest@29.7.0(@types/node@24.2.0): dependencies: '@jest/types': 29.6.3 @@ -2668,82 +2091,56 @@ snapshots: - babel-plugin-macros - supports-color - ts-node - cross-spawn@7.0.6: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 - debug@4.4.1: dependencies: ms: 2.1.3 - dedent@1.6.0: {} - deepmerge@4.3.1: {} - define-data-property@1.1.4: dependencies: es-define-property: 1.0.1 es-errors: 1.3.0 gopd: 1.2.0 - delay@5.0.0: {} - delayed-stream@1.0.0: {} - detect-newline@3.1.0: {} - diff-sequences@29.6.3: {} - dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 es-errors: 1.3.0 gopd: 1.2.0 - electron-to-chromium@1.5.194: {} - emittery@0.13.1: {} - emoji-regex@8.0.0: {} - error-ex@1.3.2: dependencies: is-arrayish: 0.2.1 - es-define-property@1.0.1: {} - es-errors@1.3.0: {} - es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 - es-set-tostringtag@2.1.0: dependencies: es-errors: 1.3.0 get-intrinsic: 1.3.0 has-tostringtag: 1.0.2 hasown: 2.0.2 - es6-promise@4.2.8: {} - es6-promisify@5.0.0: dependencies: es6-promise: 4.2.8 - escalade@3.2.0: {} - escape-string-regexp@2.0.0: {} - esprima@4.0.1: {} - eventemitter3@5.0.1: {} - eventsource@2.0.2: {} - execa@5.1.1: dependencies: cross-spawn: 7.0.6 @@ -2755,9 +2152,7 @@ snapshots: onetime: 5.1.2 signal-exit: 3.0.7 strip-final-newline: 2.0.0 - exit@0.1.2: {} - expect@29.7.0: dependencies: '@jest/expect-utils': 29.7.0 @@ -2765,36 +2160,25 @@ snapshots: jest-matcher-utils: 29.7.0 jest-message-util: 29.7.0 jest-util: 29.7.0 - eyes@0.1.8: {} - fast-json-stable-stringify@2.1.0: {} - fast-stable-stringify@1.0.0: {} - fastestsmallesttextencoderdecoder@1.0.22: {} - fb-watchman@2.0.2: dependencies: bser: 2.1.1 - file-uri-to-path@1.0.0: {} - fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 - find-up@4.1.0: dependencies: locate-path: 5.0.0 path-exists: 4.0.0 - follow-redirects@1.15.11: {} - for-each@0.3.5: dependencies: is-callable: 1.2.7 - form-data@4.0.4: dependencies: asynckit: 0.4.0 @@ -2802,18 +2186,12 @@ snapshots: es-set-tostringtag: 2.1.0 hasown: 2.0.2 mime-types: 2.1.35 - fs.realpath@1.0.0: {} - fsevents@2.3.3: optional: true - function-bind@1.1.2: {} - gensync@1.0.0-beta.2: {} - get-caller-file@2.0.5: {} - get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -2826,16 +2204,12 @@ snapshots: has-symbols: 1.1.0 hasown: 2.0.2 math-intrinsics: 1.1.0 - get-package-type@0.1.0: {} - get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 - get-stream@6.0.1: {} - glob@7.2.3: dependencies: fs.realpath: 1.0.0 @@ -2844,11 +2218,8 @@ snapshots: minimatch: 3.1.2 once: 1.4.0 path-is-absolute: 1.0.1 - gopd@1.2.0: {} - graceful-fs@4.2.11: {} - handlebars@4.7.8: dependencies: minimist: 1.2.8 @@ -2857,79 +2228,52 @@ snapshots: wordwrap: 1.0.0 optionalDependencies: uglify-js: 3.19.3 - has-flag@4.0.0: {} - has-property-descriptors@1.0.2: dependencies: es-define-property: 1.0.1 - has-symbols@1.1.0: {} - has-tostringtag@1.0.2: dependencies: has-symbols: 1.1.0 - hasown@2.0.2: dependencies: function-bind: 1.1.2 - html-escaper@2.0.2: {} - human-signals@2.1.0: {} - humanize-ms@1.2.1: dependencies: ms: 2.1.3 - husky@9.1.7: {} - ieee754@1.2.1: {} - import-local@3.2.0: dependencies: pkg-dir: 4.2.0 resolve-cwd: 3.0.0 - imurmurhash@0.1.4: {} - inflight@1.0.6: dependencies: once: 1.4.0 wrappy: 1.0.2 - inherits@2.0.4: {} - is-arrayish@0.2.1: {} - is-callable@1.2.7: {} - is-core-module@2.16.1: dependencies: hasown: 2.0.2 - is-fullwidth-code-point@3.0.0: {} - is-generator-fn@2.1.0: {} - is-number@7.0.0: {} - is-stream@2.0.1: {} - is-typed-array@1.1.15: dependencies: which-typed-array: 1.1.19 - isarray@2.0.5: {} - isexe@2.0.0: {} - isomorphic-ws@4.0.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)): dependencies: ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) - istanbul-lib-coverage@3.2.2: {} - istanbul-lib-instrument@5.2.1: dependencies: '@babel/core': 7.28.0 @@ -2939,7 +2283,6 @@ snapshots: semver: 6.3.1 transitivePeerDependencies: - supports-color - istanbul-lib-instrument@6.0.3: dependencies: '@babel/core': 7.28.0 @@ -2949,13 +2292,11 @@ snapshots: semver: 7.7.2 transitivePeerDependencies: - supports-color - istanbul-lib-report@3.0.1: dependencies: istanbul-lib-coverage: 3.2.2 make-dir: 4.0.0 supports-color: 7.2.0 - istanbul-lib-source-maps@4.0.1: dependencies: debug: 4.4.1 @@ -2963,12 +2304,10 @@ snapshots: source-map: 0.6.1 transitivePeerDependencies: - supports-color - istanbul-reports@3.1.7: dependencies: html-escaper: 2.0.2 istanbul-lib-report: 3.0.1 - jayson@4.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10): dependencies: '@types/connect': 3.4.38 @@ -2986,13 +2325,11 @@ snapshots: transitivePeerDependencies: - bufferutil - utf-8-validate - jest-changed-files@29.7.0: dependencies: execa: 5.1.1 jest-util: 29.7.0 p-limit: 3.1.0 - jest-circus@29.7.0: dependencies: '@jest/environment': 29.7.0 @@ -3018,7 +2355,6 @@ snapshots: transitivePeerDependencies: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@24.2.0): dependencies: '@jest/core': 29.7.0 @@ -3037,7 +2373,6 @@ snapshots: - babel-plugin-macros - supports-color - ts-node - jest-config@29.7.0(@types/node@24.2.0): dependencies: '@babel/core': 7.28.0 @@ -3067,18 +2402,15 @@ snapshots: transitivePeerDependencies: - babel-plugin-macros - supports-color - jest-diff@29.7.0: dependencies: chalk: 4.1.2 diff-sequences: 29.6.3 jest-get-type: 29.6.3 pretty-format: 29.7.0 - jest-docblock@29.7.0: dependencies: detect-newline: 3.1.0 - jest-each@29.7.0: dependencies: '@jest/types': 29.6.3 @@ -3086,7 +2418,6 @@ snapshots: jest-get-type: 29.6.3 jest-util: 29.7.0 pretty-format: 29.7.0 - jest-environment-node@29.7.0: dependencies: '@jest/environment': 29.7.0 @@ -3095,9 +2426,7 @@ snapshots: '@types/node': 24.2.0 jest-mock: 29.7.0 jest-util: 29.7.0 - jest-get-type@29.6.3: {} - jest-haste-map@29.7.0: dependencies: '@jest/types': 29.6.3 @@ -3113,19 +2442,16 @@ snapshots: walker: 1.0.8 optionalDependencies: fsevents: 2.3.3 - jest-leak-detector@29.7.0: dependencies: jest-get-type: 29.6.3 pretty-format: 29.7.0 - jest-matcher-utils@29.7.0: dependencies: chalk: 4.1.2 jest-diff: 29.7.0 jest-get-type: 29.6.3 pretty-format: 29.7.0 - jest-message-util@29.7.0: dependencies: '@babel/code-frame': 7.27.1 @@ -3137,26 +2463,21 @@ snapshots: pretty-format: 29.7.0 slash: 3.0.0 stack-utils: 2.0.6 - jest-mock@29.7.0: dependencies: '@jest/types': 29.6.3 '@types/node': 24.2.0 jest-util: 29.7.0 - jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): optionalDependencies: jest-resolve: 29.7.0 - jest-regex-util@29.6.3: {} - jest-resolve-dependencies@29.7.0: dependencies: jest-regex-util: 29.6.3 jest-snapshot: 29.7.0 transitivePeerDependencies: - supports-color - jest-resolve@29.7.0: dependencies: chalk: 4.1.2 @@ -3168,7 +2489,6 @@ snapshots: resolve: 1.22.10 resolve.exports: 2.0.3 slash: 3.0.0 - jest-runner@29.7.0: dependencies: '@jest/console': 29.7.0 @@ -3194,7 +2514,6 @@ snapshots: source-map-support: 0.5.13 transitivePeerDependencies: - supports-color - jest-runtime@29.7.0: dependencies: '@jest/environment': 29.7.0 @@ -3221,7 +2540,6 @@ snapshots: strip-bom: 4.0.0 transitivePeerDependencies: - supports-color - jest-snapshot@29.7.0: dependencies: '@babel/core': 7.28.0 @@ -3246,7 +2564,6 @@ snapshots: semver: 7.7.2 transitivePeerDependencies: - supports-color - jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 @@ -3255,7 +2572,6 @@ snapshots: ci-info: 3.9.0 graceful-fs: 4.2.11 picomatch: 2.3.1 - jest-validate@29.7.0: dependencies: '@jest/types': 29.6.3 @@ -3264,7 +2580,6 @@ snapshots: jest-get-type: 29.6.3 leven: 3.1.0 pretty-format: 29.7.0 - jest-watcher@29.7.0: dependencies: '@jest/test-result': 29.7.0 @@ -3275,14 +2590,12 @@ snapshots: emittery: 0.13.1 jest-util: 29.7.0 string-length: 4.0.2 - jest-worker@29.7.0: dependencies: '@types/node': 24.2.0 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@24.2.0): dependencies: '@jest/core': 29.7.0 @@ -3294,186 +2607,125 @@ snapshots: - babel-plugin-macros - supports-color - ts-node - js-tokens@4.0.0: {} - js-yaml@3.14.1: dependencies: argparse: 1.0.10 esprima: 4.0.1 - jsesc@3.1.0: {} - json-parse-even-better-errors@2.3.1: {} - json-stringify-safe@5.0.1: {} - json5@2.2.3: {} - kleur@3.0.3: {} - leven@3.1.0: {} - lines-and-columns@1.2.4: {} - locate-path@5.0.0: dependencies: p-locate: 4.1.0 - lodash.memoize@4.1.2: {} - lru-cache@5.1.1: dependencies: yallist: 3.1.1 - make-dir@4.0.0: dependencies: semver: 7.7.2 - make-error@1.3.6: {} - makeerror@1.0.12: dependencies: tmpl: 1.0.5 - math-intrinsics@1.1.0: {} - merge-stream@2.0.0: {} - micromatch@4.0.8: dependencies: braces: 3.0.3 picomatch: 2.3.1 - mime-db@1.52.0: {} - mime-types@2.1.35: dependencies: mime-db: 1.52.0 - mimic-fn@2.1.0: {} - minimatch@3.1.2: dependencies: brace-expansion: 1.1.12 - minimist@1.2.8: {} - ms@2.1.3: {} - natural-compare@1.4.0: {} - neo-async@2.6.2: {} - node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 - node-gyp-build@4.8.4: optional: true - node-int64@0.4.0: {} - node-releases@2.0.19: {} - normalize-path@3.0.0: {} - npm-run-path@4.0.1: dependencies: path-key: 3.1.1 - once@1.4.0: dependencies: wrappy: 1.0.2 - onetime@5.1.2: dependencies: mimic-fn: 2.1.0 - p-limit@2.3.0: dependencies: p-try: 2.2.0 - p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 - p-locate@4.1.0: dependencies: p-limit: 2.3.0 - p-try@2.2.0: {} - parse-json@5.2.0: dependencies: '@babel/code-frame': 7.27.1 error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 - path-exists@4.0.0: {} - path-is-absolute@1.0.1: {} - path-key@3.1.1: {} - path-parse@1.0.7: {} - picocolors@1.1.1: {} - picomatch@2.3.1: {} - pirates@4.0.7: {} - pkg-dir@4.2.0: dependencies: find-up: 4.1.0 - possible-typed-array-names@1.1.0: {} - pretty-format@29.7.0: dependencies: '@jest/schemas': 29.6.3 ansi-styles: 5.2.0 react-is: 18.3.1 - prompts@2.4.2: dependencies: kleur: 3.0.3 sisteransi: 1.0.5 - proxy-from-env@1.1.0: {} - pure-rand@6.1.0: {} - randombytes@2.1.0: dependencies: safe-buffer: 5.2.1 - react-is@18.3.1: {} - require-addon@1.1.0: dependencies: bare-addon-resolve: 1.9.4(bare-url@2.2.1) bare-url: 2.2.1 optional: true - require-directory@2.1.1: {} - resolve-cwd@3.0.0: dependencies: resolve-from: 5.0.0 - resolve-from@5.0.0: {} - resolve.exports@2.0.3: {} - resolve@1.22.10: dependencies: is-core-module: 2.16.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - rpc-websockets@9.1.3: dependencies: '@swc/helpers': 0.5.17 @@ -3486,13 +2738,9 @@ snapshots: optionalDependencies: bufferutil: 4.0.9 utf-8-validate: 5.0.10 - safe-buffer@5.2.1: {} - semver@6.3.1: {} - semver@7.7.2: {} - set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -3501,108 +2749,77 @@ snapshots: get-intrinsic: 1.3.0 gopd: 1.2.0 has-property-descriptors: 1.0.2 - sha.js@2.4.12: dependencies: inherits: 2.0.4 safe-buffer: 5.2.1 to-buffer: 1.2.1 - shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 - shebang-regex@3.0.0: {} - signal-exit@3.0.7: {} - sisteransi@1.0.5: {} - slash@3.0.0: {} - sodium-native@4.3.3: dependencies: require-addon: 1.1.0 optional: true - source-map-support@0.5.13: dependencies: buffer-from: 1.1.2 source-map: 0.6.1 - source-map@0.6.1: {} - sprintf-js@1.0.3: {} - stack-utils@2.0.6: dependencies: escape-string-regexp: 2.0.0 - stream-chain@2.2.5: {} - stream-json@1.9.1: dependencies: stream-chain: 2.2.5 - string-length@4.0.2: dependencies: char-regex: 1.0.2 strip-ansi: 6.0.1 - string-width@4.2.3: dependencies: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 - strip-bom@4.0.0: {} - strip-final-newline@2.0.0: {} - strip-json-comments@3.1.1: {} - superstruct@2.0.2: {} - supports-color@7.2.0: dependencies: has-flag: 4.0.0 - supports-color@8.1.1: dependencies: has-flag: 4.0.0 - supports-preserve-symlinks-flag@1.0.0: {} - test-exclude@6.0.0: dependencies: '@istanbuljs/schema': 0.1.3 glob: 7.2.3 minimatch: 3.1.2 - text-encoding-utf-8@1.0.2: {} - tmpl@1.0.5: {} - to-buffer@1.2.1: dependencies: isarray: 2.0.5 safe-buffer: 5.2.1 typed-array-buffer: 1.0.3 - to-regex-range@5.0.1: dependencies: is-number: 7.0.0 - toml@3.0.0: {} - tr46@0.0.3: {} - - ts-jest@29.4.1(@babel/core@7.28.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.0))(jest-util@29.7.0)(jest@29.7.0(@types/node@24.2.0))(typescript@5.9.2): - dependencies: + ? ts-jest@29.4.1(@babel/core@7.28.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.0))(jest-util@29.7.0)(jest@29.7.0(@types/node@24.2.0))(typescript@5.9.2) + : dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 handlebars: 4.7.8 @@ -3620,62 +2837,44 @@ snapshots: '@jest/types': 29.6.3 babel-jest: 29.7.0(@babel/core@7.28.0) jest-util: 29.7.0 - tslib@2.8.1: {} - tweetnacl@1.0.3: {} - type-detect@4.0.8: {} - type-fest@0.21.3: {} - type-fest@4.41.0: {} - typed-array-buffer@1.0.3: dependencies: call-bound: 1.0.4 es-errors: 1.3.0 is-typed-array: 1.1.15 - typescript@5.9.2: {} - uglify-js@3.19.3: optional: true - undici-types@7.10.0: {} - update-browserslist-db@1.1.3(browserslist@4.25.1): dependencies: browserslist: 4.25.1 escalade: 3.2.0 picocolors: 1.1.1 - urijs@1.19.11: {} - utf-8-validate@5.0.10: dependencies: node-gyp-build: 4.8.4 optional: true - uuid@8.3.2: {} - v8-to-istanbul@9.3.0: dependencies: '@jridgewell/trace-mapping': 0.3.29 '@types/istanbul-lib-coverage': 2.0.6 convert-source-map: 2.0.0 - walker@1.0.8: dependencies: makeerror: 1.0.12 - webidl-conversions@3.0.1: {} - whatwg-url@5.0.0: dependencies: tr46: 0.0.3 webidl-conversions: 3.0.1 - which-typed-array@1.1.19: dependencies: available-typed-arrays: 1.0.7 @@ -3685,42 +2884,31 @@ snapshots: get-proto: 1.0.1 gopd: 1.2.0 has-tostringtag: 1.0.2 - which@2.0.2: dependencies: isexe: 2.0.0 - wordwrap@1.0.0: {} - wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - wrappy@1.0.2: {} - write-file-atomic@4.0.2: dependencies: imurmurhash: 0.1.4 signal-exit: 3.0.7 - ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10): optionalDependencies: bufferutil: 4.0.9 utf-8-validate: 5.0.10 - ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10): optionalDependencies: bufferutil: 4.0.9 utf-8-validate: 5.0.10 - y18n@5.0.8: {} - yallist@3.1.1: {} - yargs-parser@21.1.1: {} - yargs@17.7.2: dependencies: cliui: 8.0.1 @@ -3730,5 +2918,4 @@ snapshots: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 21.1.1 - yocto-queue@0.1.0: {} From c7e2faaebb30680e68be40da9383582ffa1da7d8 Mon Sep 17 00:00:00 2001 From: Marcos Date: Wed, 27 Aug 2025 10:33:18 -0300 Subject: [PATCH 13/22] test: Fix tests --- Cargo.lock | 20 +++++++++++--------- Cargo.toml | 2 +- src/api/routes/notification.rs | 2 +- src/api/routes/signer.rs | 2 +- src/utils/mocks.rs | 6 +++--- 5 files changed, 17 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 20392a284..c271e0a0d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2480,7 +2480,7 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -2945,10 +2945,11 @@ checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" [[package]] name = "deadpool" -version = "0.12.2" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ed5957ff93768adf7a65ab167a17835c3d2c3c50d084fe305174c112f468e2f" +checksum = "fb84100978c1c7b37f09ed3ce3e5f843af02c2a2c431bae5b19230dad2c1b490" dependencies = [ + "async-trait", "deadpool-runtime", "num_cpus", "tokio", @@ -4223,7 +4224,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.0", + "socket2 0.5.10", "system-configuration", "tokio", "tower-service", @@ -6017,7 +6018,7 @@ dependencies = [ "once_cell", "socket2 0.5.10", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -6500,7 +6501,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -6614,7 +6615,7 @@ dependencies = [ "security-framework 3.3.0", "security-framework-sys", "webpki-root-certs 0.26.11", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -11523,11 +11524,12 @@ dependencies = [ [[package]] name = "wiremock" -version = "0.6.5" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08db1edfb05d9b3c1542e521aea074442088292f00b5f28e435c714a98f85031" +checksum = "a2b8b99d4cdbf36b239a9532e31fe4fb8acc38d1897c1761e161550a7dc78e6a" dependencies = [ "assert-json-diff", + "async-trait", "base64 0.22.1", "deadpool", "futures", diff --git a/Cargo.toml b/Cargo.toml index 0de0e0b93..c80d3ae31 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -97,7 +97,7 @@ rand = "0.9.0" tempfile = "3.2" serial_test = "3.2" clap = { version = "4.4", features = ["derive"] } -wiremock = "0.6" +wiremock = "0.6.4" [[bin]] name = "openzeppelin-relayer" diff --git a/src/api/routes/notification.rs b/src/api/routes/notification.rs index e8dcee1b3..02919b821 100644 --- a/src/api/routes/notification.rs +++ b/src/api/routes/notification.rs @@ -75,7 +75,7 @@ mod tests { #[actix_web::test] async fn test_notification_routes_are_registered() { // Arrange - Create app with notification routes - let app_state = create_mock_app_state(None, None, None, None, None).await; + let app_state = create_mock_app_state(None, None, None, None, None, None).await; let app = test::init_service( App::new() .app_data(web::Data::new(app_state)) diff --git a/src/api/routes/signer.rs b/src/api/routes/signer.rs index f839a42bc..e6adbf35c 100644 --- a/src/api/routes/signer.rs +++ b/src/api/routes/signer.rs @@ -72,7 +72,7 @@ mod tests { #[actix_web::test] async fn test_signer_routes_are_registered() { // Arrange - Create app with signer routes - let app_state = create_mock_app_state(None, None, None, None, None).await; + let app_state = create_mock_app_state(None, None, None, None, None, None).await; let app = test::init_service( App::new() .app_data(web::Data::new(app_state)) diff --git a/src/utils/mocks.rs b/src/utils/mocks.rs index 0323c0bea..06000f07c 100644 --- a/src/utils/mocks.rs +++ b/src/utils/mocks.rs @@ -13,12 +13,12 @@ pub mod mockutils { }, jobs::MockJobProducerTrait, models::{ - AppState, EvmTransactionData, EvmTransactionRequest, LocalSignerConfig, + ApiKeyModel, AppState, EvmTransactionData, EvmTransactionRequest, LocalSignerConfigStorage, NetworkConfigData, NetworkRepoModel, NetworkTransactionData, NetworkType, NotificationRepoModel, PluginModel, RelayerEvmPolicy, RelayerNetworkPolicy, RelayerRepoModel, RelayerSolanaPolicy, SecretString, - SignerConfig, SignerConfigStorage, SignerRepoModel, SolanaTransactionData, - TransactionRepoModel, TransactionStatus, + SignerConfigStorage, SignerRepoModel, SolanaTransactionData, TransactionRepoModel, + TransactionStatus, }, repositories::{ ApiKeyRepositoryStorage, ApiKeyRepositoryTrait, NetworkRepositoryStorage, From 77c16f9ee4802b7c89d9f16d1f3f02be62cfef3d Mon Sep 17 00:00:00 2001 From: Marcos Date: Thu, 28 Aug 2025 09:15:53 -0300 Subject: [PATCH 14/22] fix: Rollback wiremock change --- Cargo.lock | 20 +++++++++----------- Cargo.toml | 2 +- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c271e0a0d..20392a284 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2480,7 +2480,7 @@ version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2945,11 +2945,10 @@ checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" [[package]] name = "deadpool" -version = "0.10.0" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb84100978c1c7b37f09ed3ce3e5f843af02c2a2c431bae5b19230dad2c1b490" +checksum = "5ed5957ff93768adf7a65ab167a17835c3d2c3c50d084fe305174c112f468e2f" dependencies = [ - "async-trait", "deadpool-runtime", "num_cpus", "tokio", @@ -4224,7 +4223,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.10", + "socket2 0.6.0", "system-configuration", "tokio", "tower-service", @@ -6018,7 +6017,7 @@ dependencies = [ "once_cell", "socket2 0.5.10", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -6501,7 +6500,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -6615,7 +6614,7 @@ dependencies = [ "security-framework 3.3.0", "security-framework-sys", "webpki-root-certs 0.26.11", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -11524,12 +11523,11 @@ dependencies = [ [[package]] name = "wiremock" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2b8b99d4cdbf36b239a9532e31fe4fb8acc38d1897c1761e161550a7dc78e6a" +checksum = "08db1edfb05d9b3c1542e521aea074442088292f00b5f28e435c714a98f85031" dependencies = [ "assert-json-diff", - "async-trait", "base64 0.22.1", "deadpool", "futures", diff --git a/Cargo.toml b/Cargo.toml index c80d3ae31..0de0e0b93 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -97,7 +97,7 @@ rand = "0.9.0" tempfile = "3.2" serial_test = "3.2" clap = { version = "4.4", features = ["derive"] } -wiremock = "0.6.4" +wiremock = "0.6" [[bin]] name = "openzeppelin-relayer" From 547d5243011495e2f127ab97443c116851ac2a71 Mon Sep 17 00:00:00 2001 From: Marcos Date: Thu, 28 Aug 2025 10:20:09 -0300 Subject: [PATCH 15/22] refactor: Split models into files --- src/api/routes/relayer.rs | 10 +++---- src/models/api_key.rs | 27 ------------------- src/models/api_key/mod.rs | 8 ++++++ src/models/api_key/repository.rs | 11 ++++++++ src/models/api_key/request.rs | 8 ++++++ src/models/api_key/response.rs | 10 +++++++ src/repositories/api_key/api_key_in_memory.rs | 26 +++++++++--------- src/repositories/api_key/api_key_redis.rs | 16 +++++------ src/repositories/api_key/mod.rs | 20 +++++++------- src/utils/mocks.rs | 8 +++--- 10 files changed, 77 insertions(+), 67 deletions(-) delete mode 100644 src/models/api_key.rs create mode 100644 src/models/api_key/mod.rs create mode 100644 src/models/api_key/repository.rs create mode 100644 src/models/api_key/request.rs create mode 100644 src/models/api_key/response.rs diff --git a/src/api/routes/relayer.rs b/src/api/routes/relayer.rs index 47e22e304..78b992345 100644 --- a/src/api/routes/relayer.rs +++ b/src/api/routes/relayer.rs @@ -222,10 +222,10 @@ mod tests { config::{EvmNetworkConfig, NetworkConfigCommon}, jobs::MockJobProducerTrait, models::{ - ApiKeyModel, AppState, EvmTransactionData, LocalSignerConfigStorage, NetworkConfigData, - NetworkRepoModel, NetworkTransactionData, NetworkType, RelayerEvmPolicy, - RelayerNetworkPolicy, RelayerRepoModel, SignerConfigStorage, SignerRepoModel, - TransactionRepoModel, TransactionStatus, U256, + ApiKeyRepoModel, AppState, EvmTransactionData, LocalSignerConfigStorage, + NetworkConfigData, NetworkRepoModel, NetworkTransactionData, NetworkType, + RelayerEvmPolicy, RelayerNetworkPolicy, RelayerRepoModel, SignerConfigStorage, + SignerRepoModel, TransactionRepoModel, TransactionStatus, U256, }, repositories::{ ApiKeyRepositoryStorage, ApiKeyRepositoryTrait, NetworkRepositoryStorage, @@ -341,7 +341,7 @@ mod tests { transaction_repo.create(test_transaction).await.unwrap(); // Create test api key - let test_api_key = ApiKeyModel { + let test_api_key = ApiKeyRepoModel { id: "test-api-key".to_string(), name: "Test API Key".to_string(), value: "test-value".to_string(), diff --git a/src/models/api_key.rs b/src/models/api_key.rs deleted file mode 100644 index 5bcd77317..000000000 --- a/src/models/api_key.rs +++ /dev/null @@ -1,27 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Serialize, Deserialize)] -pub struct ApiKeyRequest { - pub name: String, - pub permissions: Vec, - pub allowed_origins: Option>, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct ApiKeyModel { - pub id: String, - pub value: String, - pub name: String, - pub allowed_origins: Vec, - pub created_at: String, - pub permissions: Vec, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct ApiKeyResponse { - pub id: String, - pub name: String, - pub allowed_origins: Vec, - pub created_at: String, - pub permissions: Vec, -} diff --git a/src/models/api_key/mod.rs b/src/models/api_key/mod.rs new file mode 100644 index 000000000..2845386b9 --- /dev/null +++ b/src/models/api_key/mod.rs @@ -0,0 +1,8 @@ +mod request; +pub use request::*; + +mod response; +pub use response::*; + +mod repository; +pub use repository::*; diff --git a/src/models/api_key/repository.rs b/src/models/api_key/repository.rs new file mode 100644 index 000000000..35f07e251 --- /dev/null +++ b/src/models/api_key/repository.rs @@ -0,0 +1,11 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ApiKeyRepoModel { + pub id: String, + pub value: String, + pub name: String, + pub allowed_origins: Vec, + pub created_at: String, + pub permissions: Vec, +} diff --git a/src/models/api_key/request.rs b/src/models/api_key/request.rs new file mode 100644 index 000000000..4373532c0 --- /dev/null +++ b/src/models/api_key/request.rs @@ -0,0 +1,8 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize)] +pub struct ApiKeyRequest { + pub name: String, + pub permissions: Vec, + pub allowed_origins: Option>, +} diff --git a/src/models/api_key/response.rs b/src/models/api_key/response.rs new file mode 100644 index 000000000..5b0dba330 --- /dev/null +++ b/src/models/api_key/response.rs @@ -0,0 +1,10 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ApiKeyResponse { + pub id: String, + pub name: String, + pub allowed_origins: Vec, + pub created_at: String, + pub permissions: Vec, +} diff --git a/src/repositories/api_key/api_key_in_memory.rs b/src/repositories/api_key/api_key_in_memory.rs index 42300993d..95cd706db 100644 --- a/src/repositories/api_key/api_key_in_memory.rs +++ b/src/repositories/api_key/api_key_in_memory.rs @@ -3,7 +3,7 @@ //! The `InMemoryApiKeyRepository` struct is used to store and retrieve api keys //! permissions. use crate::{ - models::{ApiKeyModel, PaginationQuery}, + models::{ApiKeyRepoModel, PaginationQuery}, repositories::{ApiKeyRepositoryTrait, PaginatedResult, RepositoryError}, }; @@ -14,7 +14,7 @@ use tokio::sync::{Mutex, MutexGuard}; #[derive(Debug)] pub struct InMemoryApiKeyRepository { - store: Mutex>, + store: Mutex>, } impl Clone for InMemoryApiKeyRepository { @@ -52,13 +52,13 @@ impl Default for InMemoryApiKeyRepository { #[async_trait] impl ApiKeyRepositoryTrait for InMemoryApiKeyRepository { - async fn create(&self, api_key: ApiKeyModel) -> Result { + async fn create(&self, api_key: ApiKeyRepoModel) -> Result { let mut store = Self::acquire_lock(&self.store).await?; store.insert(api_key.id.clone(), api_key.clone()); Ok(api_key) } - async fn get_by_id(&self, id: &str) -> Result, RepositoryError> { + async fn get_by_id(&self, id: &str) -> Result, RepositoryError> { let store = Self::acquire_lock(&self.store).await?; Ok(store.get(id).cloned()) } @@ -66,7 +66,7 @@ impl ApiKeyRepositoryTrait for InMemoryApiKeyRepository { async fn list_paginated( &self, query: PaginationQuery, - ) -> Result, RepositoryError> { + ) -> Result, RepositoryError> { let total = self.count().await?; let start = ((query.page - 1) * query.per_page) as usize; @@ -134,7 +134,7 @@ mod tests { let api_key_repository = Arc::new(InMemoryApiKeyRepository::new()); // Test add and get_by_id - let api_key = ApiKeyModel { + let api_key = ApiKeyRepoModel { id: "test-api-key".to_string(), value: "test-value".to_string(), name: "test-name".to_string(), @@ -161,7 +161,7 @@ mod tests { async fn test_get_by_id() { let api_key_repository = Arc::new(InMemoryApiKeyRepository::new()); - let api_key = ApiKeyModel { + let api_key = ApiKeyRepoModel { id: "test-api-key".to_string(), value: "test-value".to_string(), name: "test-name".to_string(), @@ -180,7 +180,7 @@ mod tests { async fn test_list_paginated_api_keys() { let api_key_repository = Arc::new(InMemoryApiKeyRepository::new()); - let api_key1 = ApiKeyModel { + let api_key1 = ApiKeyRepoModel { id: "test-api-key1".to_string(), value: "test-value1".to_string(), name: "test-name1".to_string(), @@ -189,7 +189,7 @@ mod tests { created_at: Utc::now().to_string(), }; - let api_key2 = ApiKeyModel { + let api_key2 = ApiKeyRepoModel { id: "test-api-key2".to_string(), value: "test-value2".to_string(), name: "test-name2".to_string(), @@ -217,7 +217,7 @@ mod tests { let api_key_repository = Arc::new(InMemoryApiKeyRepository::new()); assert!(!api_key_repository.has_entries().await.unwrap()); api_key_repository - .create(ApiKeyModel { + .create(ApiKeyRepoModel { id: "test-api-key".to_string(), value: "test-value".to_string(), name: "test-name".to_string(), @@ -237,7 +237,7 @@ mod tests { async fn test_delete_by_id_api_key() { let api_key_repository = Arc::new(InMemoryApiKeyRepository::new()); api_key_repository - .create(ApiKeyModel { + .create(ApiKeyRepoModel { id: "test-api-key".to_string(), value: "test-value".to_string(), name: "test-name".to_string(), @@ -260,7 +260,7 @@ mod tests { async fn test_list_permissions_api_key() { let api_key_repository = Arc::new(InMemoryApiKeyRepository::new()); api_key_repository - .create(ApiKeyModel { + .create(ApiKeyRepoModel { id: "test-api-key".to_string(), value: "test-value".to_string(), name: "test-name".to_string(), @@ -285,7 +285,7 @@ mod tests { async fn test_drop_all_entries() { let api_key_repository = Arc::new(InMemoryApiKeyRepository::new()); api_key_repository - .create(ApiKeyModel { + .create(ApiKeyRepoModel { id: "test-api-key".to_string(), value: "test-value".to_string(), name: "test-name".to_string(), diff --git a/src/repositories/api_key/api_key_redis.rs b/src/repositories/api_key/api_key_redis.rs index 1f6804a2c..68de2392c 100644 --- a/src/repositories/api_key/api_key_redis.rs +++ b/src/repositories/api_key/api_key_redis.rs @@ -1,6 +1,6 @@ //! Redis-backed implementation of the ApiKeyRepository. -use crate::models::{ApiKeyModel, PaginationQuery, RepositoryError}; +use crate::models::{ApiKeyRepoModel, PaginationQuery, RepositoryError}; use crate::repositories::redis_base::RedisRepository; use crate::repositories::{ApiKeyRepositoryTrait, BatchRetrievalResult, PaginatedResult}; use async_trait::async_trait; @@ -51,7 +51,7 @@ impl RedisApiKeyRepository { async fn get_by_ids( &self, ids: &[String], - ) -> Result, RepositoryError> { + ) -> Result, RepositoryError> { if ids.is_empty() { debug!("No api key IDs provided for batch fetch"); return Ok(BatchRetrievalResult { @@ -114,7 +114,7 @@ impl fmt::Debug for RedisApiKeyRepository { #[async_trait] impl ApiKeyRepositoryTrait for RedisApiKeyRepository { - async fn create(&self, entity: ApiKeyModel) -> Result { + async fn create(&self, entity: ApiKeyRepoModel) -> Result { if entity.id.is_empty() { return Err(RepositoryError::InvalidData( "API Key ID cannot be empty".to_string(), @@ -156,7 +156,7 @@ impl ApiKeyRepositoryTrait for RedisApiKeyRepository { async fn list_paginated( &self, query: PaginationQuery, - ) -> Result, RepositoryError> { + ) -> Result, RepositoryError> { if query.page == 0 { return Err(RepositoryError::InvalidData( "Page number must be greater than 0".to_string(), @@ -206,7 +206,7 @@ impl ApiKeyRepositoryTrait for RedisApiKeyRepository { }) } - async fn get_by_id(&self, id: &str) -> Result, RepositoryError> { + async fn get_by_id(&self, id: &str) -> Result, RepositoryError> { if id.is_empty() { return Err(RepositoryError::InvalidData( "API Key ID cannot be empty".to_string(), @@ -357,8 +357,8 @@ mod tests { use super::*; use chrono::Utc; - fn create_test_api_key(id: &str) -> ApiKeyModel { - ApiKeyModel { + fn create_test_api_key(id: &str) -> ApiKeyRepoModel { + ApiKeyRepoModel { id: id.to_string(), value: "test-value".to_string(), name: "test-name".to_string(), @@ -431,7 +431,7 @@ mod tests { let json = repo .serialize_entity(&api_key, |a| &a.id, "apikey") .unwrap(); - let deserialized: ApiKeyModel = repo + let deserialized: ApiKeyRepoModel = repo .deserialize_entity(&json, &api_key.id, "apikey") .unwrap(); diff --git a/src/repositories/api_key/mod.rs b/src/repositories/api_key/mod.rs index e95c4e028..dcd2b317d 100644 --- a/src/repositories/api_key/mod.rs +++ b/src/repositories/api_key/mod.rs @@ -29,7 +29,7 @@ pub use api_key_redis::*; use mockall::automock; use crate::{ - models::{ApiKeyModel, PaginationQuery, RepositoryError}, + models::{ApiKeyRepoModel, PaginationQuery, RepositoryError}, repositories::PaginatedResult, }; @@ -37,12 +37,12 @@ use crate::{ #[allow(dead_code)] #[cfg_attr(test, automock)] pub trait ApiKeyRepositoryTrait { - async fn get_by_id(&self, id: &str) -> Result, RepositoryError>; - async fn create(&self, api_key: ApiKeyModel) -> Result; + async fn get_by_id(&self, id: &str) -> Result, RepositoryError>; + async fn create(&self, api_key: ApiKeyRepoModel) -> Result; async fn list_paginated( &self, query: PaginationQuery, - ) -> Result, RepositoryError>; + ) -> Result, RepositoryError>; async fn count(&self) -> Result; async fn list_permissions(&self, api_key_id: &str) -> Result, RepositoryError>; async fn delete_by_id(&self, api_key_id: &str) -> Result<(), RepositoryError>; @@ -73,14 +73,14 @@ impl ApiKeyRepositoryStorage { #[async_trait] impl ApiKeyRepositoryTrait for ApiKeyRepositoryStorage { - async fn get_by_id(&self, id: &str) -> Result, RepositoryError> { + async fn get_by_id(&self, id: &str) -> Result, RepositoryError> { match self { ApiKeyRepositoryStorage::InMemory(repo) => repo.get_by_id(id).await, ApiKeyRepositoryStorage::Redis(repo) => repo.get_by_id(id).await, } } - async fn create(&self, api_key: ApiKeyModel) -> Result { + async fn create(&self, api_key: ApiKeyRepoModel) -> Result { match self { ApiKeyRepositoryStorage::InMemory(repo) => repo.create(api_key).await, ApiKeyRepositoryStorage::Redis(repo) => repo.create(api_key).await, @@ -104,7 +104,7 @@ impl ApiKeyRepositoryTrait for ApiKeyRepositoryStorage { async fn list_paginated( &self, query: PaginationQuery, - ) -> Result, RepositoryError> { + ) -> Result, RepositoryError> { match self { ApiKeyRepositoryStorage::InMemory(repo) => repo.list_paginated(query).await, ApiKeyRepositoryStorage::Redis(repo) => repo.list_paginated(query).await, @@ -133,7 +133,7 @@ impl ApiKeyRepositoryTrait for ApiKeyRepositoryStorage { } } -impl PartialEq for ApiKeyModel { +impl PartialEq for ApiKeyRepoModel { fn eq(&self, other: &Self) -> bool { self.id == other.id && self.name == other.name @@ -155,8 +155,8 @@ mod tests { value: &str, allowed_origins: &[&str], permissions: &[&str], - ) -> ApiKeyModel { - ApiKeyModel { + ) -> ApiKeyRepoModel { + ApiKeyRepoModel { id: id.to_string(), name: name.to_string(), value: value.to_string(), diff --git a/src/utils/mocks.rs b/src/utils/mocks.rs index 06000f07c..87276505d 100644 --- a/src/utils/mocks.rs +++ b/src/utils/mocks.rs @@ -13,7 +13,7 @@ pub mod mockutils { }, jobs::MockJobProducerTrait, models::{ - ApiKeyModel, AppState, EvmTransactionData, EvmTransactionRequest, + ApiKeyRepoModel, AppState, EvmTransactionData, EvmTransactionRequest, LocalSignerConfigStorage, NetworkConfigData, NetworkRepoModel, NetworkTransactionData, NetworkType, NotificationRepoModel, PluginModel, RelayerEvmPolicy, RelayerNetworkPolicy, RelayerRepoModel, RelayerSolanaPolicy, SecretString, @@ -174,7 +174,7 @@ pub mod mockutils { } pub async fn create_mock_app_state( - api_keys: Option>, + api_keys: Option>, relayers: Option>, signers: Option>, networks: Option>, @@ -284,8 +284,8 @@ pub mod mockutils { } } - pub fn create_mock_api_key() -> ApiKeyModel { - ApiKeyModel { + pub fn create_mock_api_key() -> ApiKeyRepoModel { + ApiKeyRepoModel { id: "test-api-key".to_string(), name: "test-name".to_string(), value: "test-value".to_string(), From 87479941f230fa840a403b40f4d8809c33cddca7 Mon Sep 17 00:00:00 2001 From: Marcos Date: Thu, 28 Aug 2025 10:49:21 -0300 Subject: [PATCH 16/22] fix: Remove feature flag + use SecureString --- Cargo.toml | 3 --- src/api/controllers/api_key.rs | 21 ++++++------------- src/api/routes/api_keys.rs | 7 ------- src/api/routes/mod.rs | 6 ++---- src/api/routes/relayer.rs | 6 +++--- src/models/api_key/repository.rs | 3 ++- src/repositories/api_key/api_key_in_memory.rs | 18 +++++++++------- src/repositories/api_key/api_key_redis.rs | 4 +++- src/repositories/api_key/mod.rs | 4 +++- src/utils/mocks.rs | 2 +- 10 files changed, 30 insertions(+), 44 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d8e52c99e..f87ae531d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -127,6 +127,3 @@ path = "helpers/generate_openapi.rs" [lib] path = "src/lib.rs" - -[features] -authV2 = [] diff --git a/src/api/controllers/api_key.rs b/src/api/controllers/api_key.rs index 7b2ddbdb8..1eb7376b0 100644 --- a/src/api/controllers/api_key.rs +++ b/src/api/controllers/api_key.rs @@ -4,26 +4,21 @@ //! - Create api keys //! - List api keys //! - Delete api keys -#[cfg(feature = "authV2")] use crate::{ jobs::JobProducerTrait, models::{ - ApiError, ApiKeyModel, ApiKeyRequest, ApiKeyResponse, ApiResponse, NetworkRepoModel, - NotificationRepoModel, PaginationMeta, PaginationQuery, RelayerRepoModel, SignerRepoModel, - ThinDataAppState, TransactionRepoModel, + ApiError, ApiKeyRepoModel, ApiKeyRequest, ApiKeyResponse, ApiResponse, NetworkRepoModel, + NotificationRepoModel, PaginationMeta, PaginationQuery, RelayerRepoModel, SecretString, + SignerRepoModel, ThinDataAppState, TransactionRepoModel, }, repositories::{ ApiKeyRepositoryTrait, NetworkRepository, PluginRepositoryTrait, RelayerRepository, Repository, TransactionCounterTrait, TransactionRepository, }, }; -#[cfg(feature = "authV2")] use actix_web::HttpResponse; -#[cfg(feature = "authV2")] use chrono::Utc; -#[cfg(feature = "authV2")] use eyre::Result; -#[cfg(feature = "authV2")] use uuid::Uuid; /// Create api key @@ -39,7 +34,6 @@ use uuid::Uuid; /// # Returns /// /// The result of the plugin call. -#[cfg(feature = "authV2")] pub async fn create_api_key( api_key_request: ApiKeyRequest, state: ThinDataAppState, @@ -55,9 +49,9 @@ where PR: PluginRepositoryTrait + Send + Sync + 'static, AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, { - let api_key = ApiKeyModel { + let api_key = ApiKeyRepoModel { id: Uuid::new_v4().to_string(), - value: Uuid::new_v4().to_string(), + value: SecretString::new(&Uuid::new_v4().to_string()), name: api_key_request.name, allowed_origins: api_key_request .allowed_origins @@ -83,7 +77,6 @@ where /// # Returns /// /// The result of the api key list. -#[cfg(feature = "authV2")] pub async fn list_api_keys( query: PaginationQuery, state: ThinDataAppState, @@ -101,7 +94,7 @@ where { let api_keys = state.api_key_repository.list_paginated(query).await?; - let api_key_items: Vec = api_keys.items.into_iter().collect(); + let api_key_items: Vec = api_keys.items.into_iter().collect(); // Subtract the "value" from the api key to avoid exposing it. let api_key_items: Vec = api_key_items @@ -132,7 +125,6 @@ where /// * `api_key_id` - The id of the api key. /// * `state` - The application state containing the api key repository. /// -#[cfg(feature = "authV2")] pub async fn get_api_key_permissions( api_key_id: String, state: ThinDataAppState, @@ -165,7 +157,6 @@ where /// /// If the API key is the last Admin API key in the system, it will return an error. /// -#[cfg(feature = "authV2")] pub async fn delete_api_key( api_key_id: String, state: ThinDataAppState, diff --git a/src/api/routes/api_keys.rs b/src/api/routes/api_keys.rs index c162302f9..50c8db042 100644 --- a/src/api/routes/api_keys.rs +++ b/src/api/routes/api_keys.rs @@ -1,15 +1,12 @@ //! This module defines the HTTP routes for api keys operations. //! The routes are integrated with the Actix-web framework and interact with the api key controller. -#[cfg(feature = "authV2")] use crate::{ api::controllers::api_key, models::{ApiKeyRequest, DefaultAppState, PaginationQuery}, }; -#[cfg(feature = "authV2")] use actix_web::{delete, get, post, web, Responder}; /// List plugins -#[cfg(feature = "authV2")] #[get("/api-keys")] async fn list_api_keys( query: web::Query, @@ -18,7 +15,6 @@ async fn list_api_keys( api_key::list_api_keys(query.into_inner(), data).await } -#[cfg(feature = "authV2")] #[get("/api-keys/{api_key_id}/permissions")] async fn get_api_key_permissions( api_key_id: web::Path, @@ -28,7 +24,6 @@ async fn get_api_key_permissions( } /// Calls a plugin method. -#[cfg(feature = "authV2")] #[post("/api-keys")] async fn create_api_key( req: web::Json, @@ -37,7 +32,6 @@ async fn create_api_key( api_key::create_api_key(req.into_inner(), data).await } -#[cfg(feature = "authV2")] #[delete("/api-keys/{api_key_id}")] async fn delete_api_key( api_key_id: web::Path, @@ -47,7 +41,6 @@ async fn delete_api_key( } /// Initializes the routes for api keys. -#[cfg(feature = "authV2")] pub fn init(cfg: &mut web::ServiceConfig) { // Register routes with literal segments before routes with path parameters cfg.service(create_api_key); // /api-keys diff --git a/src/api/routes/mod.rs b/src/api/routes/mod.rs index 405bc7359..bdd883aa6 100644 --- a/src/api/routes/mod.rs +++ b/src/api/routes/mod.rs @@ -25,8 +25,6 @@ pub fn configure_routes(cfg: &mut web::ServiceConfig) { .configure(plugin::init) .configure(metrics::init) .configure(notification::init) - .configure(signer::init); - - #[cfg(feature = "authV2")] - cfg.configure(api_keys::init); + .configure(signer::init) + .configure(api_keys::init); } diff --git a/src/api/routes/relayer.rs b/src/api/routes/relayer.rs index 78b992345..90c235d0f 100644 --- a/src/api/routes/relayer.rs +++ b/src/api/routes/relayer.rs @@ -224,8 +224,8 @@ mod tests { models::{ ApiKeyRepoModel, AppState, EvmTransactionData, LocalSignerConfigStorage, NetworkConfigData, NetworkRepoModel, NetworkTransactionData, NetworkType, - RelayerEvmPolicy, RelayerNetworkPolicy, RelayerRepoModel, SignerConfigStorage, - SignerRepoModel, TransactionRepoModel, TransactionStatus, U256, + RelayerEvmPolicy, RelayerNetworkPolicy, RelayerRepoModel, SecretString, + SignerConfigStorage, SignerRepoModel, TransactionRepoModel, TransactionStatus, U256, }, repositories::{ ApiKeyRepositoryStorage, ApiKeyRepositoryTrait, NetworkRepositoryStorage, @@ -344,7 +344,7 @@ mod tests { let test_api_key = ApiKeyRepoModel { id: "test-api-key".to_string(), name: "Test API Key".to_string(), - value: "test-value".to_string(), + value: SecretString::new("test-value"), permissions: vec!["test-permission".to_string()], created_at: chrono::Utc::now().to_rfc3339(), allowed_origins: vec!["*".to_string()], diff --git a/src/models/api_key/repository.rs b/src/models/api_key/repository.rs index 35f07e251..9172e94a4 100644 --- a/src/models/api_key/repository.rs +++ b/src/models/api_key/repository.rs @@ -1,9 +1,10 @@ +use crate::models::SecretString; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug, Clone)] pub struct ApiKeyRepoModel { pub id: String, - pub value: String, + pub value: SecretString, pub name: String, pub allowed_origins: Vec, pub created_at: String, diff --git a/src/repositories/api_key/api_key_in_memory.rs b/src/repositories/api_key/api_key_in_memory.rs index 95cd706db..0f0038f89 100644 --- a/src/repositories/api_key/api_key_in_memory.rs +++ b/src/repositories/api_key/api_key_in_memory.rs @@ -127,6 +127,8 @@ mod tests { use chrono::Utc; use std::sync::Arc; + use crate::models::SecretString; + use super::*; #[tokio::test] @@ -136,7 +138,7 @@ mod tests { // Test add and get_by_id let api_key = ApiKeyRepoModel { id: "test-api-key".to_string(), - value: "test-value".to_string(), + value: SecretString::new("test-value"), name: "test-name".to_string(), allowed_origins: vec!["*".to_string()], permissions: vec!["relayer:all:execute".to_string()], @@ -163,7 +165,7 @@ mod tests { let api_key = ApiKeyRepoModel { id: "test-api-key".to_string(), - value: "test-value".to_string(), + value: SecretString::new("test-value"), name: "test-name".to_string(), allowed_origins: vec!["*".to_string()], permissions: vec!["relayer:all:execute".to_string()], @@ -182,7 +184,7 @@ mod tests { let api_key1 = ApiKeyRepoModel { id: "test-api-key1".to_string(), - value: "test-value1".to_string(), + value: SecretString::new("test-value1"), name: "test-name1".to_string(), allowed_origins: vec!["*".to_string()], permissions: vec!["relayer:all:execute".to_string()], @@ -191,7 +193,7 @@ mod tests { let api_key2 = ApiKeyRepoModel { id: "test-api-key2".to_string(), - value: "test-value2".to_string(), + value: SecretString::new("test-value2"), name: "test-name2".to_string(), allowed_origins: vec!["*".to_string()], permissions: vec!["relayer:all:execute".to_string()], @@ -219,7 +221,7 @@ mod tests { api_key_repository .create(ApiKeyRepoModel { id: "test-api-key".to_string(), - value: "test-value".to_string(), + value: SecretString::new("test-value"), name: "test-name".to_string(), allowed_origins: vec!["*".to_string()], permissions: vec!["relayer:all:execute".to_string()], @@ -239,7 +241,7 @@ mod tests { api_key_repository .create(ApiKeyRepoModel { id: "test-api-key".to_string(), - value: "test-value".to_string(), + value: SecretString::new("test-value"), name: "test-name".to_string(), allowed_origins: vec!["*".to_string()], permissions: vec!["relayer:all:execute".to_string()], @@ -262,7 +264,7 @@ mod tests { api_key_repository .create(ApiKeyRepoModel { id: "test-api-key".to_string(), - value: "test-value".to_string(), + value: SecretString::new("test-value"), name: "test-name".to_string(), allowed_origins: vec!["*".to_string()], permissions: vec![ @@ -287,7 +289,7 @@ mod tests { api_key_repository .create(ApiKeyRepoModel { id: "test-api-key".to_string(), - value: "test-value".to_string(), + value: SecretString::new("test-value"), name: "test-name".to_string(), allowed_origins: vec!["*".to_string()], permissions: vec!["relayer:all:execute".to_string()], diff --git a/src/repositories/api_key/api_key_redis.rs b/src/repositories/api_key/api_key_redis.rs index 68de2392c..c480e2a97 100644 --- a/src/repositories/api_key/api_key_redis.rs +++ b/src/repositories/api_key/api_key_redis.rs @@ -354,13 +354,15 @@ impl ApiKeyRepositoryTrait for RedisApiKeyRepository { #[cfg(test)] mod tests { + use crate::models::SecretString; + use super::*; use chrono::Utc; fn create_test_api_key(id: &str) -> ApiKeyRepoModel { ApiKeyRepoModel { id: id.to_string(), - value: "test-value".to_string(), + value: SecretString::new("test-value"), name: "test-name".to_string(), allowed_origins: vec!["*".to_string()], permissions: vec!["relayer:all:execute".to_string()], diff --git a/src/repositories/api_key/mod.rs b/src/repositories/api_key/mod.rs index dcd2b317d..492c1c70e 100644 --- a/src/repositories/api_key/mod.rs +++ b/src/repositories/api_key/mod.rs @@ -144,6 +144,8 @@ impl PartialEq for ApiKeyRepoModel { #[cfg(test)] mod tests { + use crate::models::SecretString; + use super::*; use chrono::Utc; @@ -159,7 +161,7 @@ mod tests { ApiKeyRepoModel { id: id.to_string(), name: name.to_string(), - value: value.to_string(), + value: SecretString::new(value), allowed_origins: allowed_origins.iter().map(|s| s.to_string()).collect(), permissions: permissions.iter().map(|s| s.to_string()).collect(), created_at: Utc::now().to_string(), diff --git a/src/utils/mocks.rs b/src/utils/mocks.rs index 87276505d..683c039b6 100644 --- a/src/utils/mocks.rs +++ b/src/utils/mocks.rs @@ -288,7 +288,7 @@ pub mod mockutils { ApiKeyRepoModel { id: "test-api-key".to_string(), name: "test-name".to_string(), - value: "test-value".to_string(), + value: SecretString::new("test-value"), allowed_origins: vec!["*".to_string()], permissions: vec!["relayer:all:execute".to_string()], created_at: Utc::now().to_string(), From d616cadb7122ef3f84e89617a608c59b734ab702 Mon Sep 17 00:00:00 2001 From: Marcos Date: Thu, 28 Aug 2025 10:57:21 -0300 Subject: [PATCH 17/22] feat: Serialize and deserialize secret streing --- src/models/api_key/repository.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/models/api_key/repository.rs b/src/models/api_key/repository.rs index 9172e94a4..9285d76cb 100644 --- a/src/models/api_key/repository.rs +++ b/src/models/api_key/repository.rs @@ -1,9 +1,16 @@ -use crate::models::SecretString; +use crate::{ + models::SecretString, + utils::{deserialize_secret_string, serialize_secret_string}, +}; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug, Clone)] pub struct ApiKeyRepoModel { pub id: String, + #[serde( + serialize_with = "serialize_secret_string", + deserialize_with = "deserialize_secret_string" + )] pub value: SecretString, pub name: String, pub allowed_origins: Vec, From b8b6567a134539e4c7ee2420d7f123c718f6a967 Mon Sep 17 00:00:00 2001 From: Marcos Date: Thu, 28 Aug 2025 11:25:39 -0300 Subject: [PATCH 18/22] refactor: Use tryfrom for request handling --- src/api/controllers/api_key.rs | 27 +++++---------------------- src/models/api_key/repository.rs | 21 ++++++++++++++++++++- src/models/api_key/response.rs | 15 +++++++++++++++ 3 files changed, 40 insertions(+), 23 deletions(-) diff --git a/src/api/controllers/api_key.rs b/src/api/controllers/api_key.rs index 1eb7376b0..e4da4ac62 100644 --- a/src/api/controllers/api_key.rs +++ b/src/api/controllers/api_key.rs @@ -8,8 +8,8 @@ use crate::{ jobs::JobProducerTrait, models::{ ApiError, ApiKeyRepoModel, ApiKeyRequest, ApiKeyResponse, ApiResponse, NetworkRepoModel, - NotificationRepoModel, PaginationMeta, PaginationQuery, RelayerRepoModel, SecretString, - SignerRepoModel, ThinDataAppState, TransactionRepoModel, + NotificationRepoModel, PaginationMeta, PaginationQuery, RelayerRepoModel, SignerRepoModel, + ThinDataAppState, TransactionRepoModel, }, repositories::{ ApiKeyRepositoryTrait, NetworkRepository, PluginRepositoryTrait, RelayerRepository, @@ -17,9 +17,7 @@ use crate::{ }, }; use actix_web::HttpResponse; -use chrono::Utc; use eyre::Result; -use uuid::Uuid; /// Create api key /// @@ -49,16 +47,7 @@ where PR: PluginRepositoryTrait + Send + Sync + 'static, AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, { - let api_key = ApiKeyRepoModel { - id: Uuid::new_v4().to_string(), - value: SecretString::new(&Uuid::new_v4().to_string()), - name: api_key_request.name, - allowed_origins: api_key_request - .allowed_origins - .unwrap_or(vec!["*".to_string()]), - permissions: api_key_request.permissions, - created_at: Utc::now().to_string(), - }; + let api_key = ApiKeyRepoModel::try_from(api_key_request)?; let api_key = state.api_key_repository.create(api_key).await?; @@ -99,14 +88,8 @@ where // Subtract the "value" from the api key to avoid exposing it. let api_key_items: Vec = api_key_items .into_iter() - .map(|api_key| ApiKeyResponse { - id: api_key.id, - name: api_key.name, - allowed_origins: api_key.allowed_origins, - created_at: api_key.created_at, - permissions: api_key.permissions, - }) - .collect(); + .map(ApiKeyResponse::try_from) + .collect::, ApiError>>()?; Ok(HttpResponse::Ok().json(ApiResponse::paginated( api_key_items, diff --git a/src/models/api_key/repository.rs b/src/models/api_key/repository.rs index 9285d76cb..a4a266a1c 100644 --- a/src/models/api_key/repository.rs +++ b/src/models/api_key/repository.rs @@ -1,8 +1,10 @@ use crate::{ - models::SecretString, + models::{ApiError, ApiKeyRequest, SecretString}, utils::{deserialize_secret_string, serialize_secret_string}, }; +use chrono::Utc; use serde::{Deserialize, Serialize}; +use uuid::Uuid; #[derive(Serialize, Deserialize, Debug, Clone)] pub struct ApiKeyRepoModel { @@ -17,3 +19,20 @@ pub struct ApiKeyRepoModel { pub created_at: String, pub permissions: Vec, } + +impl TryFrom for ApiKeyRepoModel { + type Error = ApiError; + + fn try_from(request: ApiKeyRequest) -> Result { + let allowed_origins = request.allowed_origins.unwrap_or(vec!["*".to_string()]); + + Ok(Self { + id: Uuid::new_v4().to_string(), + value: SecretString::new(&Uuid::new_v4().to_string()), + name: request.name, + permissions: request.permissions, + allowed_origins, + created_at: Utc::now().to_string(), + }) + } +} diff --git a/src/models/api_key/response.rs b/src/models/api_key/response.rs index 5b0dba330..bad7367cb 100644 --- a/src/models/api_key/response.rs +++ b/src/models/api_key/response.rs @@ -1,3 +1,4 @@ +use crate::models::{ApiError, ApiKeyRepoModel}; use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug, Clone)] @@ -8,3 +9,17 @@ pub struct ApiKeyResponse { pub created_at: String, pub permissions: Vec, } + +impl TryFrom for ApiKeyResponse { + type Error = ApiError; + + fn try_from(api_key: ApiKeyRepoModel) -> Result { + Ok(ApiKeyResponse { + id: api_key.id, + name: api_key.name, + allowed_origins: api_key.allowed_origins, + created_at: api_key.created_at, + permissions: api_key.permissions, + }) + } +} From b2bf823a9d1196b4840bd053e64aa79f60513c8a Mon Sep 17 00:00:00 2001 From: Marcos Date: Mon, 1 Sep 2025 10:49:45 -0300 Subject: [PATCH 19/22] fix: Coderabbit fixes --- src/api/controllers/api_key.rs | 4 +--- src/api/routes/api_keys.rs | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/api/controllers/api_key.rs b/src/api/controllers/api_key.rs index e4da4ac62..d5177f444 100644 --- a/src/api/controllers/api_key.rs +++ b/src/api/controllers/api_key.rs @@ -51,7 +51,7 @@ where let api_key = state.api_key_repository.create(api_key).await?; - Ok(HttpResponse::Ok().json(ApiResponse::success(api_key))) + Ok(HttpResponse::Created().json(ApiResponse::success(api_key))) } /// List api keys @@ -138,8 +138,6 @@ where /// * `api_key_id` - The id of the api key. /// * `state` - The application state containing the api key repository. /// -/// If the API key is the last Admin API key in the system, it will return an error. -/// pub async fn delete_api_key( api_key_id: String, state: ThinDataAppState, diff --git a/src/api/routes/api_keys.rs b/src/api/routes/api_keys.rs index 50c8db042..d9c1076b4 100644 --- a/src/api/routes/api_keys.rs +++ b/src/api/routes/api_keys.rs @@ -6,7 +6,7 @@ use crate::{ }; use actix_web::{delete, get, post, web, Responder}; -/// List plugins +/// List API keys #[get("/api-keys")] async fn list_api_keys( query: web::Query, @@ -23,7 +23,7 @@ async fn get_api_key_permissions( api_key::get_api_key_permissions(api_key_id.into_inner(), data).await } -/// Calls a plugin method. +/// Create a new API key #[post("/api-keys")] async fn create_api_key( req: web::Json, From d721c150c430f1b578d0bb50b4ecbf10842e7ef6 Mon Sep 17 00:00:00 2001 From: Marcos Date: Wed, 3 Sep 2025 10:05:18 -0300 Subject: [PATCH 20/22] feat: Load default key in repo --- src/bootstrap/config_processor.rs | 68 ++++++++++++++++++++++++++++--- src/models/api_key/repository.rs | 56 +++++++++++++++++++++++++ 2 files changed, 119 insertions(+), 5 deletions(-) diff --git a/src/bootstrap/config_processor.rs b/src/bootstrap/config_processor.rs index daeca229e..d44df6c1a 100644 --- a/src/bootstrap/config_processor.rs +++ b/src/bootstrap/config_processor.rs @@ -6,9 +6,9 @@ use crate::{ config::{Config, RepositoryStorageType, ServerConfig}, jobs::JobProducerTrait, models::{ - NetworkRepoModel, NotificationRepoModel, PluginModel, Relayer, RelayerRepoModel, - Signer as SignerDomainModel, SignerFileConfig, SignerRepoModel, ThinDataAppState, - TransactionRepoModel, + ApiKeyRepoModel, NetworkRepoModel, NotificationRepoModel, PluginModel, Relayer, + RelayerRepoModel, Signer as SignerDomainModel, SignerFileConfig, SignerRepoModel, + ThinDataAppState, TransactionRepoModel, }, repositories::{ ApiKeyRepositoryTrait, NetworkRepository, PluginRepositoryTrait, RelayerRepository, @@ -20,6 +20,37 @@ use color_eyre::{eyre::WrapErr, Report, Result}; use futures::future::try_join_all; use log::info; +async fn process_api_key( + server_config: &ServerConfig, + app_state: &ThinDataAppState, +) -> Result<()> +where + J: JobProducerTrait + Send + Sync + 'static, + RR: RelayerRepository + Repository + Send + Sync + 'static, + TR: TransactionRepository + Repository + Send + Sync + 'static, + NR: NetworkRepository + Repository + Send + Sync + 'static, + NFR: Repository + Send + Sync + 'static, + SR: Repository + Send + Sync + 'static, + TCR: TransactionCounterTrait + Send + Sync + 'static, + PR: PluginRepositoryTrait + Send + Sync + 'static, + AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, +{ + let api_key_model = ApiKeyRepoModel::new( + "default".to_string(), + server_config.api_key.clone(), + vec!["*".to_string()], + vec!["*".to_string()], + ); + + app_state + .api_key_repository + .create(api_key_model) + .await + .wrap_err("Failed to create api key repository entry")?; + + Ok(()) +} + /// Process all plugins from the config file and store them in the repository. async fn process_plugins( config_file: &Config, @@ -337,6 +368,7 @@ where app_state.notification_repository.drop_all_entries().await?; app_state.network_repository.drop_all_entries().await?; app_state.plugin_repository.drop_all_entries().await?; + app_state.api_key_repository.drop_all_entries().await?; } if should_process_config_file { @@ -346,6 +378,7 @@ where process_notifications(&config_file, app_state).await?; process_networks(&config_file, app_state).await?; process_relayers(&config_file, app_state).await?; + process_api_key(&server_config, app_state).await?; } Ok(()) } @@ -361,8 +394,9 @@ mod tests { relayer::RelayerFileConfig, AppState, AwsKmsSignerFileConfig, GoogleCloudKmsKeyFileConfig, GoogleCloudKmsServiceAccountFileConfig, GoogleCloudKmsSignerFileConfig, LocalSignerFileConfig, NetworkType, NotificationConfig, - NotificationType, PlainOrEnvValue, SecretString, SignerConfigStorage, SignerFileConfig, - SignerFileConfigEnum, VaultSignerFileConfig, VaultTransitSignerFileConfig, + NotificationType, PaginationQuery, PlainOrEnvValue, SecretString, SignerConfigStorage, + SignerFileConfig, SignerFileConfigEnum, VaultSignerFileConfig, + VaultTransitSignerFileConfig, }, repositories::{ ApiKeyRepositoryStorage, InMemoryApiKeyRepository, InMemoryNetworkRepository, @@ -1090,6 +1124,30 @@ mod tests { Ok(()) } + #[tokio::test] + async fn test_process_api_key() -> Result<()> { + let server_config = Arc::new(crate::utils::mocks::mockutils::create_test_server_config( + RepositoryStorageType::InMemory, + )); + let app_state = ThinData(create_test_app_state()); + + process_api_key(&server_config, &app_state).await?; + + let pagination_query = PaginationQuery { + page: 1, + per_page: 10, + }; + + let stored_api_keys = app_state + .api_key_repository + .list_paginated(pagination_query) + .await?; + assert_eq!(stored_api_keys.items.len(), 1); + assert_eq!(stored_api_keys.items[0].name, "default"); + + Ok(()) + } + #[tokio::test] async fn test_process_config_file() -> Result<()> { // Create test signers, relayers, and notifications diff --git a/src/models/api_key/repository.rs b/src/models/api_key/repository.rs index a4a266a1c..afc6e3e3a 100644 --- a/src/models/api_key/repository.rs +++ b/src/models/api_key/repository.rs @@ -20,6 +20,24 @@ pub struct ApiKeyRepoModel { pub permissions: Vec, } +impl ApiKeyRepoModel { + pub fn new( + name: String, + value: SecretString, + permissions: Vec, + allowed_origins: Vec, + ) -> Self { + Self { + id: Uuid::new_v4().to_string(), + value, + name, + permissions, + allowed_origins, + created_at: Utc::now().to_string(), + } + } +} + impl TryFrom for ApiKeyRepoModel { type Error = ApiError; @@ -36,3 +54,41 @@ impl TryFrom for ApiKeyRepoModel { }) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_api_key_repo_model_new() { + let api_key_repo_model = ApiKeyRepoModel::new( + "test-name".to_string(), + SecretString::new("test-value"), + vec!["relayer:all:execute".to_string()], + vec!["*".to_string()], + ); + assert_eq!(api_key_repo_model.name, "test-name"); + assert_eq!(api_key_repo_model.value, SecretString::new("test-value")); + assert_eq!( + api_key_repo_model.permissions, + vec!["relayer:all:execute".to_string()] + ); + assert_eq!(api_key_repo_model.allowed_origins, vec!["*".to_string()]); + } + + #[test] + fn test_api_key_repo_model_try_from() { + let api_key_request = ApiKeyRequest { + name: "test-name".to_string(), + permissions: vec!["relayer:all:execute".to_string()], + allowed_origins: Some(vec!["*".to_string()]), + }; + let api_key_repo_model = ApiKeyRepoModel::try_from(api_key_request).unwrap(); + assert_eq!(api_key_repo_model.name, "test-name"); + assert_eq!( + api_key_repo_model.permissions, + vec!["relayer:all:execute".to_string()] + ); + assert_eq!(api_key_repo_model.allowed_origins, vec!["*".to_string()]); + } +} From 9291ebfd60def3a5554ccda6461f95fc6477a016 Mon Sep 17 00:00:00 2001 From: Marcos Date: Mon, 22 Sep 2025 17:15:00 -0300 Subject: [PATCH 21/22] test: Add routes tests --- src/api/controllers/api_key.rs | 112 ++++++++++++++++++++++++++++++++- src/api/routes/api_keys.rs | 88 +++++++++++++++++++++++++- 2 files changed, 198 insertions(+), 2 deletions(-) diff --git a/src/api/controllers/api_key.rs b/src/api/controllers/api_key.rs index d5177f444..d34fabad3 100644 --- a/src/api/controllers/api_key.rs +++ b/src/api/controllers/api_key.rs @@ -159,4 +159,114 @@ where } #[cfg(test)] -mod tests {} +mod tests { + use super::*; + use crate::{ + models::{ApiKeyRepoModel, PaginationQuery, SecretString}, + utils::mocks::mockutils::create_mock_app_state, + }; + use actix_web::web::ThinData; + + /// Helper function to create a test api key model + fn create_test_api_key_model(id: &str) -> ApiKeyRepoModel { + ApiKeyRepoModel { + id: id.to_string(), + value: SecretString::new("test-api-key-value"), + name: "Test API Key".to_string(), + allowed_origins: vec!["*".to_string()], + permissions: vec!["relayer:all:execute".to_string()], + created_at: "2023-01-01T00:00:00Z".to_string(), + } + } + + /// Helper function to create a test api key create request + fn create_test_api_key_create_request(name: &str) -> ApiKeyRequest { + ApiKeyRequest { + name: name.to_string(), + permissions: vec!["relayer:all:execute".to_string()], + allowed_origins: Some(vec!["*".to_string()]), + } + } + + #[actix_web::test] + async fn test_create_api_key() { + let app_state = create_mock_app_state(None, None, None, None, None, None).await; + let api_key_request = create_test_api_key_create_request("Test API Key"); + + let result = create_api_key(api_key_request, ThinData(app_state)).await; + + assert!(result.is_ok()); + let response = result.unwrap(); + assert_eq!(response.status(), 201); + } + + #[actix_web::test] + async fn test_list_api_keys_empty() { + let app_state = create_mock_app_state(None, None, None, None, None, None).await; + let query = PaginationQuery { + page: 1, + per_page: 10, + }; + + let result = list_api_keys(query, ThinData(app_state)).await; + + assert!(result.is_ok()); + let response = result.unwrap(); + assert_eq!(response.status(), 200); + } + + #[actix_web::test] + async fn test_list_api_keys_with_data() { + let api_key = create_test_api_key_model("test-api-key-1"); + let app_state = + create_mock_app_state(Some(vec![api_key]), None, None, None, None, None).await; + let query = PaginationQuery { + page: 1, + per_page: 10, + }; + + let result = list_api_keys(query, ThinData(app_state)).await; + + assert!(result.is_ok()); + let response = result.unwrap(); + assert_eq!(response.status(), 200); + } + + #[actix_web::test] + async fn test_get_api_key_permissions() { + let api_key = create_test_api_key_model("test-api-key-1"); + let api_key_id = api_key.id.clone(); + let app_state = + create_mock_app_state(Some(vec![api_key]), None, None, None, None, None).await; + + let result = get_api_key_permissions(api_key_id, ThinData(app_state)).await; + + assert!(result.is_ok()); + let response = result.unwrap(); + assert_eq!(response.status(), 200); + } + + #[actix_web::test] + async fn test_delete_api_key() { + let api_key = create_test_api_key_model("test-api-key-1"); + let api_key_id = api_key.id.clone(); + let app_state = + create_mock_app_state(Some(vec![api_key]), None, None, None, None, None).await; + + let result = delete_api_key(api_key_id, ThinData(app_state)).await; + + assert!(result.is_ok()); + let response = result.unwrap(); + assert_eq!(response.status(), 200); + } + + #[actix_web::test] + async fn test_get_permissions_nonexistent_api_key() { + let app_state = create_mock_app_state(None, None, None, None, None, None).await; + + let result = + get_api_key_permissions("nonexistent-id".to_string(), ThinData(app_state)).await; + + assert!(result.is_err()); + } +} diff --git a/src/api/routes/api_keys.rs b/src/api/routes/api_keys.rs index d9c1076b4..d518a5f56 100644 --- a/src/api/routes/api_keys.rs +++ b/src/api/routes/api_keys.rs @@ -50,4 +50,90 @@ pub fn init(cfg: &mut web::ServiceConfig) { } #[cfg(test)] -mod tests {} +mod tests { + use super::*; + use crate::utils::mocks::mockutils::create_mock_app_state; + use actix_web::{http::StatusCode, test, web, App}; + + #[actix_web::test] + async fn test_api_key_routes_are_registered() { + // Arrange - Create app with API key routes + let app_state = create_mock_app_state(None, None, None, None, None, None).await; + let app = test::init_service( + App::new() + .app_data(web::Data::new(app_state)) + .configure(init), + ) + .await; + + // Test GET /api-keys - should not return 404 (route exists) + let req = test::TestRequest::get().uri("/api-keys").to_request(); + let resp = test::call_service(&app, req).await; + assert_ne!( + resp.status(), + StatusCode::NOT_FOUND, + "GET /api-keys route not registered" + ); + + // Test POST /api-keys - should not return 404 + let req = test::TestRequest::post() + .uri("/api-keys") + .set_json(serde_json::json!({ + "name": "Test API Key", + "permissions": ["relayer:all:execute"], + "allowed_origins": ["*"] + })) + .to_request(); + let resp = test::call_service(&app, req).await; + assert_ne!( + resp.status(), + StatusCode::NOT_FOUND, + "POST /api-keys route not registered" + ); + + // Test GET /api-keys/{api_key_id}/permissions - should not return 404 + let req = test::TestRequest::get() + .uri("/api-keys/test-id/permissions") + .to_request(); + let resp = test::call_service(&app, req).await; + assert_ne!( + resp.status(), + StatusCode::NOT_FOUND, + "GET /api-keys/{{api_key_id}}/permissions route not registered" + ); + + // Test DELETE /api-keys/{api_key_id} - should not return 404 + let req = test::TestRequest::delete() + .uri("/api-keys/test-id") + .to_request(); + let resp = test::call_service(&app, req).await; + assert_ne!( + resp.status(), + StatusCode::NOT_FOUND, + "DELETE /api-keys/{{api_key_id}} route not registered" + ); + } + + #[actix_web::test] + async fn test_api_key_routes_with_query_params() { + // Arrange - Create app with API key routes + let app_state = create_mock_app_state(None, None, None, None, None, None).await; + let app = test::init_service( + App::new() + .app_data(web::Data::new(app_state)) + .configure(init), + ) + .await; + + // Test GET /api-keys with pagination parameters + let req = test::TestRequest::get() + .uri("/api-keys?page=1&per_page=10") + .to_request(); + let resp = test::call_service(&app, req).await; + assert_ne!( + resp.status(), + StatusCode::NOT_FOUND, + "GET /api-keys with query params route not registered" + ); + } +} From 87f5f5e2794b3b6a0829e90051f9dfbce6d1a884 Mon Sep 17 00:00:00 2001 From: Marcos Date: Tue, 23 Sep 2025 14:42:13 -0300 Subject: [PATCH 22/22] fix: Avoid deleting API keys --- src/api/controllers/api_key.rs | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/api/controllers/api_key.rs b/src/api/controllers/api_key.rs index d34fabad3..4ad1553f3 100644 --- a/src/api/controllers/api_key.rs +++ b/src/api/controllers/api_key.rs @@ -139,8 +139,8 @@ where /// * `state` - The application state containing the api key repository. /// pub async fn delete_api_key( - api_key_id: String, - state: ThinDataAppState, + _api_key_id: String, + _state: ThinDataAppState, ) -> Result where J: JobProducerTrait + Send + Sync + 'static, @@ -153,9 +153,10 @@ where PR: PluginRepositoryTrait + Send + Sync + 'static, AKR: ApiKeyRepositoryTrait + Send + Sync + 'static, { - state.api_key_repository.delete_by_id(&api_key_id).await?; + // state.api_key_repository.delete_by_id(&api_key_id).await?; - Ok(HttpResponse::Ok().json(ApiResponse::success(api_key_id))) + // Ok(HttpResponse::Ok().json(ApiResponse::success(api_key_id))) + Ok(HttpResponse::Ok().json(ApiResponse::::error("Not implemented".to_string()))) } #[cfg(test)] @@ -246,19 +247,19 @@ mod tests { assert_eq!(response.status(), 200); } - #[actix_web::test] - async fn test_delete_api_key() { - let api_key = create_test_api_key_model("test-api-key-1"); - let api_key_id = api_key.id.clone(); - let app_state = - create_mock_app_state(Some(vec![api_key]), None, None, None, None, None).await; + // #[actix_web::test] + // async fn test_delete_api_key() { + // let api_key = create_test_api_key_model("test-api-key-1"); + // let api_key_id = api_key.id.clone(); + // let app_state = + // create_mock_app_state(Some(vec![api_key]), None, None, None, None, None).await; - let result = delete_api_key(api_key_id, ThinData(app_state)).await; + // let result = delete_api_key(api_key_id, ThinData(app_state)).await; - assert!(result.is_ok()); - let response = result.unwrap(); - assert_eq!(response.status(), 200); - } + // assert!(result.is_ok()); + // let response = result.unwrap(); + // assert_eq!(response.status(), 200); + // } #[actix_web::test] async fn test_get_permissions_nonexistent_api_key() {