diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index f17962b3c..38a807bed 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -3,13 +3,19 @@ # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial # Please see LICENSE files in the repository root for full details. + +#:tchap: +# the only job used by tchap are `compute-version` and `build-image` +#:tchap: + name: Build on: push: branches: - - main + - main_tchap - "release/**" + - "test/**" tags: - "v*" @@ -27,8 +33,8 @@ env: CARGO_NET_GIT_FETCH_WITH_CLI: "true" SCCACHE_GHA_ENABLED: "true" RUSTC_WRAPPER: "sccache" - IMAGE: ghcr.io/element-hq/matrix-authentication-service - BUILDCACHE: ghcr.io/element-hq/matrix-authentication-service/buildcache + IMAGE: ghcr.io/tchapgouv/matrix-authentication-service + BUILDCACHE: ghcr.io/tchapgouv/matrix-authentication-service/buildcache DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index jobs: @@ -300,7 +306,7 @@ jobs: # Only sign on tags and on commits on main branch if: | github.event_name != 'pull_request' - && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') + && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main_tchap') env: REGULAR_DIGEST: ${{ steps.output.outputs.metadata && fromJSON(steps.output.outputs.metadata).regular.digest }} @@ -364,7 +370,7 @@ jobs: unstable: name: Update the unstable release - if: github.ref == 'refs/heads/main' + if: github.ref == 'refs/heads/main_tchap' runs-on: ubuntu-24.04 needs: diff --git a/Cargo.lock b/Cargo.lock index 7b6d93227..d6983b160 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3371,6 +3371,7 @@ dependencies = [ "serde_with", "sha2", "sqlx", + "tchap", "thiserror 2.0.16", "tokio", "tokio-util", @@ -6208,6 +6209,22 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" +[[package]] +name = "tchap" +version = "0.1.0" +dependencies = [ + "mas-data-model", + "mas-http", + "mas-storage", + "reqwest", + "serde", + "serde_json", + "tokio", + "tracing", + "url", + "wiremock", +] + [[package]] name = "tempfile" version = "3.20.0" diff --git a/Cargo.toml b/Cargo.toml index 44dc8990f..7bc11afcc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,6 +63,7 @@ mas-templates = { path = "./crates/templates/", version = "=1.2.0" } mas-tower = { path = "./crates/tower/", version = "=1.2.0" } oauth2-types = { path = "./crates/oauth2-types/", version = "=1.2.0" } syn2mas = { path = "./crates/syn2mas", version = "=1.2.0" } +tchap = { path = "./crates/tchap", version = "=0.1.0" } # OpenAPI schema generation and validation [workspace.dependencies.aide] diff --git a/Dockerfile b/Dockerfile index 14eda1924..c345c377d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,6 +9,10 @@ # implicit BUILDARG: BUILDPLATFORM being the host platform and TARGETPLATFORM # being the platform being built. +#:tchap: +# tchap files are added to this image (templates, translations, css) +#:tchap: + # The Debian version and version name must be in sync ARG DEBIAN_VERSION=12 ARG DEBIAN_VERSION_NAME=bookworm @@ -32,7 +36,9 @@ RUN --network=default \ COPY ./frontend/ /app/frontend/ COPY ./templates/ /app/templates/ RUN --network=none \ - npm run build + #:tchap: + npm run build-tchap + #:tchap: # Move the built files RUN --network=none \ @@ -141,6 +147,12 @@ COPY --from=policy /app/policies/policy.wasm /share/policy.wasm COPY ./templates/ /share/templates COPY ./translations/ /share/translations +#:tchap: +COPY ./tchap/resources/templates/ /share/templates/ +COPY ./tchap/resources/translations/ /share/translations/ +#:tchap: + + ################################## ## Runtime stage, debug variant ## ################################## diff --git a/crates/cli/src/app_state.rs b/crates/cli/src/app_state.rs index f9f761338..39c82eadb 100644 --- a/crates/cli/src/app_state.rs +++ b/crates/cli/src/app_state.rs @@ -9,7 +9,14 @@ use std::{convert::Infallible, net::IpAddr, sync::Arc}; use axum::extract::{FromRef, FromRequestParts}; use ipnetwork::IpNetwork; use mas_context::LogContext; -use mas_data_model::{BoxClock, BoxRng, SiteConfig, SystemClock}; +use mas_data_model::{ + BoxClock, + BoxRng, + SiteConfig, + SystemClock, + //:tchap: + TchapConfig, //:tchap:end +}; use mas_handlers::{ ActivityTracker, BoundActivityTracker, CookieManager, ErrorWrapper, GraphQLSchema, Limiter, MetadataCache, RequesterFingerprint, passwords::PasswordManager, @@ -47,6 +54,9 @@ pub struct AppState { pub activity_tracker: ActivityTracker, pub trusted_proxies: Vec, pub limiter: Limiter, + //:tchap: + pub tchap_config: TchapConfig, + //:tchap: end } impl AppState { @@ -214,6 +224,14 @@ impl FromRef for Arc { } } +//:tchap: +impl FromRef for TchapConfig { + fn from_ref(input: &AppState) -> Self { + input.tchap_config.clone() + } +} +//:tchap:end + impl FromRequestParts for BoxClock { type Rejection = Infallible; diff --git a/crates/cli/src/commands/server.rs b/crates/cli/src/commands/server.rs index 1367f131e..e319cad2d 100644 --- a/crates/cli/src/commands/server.rs +++ b/crates/cli/src/commands/server.rs @@ -11,10 +11,24 @@ use clap::Parser; use figment::Figment; use itertools::Itertools; use mas_config::{ - AppConfig, ClientsConfig, ConfigurationSection, ConfigurationSectionExt, UpstreamOAuth2Config, + AppConfig, + ClientsConfig, + ConfigurationSection, + ConfigurationSectionExt, + //:tchap: + TchapAppConfig, + // :tchap: end + UpstreamOAuth2Config, }; use mas_context::LogContext; -use mas_data_model::SystemClock; +use mas_data_model::{ + //:tchap: + EmailLookupFallbackRule, + //:tchap:end + SystemClock, + //:tchap: + TchapConfig, // :tchap: end +}; use mas_handlers::{ActivityTracker, CookieManager, Limiter, MetadataCache}; use mas_listener::server::Server; use mas_router::UrlBuilder; @@ -59,6 +73,10 @@ impl Options { let span = info_span!("cli.run.init").entered(); let mut shutdown = LifecycleManager::new()?; let config = AppConfig::extract(figment).map_err(anyhow::Error::from_boxed)?; + //:tchap: + let tchap_app_config = + TchapAppConfig::extract(figment).map_err(anyhow::Error::from_boxed)?; + //:tchap: end info!(version = crate::VERSION, "Starting up"); @@ -159,6 +177,10 @@ impl Options { &config.captcha, )?; + //:tchap: + let tchap_config = tchap_config_from_tchap_app_config(&tchap_app_config); + //:tchap: end + // Load and compile the templates let templates = templates_from_config(&config.templates, &site_config, &url_builder).await?; @@ -246,6 +268,9 @@ impl Options { activity_tracker, trusted_proxies, limiter, + //:tchap: + tchap_config, + //:tchap:end }; s.init_metrics(); s.init_metadata_cache(); @@ -334,3 +359,19 @@ impl Options { Ok(exit_code) } } + +//:tchap: +fn tchap_config_from_tchap_app_config(tchap_app_config: &TchapAppConfig) -> TchapConfig { + TchapConfig { + identity_server_url: tchap_app_config.identity_server_url.clone(), + email_lookup_fallback_rules: tchap_app_config + .email_lookup_fallback_rules + .iter() + .map(|rule| EmailLookupFallbackRule { + match_with: rule.match_with.clone(), + search: rule.search.clone(), + }) + .collect(), + } +} +//:tchap: end diff --git a/crates/config/src/sections/mod.rs b/crates/config/src/sections/mod.rs index f992d8698..c69c2eeb2 100644 --- a/crates/config/src/sections/mod.rs +++ b/crates/config/src/sections/mod.rs @@ -21,6 +21,9 @@ mod passwords; mod policy; mod rate_limiting; mod secrets; +//:tchap: +mod tchap; +//:tchap:end mod telemetry; mod templates; mod upstream_oauth2; @@ -44,6 +47,9 @@ pub use self::{ policy::PolicyConfig, rate_limiting::RateLimitingConfig, secrets::SecretsConfig, + //:tchap: + tchap::TchapAppConfig, + //:tchap:end telemetry::{ MetricsConfig, MetricsExporterKind, Propagator, TelemetryConfig, TracingConfig, TracingExporterKind, diff --git a/crates/config/src/sections/tchap.rs b/crates/config/src/sections/tchap.rs new file mode 100644 index 000000000..1e2cbb2f1 --- /dev/null +++ b/crates/config/src/sections/tchap.rs @@ -0,0 +1,119 @@ +// +// MIT License +// +// Copyright (c) 2025, Direction interministérielle du numérique - Gouvernement +// Français +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use serde_with::serde_as; +use url::Url; + +use super::ConfigurationSection; + +fn default_identity_server_url() -> Url { + Url::parse("http://localhost:8090/").unwrap() +} + +/// Tchap specific configuration +#[serde_as] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +pub struct TchapAppConfig { + /// Identity Server Url + #[serde(default = "default_identity_server_url")] + pub identity_server_url: Url, + + /// Fallback Rules to use when linking an upstream account + #[serde(default)] + pub email_lookup_fallback_rules: Vec, +} + +/// When linking the localpart, the email can be used to find the correct +/// localpart. By using the fallback rule, we can search for a Matrix account +/// with the `search` email pattern for an upstream account matching with the +/// `match_with` pattern +#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug, Default, JsonSchema)] +pub struct EmailLookupFallbackRule { + /// The upstream email pattern to match with when linking the localpart by + /// email + pub match_with: String, + /// The email pattern to use for the search when linking the localpart by + /// email + pub search: String, +} + +impl ConfigurationSection for TchapAppConfig { + const PATH: Option<&'static str> = Some("tchap"); + + // NOTE: implement this function to perform validation on config + fn validate( + &self, + _figment: &figment::Figment, + ) -> Result<(), Box> { + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use figment::{ + Figment, Jail, + providers::{Format, Yaml}, + }; + + use super::*; + + #[test] + fn load_config() { + Jail::expect_with(|jail| { + jail.create_file( + "config.yaml", + r" + tchap: + identity_server_url: http://localhost:8091 + email_lookup_fallback_rules: + - match_with : '@upstream.domain.tld' + search: '@matrix.domain.tld' + ", + )?; + + let config = Figment::new() + .merge(Yaml::file("config.yaml")) + .extract_inner::("tchap")?; + + assert_eq!( + &config.identity_server_url.as_str().to_owned(), + "http://localhost:8091/" + ); + + assert_eq!( + config.email_lookup_fallback_rules, + vec![EmailLookupFallbackRule { + match_with: "@upstream.domain.tld".to_string(), + search: "@matrix.domain.tld".to_string(), + }] + ); + + Ok(()) + }); + } +} diff --git a/crates/data-model/src/lib.rs b/crates/data-model/src/lib.rs index 6be06b4d9..e16b1dc25 100644 --- a/crates/data-model/src/lib.rs +++ b/crates/data-model/src/lib.rs @@ -13,6 +13,9 @@ pub(crate) mod compat; pub mod oauth2; pub(crate) mod policy_data; mod site_config; +//:tchap: +pub(crate) mod tchap_config; +//:tchap:end pub(crate) mod tokens; pub(crate) mod upstream_oauth2; pub(crate) mod user_agent; @@ -38,6 +41,9 @@ pub use self::{ }, policy_data::PolicyData, site_config::{CaptchaConfig, CaptchaService, SessionExpirationConfig, SiteConfig}, + //:tchap: + tchap_config::*, + //:tchap:end tokens::{ AccessToken, AccessTokenState, RefreshToken, RefreshTokenState, TokenFormatError, TokenType, }, diff --git a/crates/data-model/src/tchap_config.rs b/crates/data-model/src/tchap_config.rs new file mode 100644 index 000000000..bab4e943d --- /dev/null +++ b/crates/data-model/src/tchap_config.rs @@ -0,0 +1,43 @@ +// +// MIT License +// +// Copyright (c) 2025, Direction interministérielle du numérique - Gouvernement +// Français +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +use url::Url; + +/// Random tchap configuration we want accessible in various places. +#[allow(clippy::struct_excessive_bools)] +#[derive(Debug, Clone)] +pub struct TchapConfig { + /// Identity Server Url + pub identity_server_url: Url, + + /// Fallback Rules to use when linking an upstream account + pub email_lookup_fallback_rules: Vec, +} + +#[derive(PartialEq, Eq, Clone, Debug)] +pub struct EmailLookupFallbackRule { + pub match_with: String, + pub search: String, +} diff --git a/crates/handlers/Cargo.toml b/crates/handlers/Cargo.toml index 57f391854..10ae6aba5 100644 --- a/crates/handlers/Cargo.toml +++ b/crates/handlers/Cargo.toml @@ -89,6 +89,8 @@ mas-templates.workspace = true oauth2-types.workspace = true zxcvbn.workspace = true +tchap.workspace = true + [dev-dependencies] insta.workspace = true tracing-subscriber.workspace = true diff --git a/crates/handlers/src/lib.rs b/crates/handlers/src/lib.rs index 65a75f550..a1378aabc 100644 --- a/crates/handlers/src/lib.rs +++ b/crates/handlers/src/lib.rs @@ -36,7 +36,11 @@ use hyper::{ }, }; use mas_axum_utils::{InternalError, cookies::CookieJar}; -use mas_data_model::SiteConfig; +use mas_data_model::{ + SiteConfig, + //:tchap: + TchapConfig, //:tchap:end +}; use mas_http::CorsLayerExt; use mas_keystore::{Encrypter, Keystore}; use mas_matrix::HomeserverConnection; @@ -350,6 +354,9 @@ where BoxClock: FromRequestParts, BoxRng: FromRequestParts, Policy: FromRequestParts, + //:tchap: + TchapConfig: FromRef, + //:tchap:end { Router::new() // XXX: hard-coded redirect from /account to /account/ diff --git a/crates/handlers/src/test_utils.rs b/crates/handlers/src/test_utils.rs index e43194776..15df95bad 100644 --- a/crates/handlers/src/test_utils.rs +++ b/crates/handlers/src/test_utils.rs @@ -28,7 +28,15 @@ use mas_axum_utils::{ cookies::{CookieJar, CookieManager}, }; use mas_config::RateLimitingConfig; -use mas_data_model::{BoxClock, BoxRng, SiteConfig, clock::MockClock}; +use mas_data_model::{ + BoxClock, + BoxRng, + SiteConfig, + //:tchap: + TchapConfig, + //:tchap:end + clock::MockClock, +}; use mas_email::{MailTransport, Mailer}; use mas_i18n::Translator; use mas_keystore::{Encrypter, JsonWebKey, JsonWebKeySet, Keystore, PrivateKey}; @@ -112,6 +120,9 @@ pub(crate) struct TestState { pub rng: Arc>, pub http_client: reqwest::Client, pub task_tracker: TaskTracker, + //:tchap: + pub tchap_config: TchapConfig, + //:tchap:end queue_worker: Arc>, #[allow(dead_code)] // It is used, as it will cancel the CancellationToken when dropped @@ -256,6 +267,10 @@ impl TestState { let queue_worker = Arc::new(tokio::sync::Mutex::new(queue_worker)); + //:tchap: + let tchap_config = tchap::test_tchap_config(); + //:tchap:end + Ok(Self { repository_factory: PgRepositoryFactory::new(pool), templates, @@ -277,6 +292,9 @@ impl TestState { task_tracker, queue_worker, cancellation_drop_guard: Arc::new(shutdown_token.drop_guard()), + //:tchap: + tchap_config, + //:tchap:end }) } @@ -575,6 +593,14 @@ impl FromRef for reqwest::Client { } } +//:tchap: +impl FromRef for TchapConfig { + fn from_ref(input: &TestState) -> Self { + input.tchap_config.clone() + } +} +//:tchap:end + impl FromRequestParts for ActivityTracker { type Rejection = Infallible; diff --git a/crates/handlers/src/upstream_oauth2/link.rs b/crates/handlers/src/upstream_oauth2/link.rs index a3d4c1bb9..9c14237e2 100644 --- a/crates/handlers/src/upstream_oauth2/link.rs +++ b/crates/handlers/src/upstream_oauth2/link.rs @@ -19,7 +19,14 @@ use mas_axum_utils::{ csrf::{CsrfExt, ProtectedForm}, record_error, }; -use mas_data_model::{BoxClock, BoxRng, UpstreamOAuthProviderOnConflict}; +use mas_data_model::{ + BoxClock, + BoxRng, + //:tchap: + TchapConfig, + //:tchap:end + UpstreamOAuthProviderOnConflict, +}; use mas_jose::jwt::Jwt; use mas_matrix::HomeserverConnection; use mas_policy::Policy; @@ -37,9 +44,12 @@ use mas_templates::{ use minijinja::Environment; use opentelemetry::{Key, KeyValue, metrics::Counter}; use serde::{Deserialize, Serialize}; +//:tchap: +use tchap::{self, EmailAllowedResult}; use thiserror::Error; use ulid::Ulid; +//:tchap: end use super::{ UpstreamSessionsCookie, template::{AttributeMappingContext, environment}, @@ -228,6 +238,9 @@ pub(crate) async fn get( State(templates): State, State(url_builder): State, State(homeserver): State>, + //:tchap: + State(tchap_config): State, + //:tchap:end cookie_jar: CookieJar, activity_tracker: BoundActivityTracker, user_agent: Option>, @@ -445,6 +458,46 @@ pub(crate) async fn get( provider.claims_imports.email.is_required(), )? { Some(value) => { + //:tchap: + let server_name = homeserver.homeserver(); + let email_result = + check_email_allowed(&value, server_name, &tchap_config).await; + + match email_result { + EmailAllowedResult::Allowed => { + // Email is allowed, continue + } + EmailAllowedResult::WrongServer => { + // Email is mapped to a different server + let ctx = ErrorContext::new() + .with_code("wrong_server") + .with_description(format!("Votre adresse mail {value} est associée à un autre serveur.")) + .with_details("Veuillez-vous contacter le support de Tchap support@tchap.beta.gouv.fr".to_owned()) + .with_language(&locale); + + //return error template + return Ok(( + cookie_jar, + Html(templates.render_error(&ctx)?).into_response(), + )); + } + EmailAllowedResult::InvitationMissing => { + // Server requires an invitation that is not present + let ctx = ErrorContext::new() + .with_code("invitation_missing") + .with_description("Vous avez besoin d'une invitation pour accéder à Tchap.".to_owned()) + .with_details("Les partenaires externes peuvent accéder à Tchap uniquement avec une invitation d'un agent public.".to_owned()) + .with_language(&locale); + + //return error template + return Ok(( + cookie_jar, + Html(templates.render_error(&ctx)?).into_response(), + )); + } + } + //:tchap: end + ctx.with_email(value, provider.claims_imports.email.is_forced_or_required()) } None => ctx, @@ -471,7 +524,32 @@ pub(crate) async fn get( // We could run policy & existing user checks when the user submits the // form, but this lead to poor UX. This is why we do // it ahead of time here. - let maybe_existing_user = repo.user().find_by_username(&localpart).await?; + //:tchap: + let mut maybe_existing_user = + repo.user().find_by_username(&localpart).await?; + //if not found by username, check by email + if maybe_existing_user.is_none() { + let template = provider + .claims_imports + .email + .template + .as_deref() + .unwrap_or(DEFAULT_EMAIL_TEMPLATE); + + let maybe_email = render_attribute_template( + &env, + template, + &context, + provider.claims_imports.email.is_required(), + ); + + if let Ok(Some(email)) = maybe_email { + maybe_existing_user = + tchap::search_user_by_email(&mut repo, &email, &tchap_config) + .await?; + } + } + //:tchap: end let is_available = homeserver .is_localpart_available(&localpart) .await @@ -599,6 +677,9 @@ pub(crate) async fn post( State(homeserver): State>, State(url_builder): State, State(site_config): State, + //:tchap: + State(tchap_config): State, + //:tchap:end Path(link_id): Path, Form(form): Form>, ) -> Result { @@ -699,7 +780,30 @@ pub(crate) async fn post( return Err(RouteError::InvalidFormAction); }; - let maybe_user = repo.user().find_by_username(&localpart).await?; + //:tchap: + let mut maybe_user = repo.user().find_by_username(&localpart).await?; + + //if not found by username, check by email + if maybe_user.is_none() { + let template = provider + .claims_imports + .email + .template + .as_deref() + .unwrap_or(DEFAULT_EMAIL_TEMPLATE); + + let maybe_email = render_attribute_template( + &env, + template, + &context, + provider.claims_imports.email.is_required(), + ); + + if let Ok(Some(email)) = maybe_email { + maybe_user = + tchap::search_user_by_email(&mut repo, &email, &tchap_config).await?; + } + } let Some(user) = maybe_user else { // user cannot be None at this stage @@ -1007,6 +1111,27 @@ pub(crate) async fn post( Ok((cookie_jar, post_auth_action.go_next(&url_builder)).into_response()) } +//:tchap: +///real function used when not testing +#[cfg(not(test))] +async fn check_email_allowed( + email: &str, + server_name: &str, + tchap_config: &TchapConfig, +) -> EmailAllowedResult { + tchap::is_email_allowed(email, server_name, tchap_config).await +} +///mock function used when testing +#[cfg(test)] +async fn check_email_allowed( + _email: &str, + _server_name: &str, + _tchap_config: &TchapConfig, +) -> EmailAllowedResult { + EmailAllowedResult::Allowed +} +//:tchap:end + #[cfg(test)] mod tests { use hyper::{Request, StatusCode, header::CONTENT_TYPE}; @@ -1020,7 +1145,8 @@ mod tests { use mas_keystore::Keystore; use mas_router::Route; use mas_storage::{ - Pagination, Repository, RepositoryError, upstream_oauth2::UpstreamOAuthProviderParams, + Pagination, Repository, RepositoryError, + upstream_oauth2::{UpstreamOAuthLinkFilter, UpstreamOAuthProviderParams}, user::UserEmailFilter, }; use oauth2_types::scope::{OPENID, Scope}; @@ -1462,6 +1588,357 @@ mod tests { assert!(response.body().contains("Unexpected error")); } + #[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")] + async fn test_link_existing_account_by_email(pool: PgPool) { + let subject = "subject"; + + let existing_username = "john"; + let oidc_username = "any"; + + let existing_email = "john@beta.gouv.fr"; + let oidc_email = "john@beta.gouv.fr"; + + setup(); + let state = TestState::from_pool(pool).await.unwrap(); + let mut rng = state.rng(); + let cookies = CookieHelper::new(); + + let claims_imports = UpstreamOAuthProviderClaimsImports { + localpart: UpstreamOAuthProviderLocalpartPreference { + action: mas_data_model::UpstreamOAuthProviderImportAction::Require, + template: None, + on_conflict: mas_data_model::UpstreamOAuthProviderOnConflict::Add, + }, + email: UpstreamOAuthProviderImportPreference { + action: mas_data_model::UpstreamOAuthProviderImportAction::Require, + template: None, + }, + ..UpstreamOAuthProviderClaimsImports::default() + }; + + let id_token_claims = serde_json::json!({ + "preferred_username": oidc_username, + "email": oidc_email, + "email_verified": true, + }); + + let id_token = sign_token(&mut rng, &state.key_store, id_token_claims.clone()).unwrap(); + + // Provision a provider and a link + let mut repo = state.repository().await.unwrap(); + let provider = repo + .upstream_oauth_provider() + .add( + &mut rng, + &state.clock, + UpstreamOAuthProviderParams { + issuer: Some("https://example.com/".to_owned()), + human_name: Some("Example Ltd.".to_owned()), + brand_name: None, + scope: Scope::from_iter([OPENID]), + token_endpoint_auth_method: UpstreamOAuthProviderTokenAuthMethod::None, + token_endpoint_signing_alg: None, + id_token_signed_response_alg: JsonWebSignatureAlg::Rs256, + client_id: "client".to_owned(), + encrypted_client_secret: None, + claims_imports, + authorization_endpoint_override: None, + token_endpoint_override: None, + userinfo_endpoint_override: None, + fetch_userinfo: false, + userinfo_signed_response_alg: None, + jwks_uri_override: None, + discovery_mode: mas_data_model::UpstreamOAuthProviderDiscoveryMode::Oidc, + pkce_mode: mas_data_model::UpstreamOAuthProviderPkceMode::Auto, + response_mode: None, + additional_authorization_parameters: Vec::new(), + forward_login_hint: false, + on_backchannel_logout: + mas_data_model::UpstreamOAuthProviderOnBackchannelLogout::DoNothing, + ui_order: 0, + }, + ) + .await + .unwrap(); + + //provision upstream authorization session to setup cookies + let (link, session) = add_linked_upstream_session( + &mut rng, + &state.clock, + &mut repo, + &provider, + subject, + &id_token.into_string(), + id_token_claims, + ) + .await + .unwrap(); + + //create existing user with email + let existing_user = repo + .user() + .add(&mut rng, &state.clock, existing_username.to_owned()) + .await + .unwrap(); + let _user_email = repo + .user_email() + .add( + &mut rng, + &state.clock, + &existing_user, + existing_email.to_owned(), + ) + .await; + + repo.save().await.unwrap(); + + let cookie_jar = state.cookie_jar(); + let upstream_sessions = UpstreamSessionsCookie::default() + .add(session.id, provider.id, "state".to_owned(), None) + .add_link_to_session(session.id, link.id) + .unwrap(); + let cookie_jar = upstream_sessions.save(cookie_jar, &state.clock); + cookies.import(cookie_jar); + + let request = Request::get(&*mas_router::UpstreamOAuth2Link::new(link.id).path()).empty(); + let request = cookies.with_cookies(request); + let response = state.request(request).await; + cookies.save_cookies(&response); + response.assert_status(StatusCode::OK); + response.assert_header_value(CONTENT_TYPE, "text/html; charset=utf-8"); + + // Extract the CSRF token from the response body + let csrf_token = response + .body() + .split("name=\"csrf\" value=\"") + .nth(1) + .unwrap() + .split('\"') + .next() + .unwrap(); + + let request = Request::post(&*mas_router::UpstreamOAuth2Link::new(link.id).path()).form( + serde_json::json!({ + "csrf": csrf_token, + "action": "link", + }), + ); + let request = cookies.with_cookies(request); + let response = state.request(request).await; + cookies.save_cookies(&response); + response.assert_status(StatusCode::SEE_OTHER); + + let mut repo = state.repository().await.unwrap(); + + let link = repo + .upstream_oauth_link() + .find_by_subject(&provider, subject) + .await + .unwrap() + .expect("link exists"); + + // Check that the existing user has the oidc link + assert_eq!( + link.user_id.unwrap().to_string(), + existing_user.id.to_string() + ); + + let page = repo + .user_email() + .list( + UserEmailFilter::new().for_user(&existing_user), + Pagination::first(1), + ) + .await + .unwrap(); + + //check that the existing user email is updated by oidc email + assert_eq!(page.edges.len(), 1); + let email = page.edges.first().expect("email exists"); + + assert_eq!(email.email, oidc_email); + } + + #[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")] + async fn test_link_existing_account_with_fallback_email(pool: PgPool) { + let subject = "subject"; + + let existing_username = "john"; + let oidc_username = "any"; + + let existing_email = "john@beta.gouv.fr"; + let oidc_email = "john@numerique.gouv.fr"; + + setup(); + let state = TestState::from_pool(pool).await.unwrap(); + let mut rng = state.rng(); + let cookies = CookieHelper::new(); + + let claims_imports = UpstreamOAuthProviderClaimsImports { + localpart: UpstreamOAuthProviderLocalpartPreference { + action: mas_data_model::UpstreamOAuthProviderImportAction::Require, + template: None, + on_conflict: mas_data_model::UpstreamOAuthProviderOnConflict::Add, + }, + email: UpstreamOAuthProviderImportPreference { + action: mas_data_model::UpstreamOAuthProviderImportAction::Require, + template: None, + }, + ..UpstreamOAuthProviderClaimsImports::default() + }; + + let id_token_claims = serde_json::json!({ + "preferred_username": oidc_username, + "email": oidc_email, + "email_verified": true, + }); + + let id_token = sign_token(&mut rng, &state.key_store, id_token_claims.clone()).unwrap(); + + // Provision a provider and a link + let mut repo = state.repository().await.unwrap(); + let provider = repo + .upstream_oauth_provider() + .add( + &mut rng, + &state.clock, + UpstreamOAuthProviderParams { + issuer: Some("https://example.com/".to_owned()), + human_name: Some("Example Ltd.".to_owned()), + brand_name: None, + scope: Scope::from_iter([OPENID]), + token_endpoint_auth_method: UpstreamOAuthProviderTokenAuthMethod::None, + token_endpoint_signing_alg: None, + id_token_signed_response_alg: JsonWebSignatureAlg::Rs256, + client_id: "client".to_owned(), + encrypted_client_secret: None, + claims_imports, + authorization_endpoint_override: None, + token_endpoint_override: None, + userinfo_endpoint_override: None, + fetch_userinfo: false, + userinfo_signed_response_alg: None, + jwks_uri_override: None, + discovery_mode: mas_data_model::UpstreamOAuthProviderDiscoveryMode::Oidc, + pkce_mode: mas_data_model::UpstreamOAuthProviderPkceMode::Auto, + response_mode: None, + additional_authorization_parameters: Vec::new(), + forward_login_hint: false, + on_backchannel_logout: + mas_data_model::UpstreamOAuthProviderOnBackchannelLogout::DoNothing, + ui_order: 0, + }, + ) + .await + .unwrap(); + + //provision upstream authorization session to setup cookies + let (link, session) = add_linked_upstream_session( + &mut rng, + &state.clock, + &mut repo, + &provider, + subject, + &id_token.into_string(), + id_token_claims, + ) + .await + .unwrap(); + + //Create user with an email + let existing_user = repo + .user() + .add(&mut rng, &state.clock, existing_username.to_owned()) + .await + .unwrap(); + let _user_email = repo + .user_email() + .add( + &mut rng, + &state.clock, + &existing_user, + existing_email.to_owned(), + ) + .await; + + repo.save().await.unwrap(); + + let cookie_jar = state.cookie_jar(); + let upstream_sessions = UpstreamSessionsCookie::default() + .add(session.id, provider.id, "state".to_owned(), None) + .add_link_to_session(session.id, link.id) + .unwrap(); + let cookie_jar = upstream_sessions.save(cookie_jar, &state.clock); + cookies.import(cookie_jar); + + let request = Request::get(&*mas_router::UpstreamOAuth2Link::new(link.id).path()).empty(); + let request = cookies.with_cookies(request); + let response = state.request(request).await; + cookies.save_cookies(&response); + response.assert_status(StatusCode::OK); + response.assert_header_value(CONTENT_TYPE, "text/html; charset=utf-8"); + + // Extract the CSRF token from the response body + let csrf_token = response + .body() + .split("name=\"csrf\" value=\"") + .nth(1) + .unwrap() + .split('\"') + .next() + .unwrap(); + + let request = Request::post(&*mas_router::UpstreamOAuth2Link::new(link.id).path()).form( + serde_json::json!({ + "csrf": csrf_token, + "action": "link" + }), + ); + let request = cookies.with_cookies(request); + let response = state.request(request).await; + cookies.save_cookies(&response); + response.assert_status(StatusCode::SEE_OTHER); + + // Check that the existing user has the oidc link + let mut repo = state.repository().await.unwrap(); + + let link = repo + .upstream_oauth_link() + .find_by_subject(&provider, subject) + .await + .unwrap() + .expect("link exists"); + + assert_eq!( + link.user_id.unwrap().to_string(), + existing_user.id.to_string() + ); + + // Check only one link searching subject by user + let link_count = repo + .upstream_oauth_link() + .count(UpstreamOAuthLinkFilter::default().for_user(&existing_user)) + .await + .unwrap(); + + assert_eq!(link_count, 1); + + //check that the user email is not updated with oidc email + let page = repo + .user_email() + .list( + UserEmailFilter::new().for_user(&existing_user), + Pagination::first(1), + ) + .await + .unwrap(); + + assert_eq!(page.edges.len(), 1); + let email = page.edges.first().expect("email exists"); + + assert_ne!(email.email, oidc_email); + } + fn sign_token( rng: &mut ChaChaRng, keystore: &Keystore, diff --git a/crates/handlers/src/upstream_oauth2/template.rs b/crates/handlers/src/upstream_oauth2/template.rs index fcf24473a..d65d8df97 100644 --- a/crates/handlers/src/upstream_oauth2/template.rs +++ b/crates/handlers/src/upstream_oauth2/template.rs @@ -11,6 +11,7 @@ use minijinja::{ Environment, Error, ErrorKind, Value, value::{Enumerator, Object}, }; +use tchap; /// Context passed to the attribute mapping template /// @@ -188,6 +189,16 @@ pub fn environment() -> Environment<'static> { env.add_filter("string", string); env.add_filter("from_json", from_json); + // Add Tchap-specific filters, this could be a generic config submitted + // to upstream allowing all users to add their own filters without upstream code + // modifications tester les fonctions async pour le reseau + env.add_filter("email_to_display_name", |s: &str| { + tchap::email_to_display_name(s) + }); + env.add_filter("email_to_mxid_localpart", |s: &str| { + tchap::email_to_mxid_localpart(s) + }); + env.set_unknown_method_callback(minijinja_contrib::pycompat::unknown_method_callback); env diff --git a/crates/tchap/Cargo.toml b/crates/tchap/Cargo.toml new file mode 100644 index 000000000..e8e2e3629 --- /dev/null +++ b/crates/tchap/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "tchap" +version = "0.1.0" +description = "Tchap-specific functionality for Matrix Authentication Service" +license = "MIT" +edition = "2024" + + +[dependencies] +reqwest.workspace = true +serde_json.workspace = true +tracing.workspace = true +tokio.workspace = true +url.workspace = true +serde.workspace = true + +mas-data-model.workspace = true +mas-storage.workspace = true +mas-http.workspace = true + +[dev-dependencies] +wiremock.workspace = true \ No newline at end of file diff --git a/crates/tchap/src/identity_client.rs b/crates/tchap/src/identity_client.rs new file mode 100644 index 000000000..aa0682fae --- /dev/null +++ b/crates/tchap/src/identity_client.rs @@ -0,0 +1,69 @@ +// +// MIT License +// +// Copyright (c) 2025, Direction interministérielle du numérique - Gouvernement +// Français +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +//! This module provides utilities for interacting with the Matrix identity +//! server API. + +use mas_data_model::TchapConfig; +use mas_http::RequestBuilderExt; +use tracing::info; + +/// Queries the identity server for information about an email address +/// +/// # Parameters +/// +/// * `email`: The email address to check/// +/// # Returns +/// +/// A Result containing either the JSON response or an error +pub async fn query_identity_server( + email: &str, + tchap_config: &TchapConfig, +) -> Result { + let identity_server_url = &tchap_config.identity_server_url; + + // Construct the URL with the email address + let url = format!( + "{}_matrix/identity/api/v1/internal-info", + identity_server_url + ); + let query_params = [("medium", "email"), ("address", email)]; + + info!("Making request to identity server: {}", url); + + let http_client = mas_http::reqwest_client(); + + // Make the HTTP request asynchronously + let response = http_client + .get(url) + .query(&query_params) + .send_traced() + .await?; + + // Parse the JSON response + let json = response.json::().await?; + + Ok(json) +} diff --git a/crates/tchap/src/lib.rs b/crates/tchap/src/lib.rs new file mode 100644 index 000000000..19c3a8855 --- /dev/null +++ b/crates/tchap/src/lib.rs @@ -0,0 +1,532 @@ +// +// MIT License +// +// Copyright (c) 2025, Direction interministérielle du numérique - Gouvernement +// Français +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +//! Tchap-specific functionality for Matrix Authentication Service + +extern crate tracing; +use mas_data_model::TchapConfig; +use mas_storage::BoxRepository; +use tracing::info; + +mod identity_client; +mod test_utils; + +/// Capitalise parts of a name containing different words, including those +/// separated by hyphens. +/// +/// For example, 'John-Doe' +/// +/// # Parameters +/// +/// * `name`: The name to parse +/// +/// # Returns +/// +/// The capitalized name +#[must_use] +pub fn cap(name: &str) -> String { + if name.is_empty() { + return name.to_string(); + } + + // Split the name by whitespace then hyphens, capitalizing each part then + // joining it back together. + name.split_whitespace() + .map(|space_part| { + space_part + .split('-') + .map(|part| { + let mut chars = part.chars(); + match chars.next() { + None => String::new(), + Some(first_char) => { + let first_char_upper = first_char.to_uppercase().collect::(); + let rest: String = chars.collect(); + format!("{}{}", first_char_upper, rest) + } + } + }) + .collect::>() + .join("-") + }) + .collect::>() + .join(" ") +} + +/// Generate a Matrix ID localpart from an email address. +/// +/// This function: +/// 1. Replaces "@" with "-" in the email address +/// 2. Converts the email to lowercase +/// 3. Filters out any characters that are not allowed in a Matrix ID localpart +/// +/// The allowed characters are: lowercase ASCII letters, digits, and "_-./=" +/// +/// # Parameters +/// +/// * `address`: The email address to process +/// +/// # Returns +/// +/// A valid Matrix ID localpart derived from the email address +#[must_use] +pub fn email_to_mxid_localpart(address: &str) -> String { + // Define the allowed characters for a Matrix ID localpart + const ALLOWED_CHARS: &str = "abcdefghijklmnopqrstuvwxyz0123456789_-./="; + + // Replace "@" with "-" and convert to lowercase + let processed = address.replace('@', "-").to_lowercase(); + + // Filter out any characters that are not allowed + processed + .chars() + .filter(|c| ALLOWED_CHARS.contains(*c)) + .collect() +} + +/// Generate a display name from an email address based on specific rules. +/// +/// This function: +/// 1. Replaces dots with spaces in the username part +/// 2. Determines the organization based on domain rules: +/// - gouv.fr emails use the subdomain or "gouv" if none +/// - other emails use the second-level domain +/// 3. Returns a display name in the format "Username [Organization]" +/// +/// # Parameters +/// +/// * `address`: The email address to process +/// +/// # Returns +/// +/// The formatted display name +#[must_use] +pub fn email_to_display_name(address: &str) -> String { + // Split the part before and after the @ in the email. + // Replace all . with spaces in the first part + let parts: Vec<&str> = address.split('@').collect(); + if parts.len() != 2 { + return String::new(); + } + + let username = parts[0].replace('.', " "); + let domain = parts[1]; + + // Figure out which org this email address belongs to + let domain_parts: Vec<&str> = domain.split('.').collect(); + + let org = if domain_parts.len() >= 2 + && domain_parts[domain_parts.len() - 2] == "gouv" + && domain_parts[domain_parts.len() - 1] == "fr" + { + // Is this is a ...gouv.fr address, set the org to whatever is before + // gouv.fr. If there isn't anything (a @gouv.fr email) simply mark their + // org as "gouv" + if domain_parts.len() > 2 { + domain_parts[domain_parts.len() - 3] + } else { + "gouv" + } + } else if domain_parts.len() >= 2 { + // Otherwise, mark their org as the email's second-level domain name + domain_parts[domain_parts.len() - 2] + } else { + "" + }; + + // Format the display name + format!("{} [{}]", cap(&username), cap(org)) +} + +/// Result of checking if an email is allowed on a server +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum EmailAllowedResult { + /// Email is allowed on this server + Allowed, + /// Email is mapped to a different server + WrongServer, + /// Server requires an invitation that is not present + InvitationMissing, +} + +/// Checks if an email address is allowed to be associated in the current server +/// +/// This function makes an asynchronous GET request to the Matrix identity +/// server API to retrieve information about the home server associated with an +/// email address, then applies logic to determine if the email is allowed. +/// +/// # Parameters +/// +/// * `email`: The email address to check +/// * `server_name`: The name of the server to check against +/// +/// # Returns +/// +/// An `EmailAllowedResult` indicating whether the email is allowed and if not, +/// why +#[must_use] +pub async fn is_email_allowed( + email: &str, + server_name: &str, + tchap_config: &TchapConfig, +) -> EmailAllowedResult { + // Query the identity server + match identity_client::query_identity_server(email, tchap_config).await { + Ok(json) => { + let hs = json.get("hs"); + + // Check if "hs" is in the response or if hs different from server_name + if hs.is_none() || hs.unwrap() != server_name { + // Email is mapped to a different server or no server at all + return EmailAllowedResult::WrongServer; + } + + info!("hs: {} ", hs.unwrap()); + + // Check if requires_invite is true and invited is false + let requires_invite = json + .get("requires_invite") + .and_then(|v| v.as_bool()) + .unwrap_or(false); + + let invited = json + .get("invited") + .and_then(|v| v.as_bool()) + .unwrap_or(false); + + info!("requires_invite: {} invited: {}", requires_invite, invited); + + if requires_invite && !invited { + // Requires an invite but hasn't been invited + return EmailAllowedResult::InvitationMissing; + } + + // All checks passed + EmailAllowedResult::Allowed + } + Err(err) => { + // Log the error and return WrongServer as a default error + eprintln!("HTTP request failed: {}", err); + EmailAllowedResult::WrongServer + } + } +} + +/// Search for a user by email with fallback rules +/// +/// # Parameters +/// * `repo` - Repository access +/// * `email` - The email to search for +/// * `fallback_rules` - Fallback rules for email transformation +/// +/// # Returns +/// Option<`mas_data_model::User`> - The found user if any +pub async fn search_user_by_email( + repo: &mut BoxRepository, + email: &str, + tchap_config: &TchapConfig, +) -> Result, mas_storage::RepositoryError> { + tracing::info!("Matching oidc identity by email:{}", email); + let maybe_user_email = repo.user_email().find_by_email(email).await?; + + if let Some(user_email) = maybe_user_email { + let maybe_user_found: Option = + repo.user().lookup(user_email.user_id).await?; + return Ok(maybe_user_found); + } + + tracing::info!( + "Email not found, Matching oidc identity by email using fallback rules:{}", + email + ); + let fallback_rules = &tchap_config.email_lookup_fallback_rules; + // let fallback_rules: Value = + // serde_json::from_str(r#"[{"match":"@numerique.gouv.fr", + // "search":"@beta.gouv.fr"}]"#) .unwrap(); + + // Iterate on fallback_rules, if a rule 'match' matches the email, + // replace by value of 'search' and lookup again the email + for rule in fallback_rules { + let match_pattern = &rule.match_with; + let search_value = &rule.search; + tracing::info!( + "Checking fallback rules {} : {}", + match_pattern, + search_value + ); + + // Check if email contains the match pattern + if email.contains(match_pattern) { + // Replace match pattern with search value + let transformed_email = email.replace(match_pattern, search_value); + tracing::debug!( + "Search by transformed email fallback rules {}", + transformed_email + ); + + // Look up the transformed email + let maybe_transformed_user_email = + repo.user_email().find_by_email(&transformed_email).await?; + + if let Some(transformed_user_email) = maybe_transformed_user_email { + let user_found: Option = + repo.user().lookup(transformed_user_email.user_id).await?; + tracing::info!( + "User found with fallback rules {} : {}", + match_pattern, + search_value + ); + + return Ok(user_found); + } + } + } + + Ok(None) +} +//:tchap: end + +pub use self::test_utils::*; + +#[cfg(test)] +mod tests { + use serde_json::json; + use url::Url; + use wiremock::{ + Mock, MockServer, ResponseTemplate, + matchers::{method, path, query_param}, + }; + + use super::*; + + #[test] + fn test_cap() { + assert_eq!(cap("john"), "John"); + assert_eq!(cap("john-doe"), "John-Doe"); + assert_eq!(cap("john doe"), "John Doe"); + assert_eq!(cap("john-doe smith"), "John-Doe Smith"); + assert_eq!(cap(""), ""); + } + + #[test] + fn test_email_to_display_name() { + // Test gouv.fr email with subdomain + assert_eq!( + email_to_display_name("jane.smith@example.gouv.fr"), + "Jane Smith [Example]" + ); + + // Test gouv.fr email without subdomain + assert_eq!(email_to_display_name("user@gouv.fr"), "User [Gouv]"); + + // Test gouv.fr email with subdomain + assert_eq!( + email_to_display_name("user@gendarmerie.gouv.fr"), + "User [Gendarmerie]" + ); + + // Test gouv.fr email with subdomain + assert_eq!( + email_to_display_name("user@gendarmerie.interieur.gouv.fr"), + "User [Interieur]" + ); + + // Test regular email + assert_eq!( + email_to_display_name("contact@example.com"), + "Contact [Example]" + ); + + // Test invalid email + assert_eq!(email_to_display_name("invalid-email"), ""); + } + + #[test] + fn test_email_to_mxid_localpart() { + // Test basic email + assert_eq!( + email_to_mxid_localpart("john.doe@example.com"), + "john.doe-example.com" + ); + + // Test with uppercase letters + assert_eq!( + email_to_mxid_localpart("John.Doe@Example.com"), + "john.doe-example.com" + ); + + // Test with special characters + assert_eq!( + email_to_mxid_localpart("user+tag@domain.com"), + "usertag-domain.com" + ); + + // Test with invalid characters + assert_eq!( + email_to_mxid_localpart("user!#$%^&*()@domain.com"), + "user-domain.com" + ); + } + + #[tokio::test] + async fn test_is_email_allowed() { + let email = "user@example.org"; + let server_name = "homeserver1"; + let allowed = true; + + let mock_server = MockServer::start().await; + + let expected_calls = 1; + + let _mock_guard = Mock::given(method("GET")) + .and(path("/_matrix/identity/api/v1/internal-info")) + .and(query_param("medium", "email")) + .and(query_param("address", email)) + .respond_with(ResponseTemplate::new(200).set_body_json(json!({ + "hs": server_name, + "requires_invite": true, + "invited": allowed + }))) + .expect(expected_calls) + .mount(&mock_server) + .await; + + // Fake config + let url = mock_server.uri() + "/"; + let config = TchapConfig { + identity_server_url: Url::parse(url.as_str()).unwrap(), + email_lookup_fallback_rules: vec![], + }; + + let result = is_email_allowed(email, server_name, &config).await; + + assert_eq!(result, EmailAllowedResult::Allowed); + } + + #[tokio::test] + async fn test_is_email_allowed_no_invitation() { + let email = "user@example.org"; + let server_name = "homeserver1"; + let allowed = false; + + let mock_server = MockServer::start().await; + + let expected_calls = 1; + + let _mock_guard = Mock::given(method("GET")) + .and(path("/_matrix/identity/api/v1/internal-info")) + .and(query_param("medium", "email")) + .and(query_param("address", email)) + .respond_with(ResponseTemplate::new(200).set_body_json(json!({ + "hs": server_name, + "requires_invite": true, + "invited": allowed + }))) + .expect(expected_calls) + .mount(&mock_server) + .await; + + // Fake config + let url = mock_server.uri() + "/"; + let config = TchapConfig { + identity_server_url: Url::parse(url.as_str()).unwrap(), + email_lookup_fallback_rules: vec![], + }; + + let result = is_email_allowed(email, server_name, &config).await; + + assert_eq!(result, EmailAllowedResult::InvitationMissing); + } + + #[tokio::test] + async fn test_is_email_allowed_wrong_server() { + let email = "user@example.org"; + let server_name = "homeserver1"; + let allowed = true; + + let mock_server = MockServer::start().await; + + let expected_calls = 1; + + let _mock_guard = Mock::given(method("GET")) + .and(path("/_matrix/identity/api/v1/internal-info")) + .and(query_param("medium", "email")) + .and(query_param("address", email)) + .respond_with(ResponseTemplate::new(200).set_body_json(json!({ + "hs": "homeserver2", + "requires_invite": true, + "invited": allowed + }))) + .expect(expected_calls) + .mount(&mock_server) + .await; + + // Fake config + let url = mock_server.uri() + "/"; + let config = TchapConfig { + identity_server_url: Url::parse(url.as_str()).unwrap(), + email_lookup_fallback_rules: vec![], + }; + + let result = is_email_allowed(email, server_name, &config).await; + + assert_eq!(result, EmailAllowedResult::WrongServer); + } + + #[tokio::test] + async fn test_is_email_allowed_with_special_character() { + let email = "user+1@example.org"; + let server_name = "homeserver1"; + let allowed = true; + + let mock_server = MockServer::start().await; + + let expected_calls = 1; + + let _mock_guard = Mock::given(method("GET")) + .and(path("/_matrix/identity/api/v1/internal-info")) + .and(query_param("medium", "email")) + .and(query_param("address", email)) + .respond_with(ResponseTemplate::new(200).set_body_json(json!({ + "hs": server_name, + "requires_invite": true, + "invited": allowed + }))) + .expect(expected_calls) + .mount(&mock_server) + .await; + + // Fake config + let url = mock_server.uri() + "/"; + let config = TchapConfig { + identity_server_url: Url::parse(url.as_str()).unwrap(), + email_lookup_fallback_rules: vec![], + }; + + let result = is_email_allowed(email, server_name, &config).await; + + assert_eq!(result, EmailAllowedResult::Allowed); + } +} diff --git a/crates/tchap/src/test_utils.rs b/crates/tchap/src/test_utils.rs new file mode 100644 index 000000000..7e549ef87 --- /dev/null +++ b/crates/tchap/src/test_utils.rs @@ -0,0 +1,37 @@ +// +// MIT License +// +// Copyright (c) 2025, Direction interministérielle du numérique - Gouvernement +// Français +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +use mas_data_model::{EmailLookupFallbackRule, TchapConfig}; +use url::Url; + +pub fn test_tchap_config() -> TchapConfig { + TchapConfig { + identity_server_url: Url::parse("http://localhost:8091").unwrap(), + email_lookup_fallback_rules: vec![EmailLookupFallbackRule { + match_with: "@numerique.gouv.fr".to_string(), + search: "@beta.gouv.fr".to_string(), + }], + } +} diff --git a/frontend/.storybook/public/mockServiceWorker.js b/frontend/.storybook/public/mockServiceWorker.js index 7e23102e0..2eec3ee33 100644 --- a/frontend/.storybook/public/mockServiceWorker.js +++ b/frontend/.storybook/public/mockServiceWorker.js @@ -7,8 +7,8 @@ * - Please do NOT modify this file. */ -const PACKAGE_VERSION = '2.11.1' -const INTEGRITY_CHECKSUM = 'f5825c521429caf22a4dd13b66e243af' +const PACKAGE_VERSION = '2.11.2' +const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82' const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') const activeClientIds = new Set() @@ -71,11 +71,6 @@ addEventListener('message', async function (event) { break } - case 'MOCK_DEACTIVATE': { - activeClientIds.delete(clientId) - break - } - case 'CLIENT_CLOSED': { activeClientIds.delete(clientId) @@ -94,6 +89,8 @@ addEventListener('message', async function (event) { }) addEventListener('fetch', function (event) { + const requestInterceptedAt = Date.now() + // Bypass navigation requests. if (event.request.mode === 'navigate') { return @@ -110,23 +107,29 @@ addEventListener('fetch', function (event) { // Bypass all requests when there are no active clients. // Prevents the self-unregistered worked from handling requests - // after it's been deleted (still remains active until the next reload). + // after it's been terminated (still remains active until the next reload). if (activeClientIds.size === 0) { return } const requestId = crypto.randomUUID() - event.respondWith(handleRequest(event, requestId)) + event.respondWith(handleRequest(event, requestId, requestInterceptedAt)) }) /** * @param {FetchEvent} event * @param {string} requestId + * @param {number} requestInterceptedAt */ -async function handleRequest(event, requestId) { +async function handleRequest(event, requestId, requestInterceptedAt) { const client = await resolveMainClient(event) const requestCloneForEvents = event.request.clone() - const response = await getResponse(event, client, requestId) + const response = await getResponse( + event, + client, + requestId, + requestInterceptedAt, + ) // Send back the response clone for the "response:*" life-cycle events. // Ensure MSW is active and ready to handle the message, otherwise @@ -204,7 +207,7 @@ async function resolveMainClient(event) { * @param {string} requestId * @returns {Promise} */ -async function getResponse(event, client, requestId) { +async function getResponse(event, client, requestId, requestInterceptedAt) { // Clone the request because it might've been already used // (i.e. its body has been read and sent to the client). const requestClone = event.request.clone() @@ -255,6 +258,7 @@ async function getResponse(event, client, requestId) { type: 'REQUEST', payload: { id: requestId, + interceptedAt: requestInterceptedAt, ...serializedRequest, }, }, diff --git a/frontend/knip.config.ts b/frontend/knip.config.ts index b5e382961..fddee70fe 100644 --- a/frontend/knip.config.ts +++ b/frontend/knip.config.ts @@ -7,7 +7,14 @@ import type { KnipConfig } from "knip"; export default { entry: ["src/main.tsx", "src/swagger.ts", "src/routes/*"], - ignore: ["src/gql/*", "src/routeTree.gen.ts", ".storybook/locales.ts"], + //:tchap: add tchap vite + ignore: [ + "src/gql/*", + "src/routeTree.gen.ts", + ".storybook/locales.ts", + "tchap/vite.tchap.config.ts", + ], + //:tchap: ignoreDependencies: [ // This is used by the tailwind PostCSS plugin, but not detected by knip "postcss-nesting", diff --git a/frontend/package-lock.json b/frontend/package-lock.json index cae710f08..df463a1b8 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -10,11 +10,12 @@ "dependencies": { "@fontsource/inconsolata": "^5.2.6", "@fontsource/inter": "^5.2.6", + "@gouvfr-lasuite/integration": "^1.0.3", "@radix-ui/react-collapsible": "^1.1.11", "@radix-ui/react-dialog": "^1.1.14", "@tanstack/react-query": "^5.85.5", "@tanstack/react-router": "^1.131.27", - "@vector-im/compound-design-tokens": "5.0.2", + "@vector-im/compound-design-tokens": "git+https://github.com/tchapgouv/compound-design-tokens.git", "@vector-im/compound-web": "^8.2.0", "@zxcvbn-ts/core": "^3.0.4", "@zxcvbn-ts/language-common": "^3.0.4", @@ -130,9 +131,9 @@ "license": "MIT" }, "node_modules/@adobe/css-tools": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.3.tgz", - "integrity": "sha512-VQKMkwriZbaOgVCby1UDY/LDk5fIjhQicCvVPFqfe+69fWaPWydbWJ3wRt59/YzIwda1I81loas3oCoHxnqvdA==", + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz", + "integrity": "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==", "dev": true, "license": "MIT" }, @@ -204,9 +205,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.27.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.5.tgz", - "integrity": "sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", + "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", "dev": true, "license": "MIT", "engines": { @@ -214,21 +215,22 @@ } }, "node_modules/@babel/core": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", - "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz", + "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==", "dev": true, + "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", + "@babel/generator": "^7.28.3", "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.27.3", - "@babel/helpers": "^7.27.6", - "@babel/parser": "^7.28.0", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.3", + "@babel/parser": "^7.28.3", "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.0", - "@babel/types": "^7.28.0", + "@babel/traverse": "^7.28.3", + "@babel/types": "^7.28.2", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -254,13 +256,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", - "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.0", - "@babel/types": "^7.28.0", + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -310,18 +313,18 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz", - "integrity": "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz", + "integrity": "sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.27.1", + "@babel/traverse": "^7.28.3", "semver": "^6.3.1" }, "engines": { @@ -346,6 +349,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -379,15 +383,15 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", - "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.3" + "@babel/traverse": "^7.28.3" }, "engines": { "node": ">=6.9.0" @@ -482,26 +486,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.27.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", - "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz", + "integrity": "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==", "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.27.2", - "@babel/types": "^7.27.6" + "@babel/types": "^7.28.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", - "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz", + "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.28.0" + "@babel/types": "^7.28.2" }, "bin": { "parser": "bin/babel-parser.js" @@ -672,9 +677,9 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.27.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.27.5.tgz", - "integrity": "sha512-JF6uE2s67f0y2RZcm2kpAUEbD50vH62TyWVebxwHAlbSdM49VqPz8t4a1uIjp4NIOIZ4xzLfjY5emt/RCyC7TQ==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.0.tgz", + "integrity": "sha512-gKKnwjpdx5sER/wl0WN0efUBFzF/56YZO0RJrSYP4CljXnP31ByY7fol89AzomdlLNzI36AvOTmYHsnZTCkq8Q==", "dev": true, "license": "MIT", "dependencies": { @@ -688,18 +693,18 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.27.1.tgz", - "integrity": "sha512-7iLhfFAubmpeJe/Wo2TVuDrykh/zlWXLzPNdL0Jqn/Xu8R3QQ8h9ff8FQoISZOsw74/HFqFI7NX63HN7QFIHKA==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.3.tgz", + "integrity": "sha512-DoEWC5SuxuARF2KdKmGUq3ghfPMO6ZzR12Dnp5gubwbeWJo4dbNWXJPVlwvh4Zlq6Z7YVvL8VFxeSOJgjsx4Sg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-globals": "^7.28.0", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", - "@babel/traverse": "^7.27.1", - "globals": "^11.1.0" + "@babel/traverse": "^7.28.3" }, "engines": { "node": ">=6.9.0" @@ -726,13 +731,14 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.27.3.tgz", - "integrity": "sha512-s4Jrok82JpiaIprtY2nHsYmrThKvvwgHwjgd7UMiYhZaN0asdXNLr0y+NjTfkA7SyQE5i2Fb7eawUOZmLvyqOA==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.0.tgz", + "integrity": "sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.0" }, "engines": { "node": ">=6.9.0" @@ -860,9 +866,9 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.1.tgz", - "integrity": "sha512-018KRk76HWKeZ5l4oTj2zPpSh+NbGdt0st5S6x0pga6HgrjBOJb24mMDHorFopOOd6YHkLgOZ+zaCjZGPO4aKg==", + "version": "7.27.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", + "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", "dev": true, "license": "MIT", "dependencies": { @@ -892,9 +898,9 @@ } }, "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.27.1.tgz", - "integrity": "sha512-p9+Vl3yuHPmkirRrg021XiP+EETmPMQTLr6Ayjj85RLNEbb3Eya/4VI0vAdzQG9SEAl2Lnt7fy5lZyMzjYoZQQ==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.28.0.tgz", + "integrity": "sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA==", "dev": true, "license": "MIT", "dependencies": { @@ -1049,9 +1055,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.27.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz", - "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.3.tgz", + "integrity": "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -1073,17 +1079,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", - "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz", + "integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", + "@babel/generator": "^7.28.3", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.0", + "@babel/parser": "^7.28.3", "@babel/template": "^7.27.2", - "@babel/types": "^7.28.0", + "@babel/types": "^7.28.2", "debug": "^4.3.1" }, "engines": { @@ -1091,10 +1098,11 @@ } }, "node_modules/@babel/types": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.0.tgz", - "integrity": "sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==", + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" @@ -1114,9 +1122,9 @@ } }, "node_modules/@biomejs/biome": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.1.2.tgz", - "integrity": "sha512-yq8ZZuKuBVDgAS76LWCfFKHSYIAgqkxVB3mGVVpOe2vSkUTs7xG46zXZeNPRNVjiJuw0SZ3+J2rXiYx0RUpfGg==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.2.2.tgz", + "integrity": "sha512-j1omAiQWCkhuLgwpMKisNKnsM6W8Xtt1l0WZmqY/dFj8QPNkIoTvk4tSsi40FaAAkBE1PU0AFG2RWFBWenAn+w==", "dev": true, "license": "MIT OR Apache-2.0", "bin": { @@ -1130,37 +1138,20 @@ "url": "https://opencollective.com/biome" }, "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "2.1.2", - "@biomejs/cli-darwin-x64": "2.1.2", - "@biomejs/cli-linux-arm64": "2.1.2", - "@biomejs/cli-linux-arm64-musl": "2.1.2", - "@biomejs/cli-linux-x64": "2.1.2", - "@biomejs/cli-linux-x64-musl": "2.1.2", - "@biomejs/cli-win32-arm64": "2.1.2", - "@biomejs/cli-win32-x64": "2.1.2" - } - }, - "node_modules/@biomejs/cli-darwin-arm64": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.1.2.tgz", - "integrity": "sha512-leFAks64PEIjc7MY/cLjE8u5OcfBKkcDB0szxsWUB4aDfemBep1WVKt0qrEyqZBOW8LPHzrFMyDl3FhuuA0E7g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=14.21.3" + "@biomejs/cli-darwin-arm64": "2.2.2", + "@biomejs/cli-darwin-x64": "2.2.2", + "@biomejs/cli-linux-arm64": "2.2.2", + "@biomejs/cli-linux-arm64-musl": "2.2.2", + "@biomejs/cli-linux-x64": "2.2.2", + "@biomejs/cli-linux-x64-musl": "2.2.2", + "@biomejs/cli-win32-arm64": "2.2.2", + "@biomejs/cli-win32-x64": "2.2.2" } }, "node_modules/@biomejs/cli-darwin-x64": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.1.2.tgz", - "integrity": "sha512-Nmmv7wRX5Nj7lGmz0FjnWdflJg4zii8Ivruas6PBKzw5SJX/q+Zh2RfnO+bBnuKLXpj8kiI2x2X12otpH6a32A==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.2.2.tgz", + "integrity": "sha512-Tn4JmVO+rXsbRslml7FvKaNrlgUeJot++FkvYIhl1OkslVCofAtS35MPlBMhXgKWF9RNr9cwHanrPTUUXcYGag==", "cpu": [ "x64" ], @@ -1174,108 +1165,6 @@ "node": ">=14.21.3" } }, - "node_modules/@biomejs/cli-linux-arm64": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.1.2.tgz", - "integrity": "sha512-NWNy2Diocav61HZiv2enTQykbPP/KrA/baS7JsLSojC7Xxh2nl9IczuvE5UID7+ksRy2e7yH7klm/WkA72G1dw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.1.2.tgz", - "integrity": "sha512-qgHvafhjH7Oca114FdOScmIKf1DlXT1LqbOrrbR30kQDLFPEOpBG0uzx6MhmsrmhGiCFCr2obDamu+czk+X0HQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-x64": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.1.2.tgz", - "integrity": "sha512-Km/UYeVowygTjpX6sGBzlizjakLoMQkxWbruVZSNE6osuSI63i4uCeIL+6q2AJlD3dxoiBJX70dn1enjQnQqwA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.1.2.tgz", - "integrity": "sha512-xlB3mU14ZUa3wzLtXfmk2IMOGL+S0aHFhSix/nssWS/2XlD27q+S6f0dlQ8WOCbYoXcuz8BCM7rCn2lxdTrlQA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-win32-arm64": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.1.2.tgz", - "integrity": "sha512-G8KWZli5ASOXA3yUQgx+M4pZRv3ND16h77UsdunUL17uYpcL/UC7RkWTdkfvMQvogVsAuz5JUcBDjgZHXxlKoA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=14.21.3" - } - }, - "node_modules/@biomejs/cli-win32-x64": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.1.2.tgz", - "integrity": "sha512-9zajnk59PMpjBkty3bK2IrjUsUHvqe9HWwyAWQBjGLE7MIBjbX2vwv1XPEhmO2RRuGoTkVx3WCanHrjAytICLA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=14.21.3" - } - }, "node_modules/@browser-logos/chrome": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@browser-logos/chrome/-/chrome-2.0.0.tgz", @@ -1395,44 +1284,10 @@ "postcss-selector-parser": "^7.0.0" } }, - "node_modules/@emnapi/core": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.3.tgz", - "integrity": "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.0.2", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", - "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.2.tgz", - "integrity": "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@envelop/core": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@envelop/core/-/core-5.2.3.tgz", - "integrity": "sha512-KfoGlYD/XXQSc3BkM1/k15+JQbkQ4ateHazeZoWl9P71FsLTDXSjGy6j7QqfhpIDSbxNISqhPMfZHYSbDFOofQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@envelop/core/-/core-5.3.0.tgz", + "integrity": "sha512-xvUkOWXI8JsG2OOnqiI2tOkEc52wbmIqWORr7yGc8B8E53Oh1MMGGGck4mbR80s25LnHVzfNIiIlNkuDgZRuuA==", "dev": true, "license": "MIT", "dependencies": { @@ -1473,513 +1328,117 @@ "node": ">=18.0.0" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", - "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==", + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", "cpu": [ - "ppc64" + "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "aix" + "darwin" ], "engines": { "node": ">=18" } }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz", - "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==", - "cpu": [ - "arm" - ], + "node_modules/@fastify/busboy": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.2.0.tgz", + "integrity": "sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA==", "dev": true, + "license": "MIT" + }, + "node_modules/@floating-ui/core": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", + "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@floating-ui/utils": "^0.2.10" } }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz", - "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/@floating-ui/dom": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", + "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@floating-ui/core": "^1.7.3", + "@floating-ui/utils": "^0.2.10" } }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz", - "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/@floating-ui/react": { + "version": "0.27.16", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.27.16.tgz", + "integrity": "sha512-9O8N4SeG2z++TSM8QA/KTeKFBVCNEz/AGS7gWPJf6KFRzmRWixFRnCnkPHRDwSVZW6QPDO6uT0P2SpWNKCc9/g==", "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@floating-ui/react-dom": "^2.1.6", + "@floating-ui/utils": "^0.2.10", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=17.0.0", + "react-dom": ">=17.0.0" } }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz", - "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.6.tgz", + "integrity": "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==", "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" + "dependencies": { + "@floating-ui/dom": "^1.7.4" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz", - "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "license": "MIT" + }, + "node_modules/@fontsource/inconsolata": { + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/@fontsource/inconsolata/-/inconsolata-5.2.6.tgz", + "integrity": "sha512-TRGh7bN+BN/oP8qD1IYe8REXM/0Uw3jbuERSncA/ZF6mqKFEOeTt6PR2T3xK7G+65N9pn2p0ablamdboee2nFQ==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" } }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz", - "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" + "node_modules/@fontsource/inter": { + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/@fontsource/inter/-/inter-5.2.6.tgz", + "integrity": "sha512-CZs9S1CrjD0jPwsNy9W6j0BhsmRSQrgwlTNkgQXTsAeDRM42LBRLo3eo9gCzfH4GvV7zpyf78Ozfl773826csw==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" } }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz", - "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz", - "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz", - "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz", - "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz", - "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz", - "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz", - "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz", - "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz", - "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz", - "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz", - "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz", - "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz", - "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz", - "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz", - "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz", - "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz", - "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz", - "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@fastify/busboy": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.1.1.tgz", - "integrity": "sha512-5DGmA8FTdB2XbDeEwc/5ZXBl6UbBAyBOOLlPuBnZ/N1SwdH9Ii+cOX3tBROlDgcTXxjOYnLMVoKk9+FXAw0CJw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@floating-ui/core": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.1.tgz", - "integrity": "sha512-azI0DrjMMfIug/ExbBaeDVJXcY0a7EPvPjb2xAJPa4HeimBX+Z18HK8QQR3jb6356SnDDdxx+hinMLcJEDdOjw==", - "license": "MIT", - "dependencies": { - "@floating-ui/utils": "^0.2.9" - } - }, - "node_modules/@floating-ui/dom": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.1.tgz", - "integrity": "sha512-cwsmW/zyw5ltYTUeeYJ60CnQuPqmGwuGVhG9w0PRaRKkAyi38BT5CKrpIbb+jtahSwUl04cWzSx9ZOIxeS6RsQ==", - "license": "MIT", - "dependencies": { - "@floating-ui/core": "^1.7.1", - "@floating-ui/utils": "^0.2.9" - } - }, - "node_modules/@floating-ui/react": { - "version": "0.27.12", - "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.27.12.tgz", - "integrity": "sha512-kKlWNrpIQxF1B/a2MZvE0/uyKby4960yjO91W7nVyNKmmfNi62xU9HCjL1M1eWzx/LFj/VPSwJVbwQk9Pq/68A==", - "license": "MIT", - "dependencies": { - "@floating-ui/react-dom": "^2.1.3", - "@floating-ui/utils": "^0.2.9", - "tabbable": "^6.0.0" - }, - "peerDependencies": { - "react": ">=17.0.0", - "react-dom": ">=17.0.0" - } - }, - "node_modules/@floating-ui/react-dom": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.3.tgz", - "integrity": "sha512-huMBfiU9UnQ2oBwIhgzyIiSpVgvlDstU8CX0AF+wS+KzmYMs0J2a3GwuFHV1Lz+jlrQGeC1fF+Nv0QoumyV0bA==", - "license": "MIT", - "dependencies": { - "@floating-ui/dom": "^1.0.0" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/@floating-ui/utils": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz", - "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", - "license": "MIT" - }, - "node_modules/@fontsource/inconsolata": { - "version": "5.2.6", - "resolved": "https://registry.npmjs.org/@fontsource/inconsolata/-/inconsolata-5.2.6.tgz", - "integrity": "sha512-TRGh7bN+BN/oP8qD1IYe8REXM/0Uw3jbuERSncA/ZF6mqKFEOeTt6PR2T3xK7G+65N9pn2p0ablamdboee2nFQ==", - "license": "OFL-1.1", - "funding": { - "url": "https://github.com/sponsors/ayuhito" - } - }, - "node_modules/@fontsource/inter": { - "version": "5.2.6", - "resolved": "https://registry.npmjs.org/@fontsource/inter/-/inter-5.2.6.tgz", - "integrity": "sha512-CZs9S1CrjD0jPwsNy9W6j0BhsmRSQrgwlTNkgQXTsAeDRM42LBRLo3eo9gCzfH4GvV7zpyf78Ozfl773826csw==", - "license": "OFL-1.1", - "funding": { - "url": "https://github.com/sponsors/ayuhito" - } - }, - "node_modules/@graphql-codegen/add": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@graphql-codegen/add/-/add-5.0.3.tgz", - "integrity": "sha512-SxXPmramkth8XtBlAHu4H4jYcYXM/o3p01+psU+0NADQowA8jtYkK6MW5rV6T+CxkEaNZItfSmZRPgIuypcqnA==", + "node_modules/@gouvfr-lasuite/integration": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@gouvfr-lasuite/integration/-/integration-1.0.3.tgz", + "integrity": "sha512-OgP28CqlPi35wQPul1Dr52SngACXAk8buLGqHYXDp23fbTOJThqarrZE/pgJHoc9Ndwiu7ngwBSO4rZ7OPyMpA==", + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "*", + "react-dom": "*", + "typescript": "*" + } + }, + "node_modules/@graphql-codegen/add": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@graphql-codegen/add/-/add-5.0.3.tgz", + "integrity": "sha512-SxXPmramkth8XtBlAHu4H4jYcYXM/o3p01+psU+0NADQowA8jtYkK6MW5rV6T+CxkEaNZItfSmZRPgIuypcqnA==", "dev": true, "license": "MIT", "dependencies": { @@ -2064,6 +1523,7 @@ "resolved": "https://registry.npmjs.org/@graphql-codegen/client-preset/-/client-preset-4.8.3.tgz", "integrity": "sha512-QpEsPSO9fnRxA6Z66AmBuGcwHjZ6dYSxYo5ycMlYgSPzAbyG8gn/kWljofjJfWqSY+T/lRn+r8IXTH14ml24vQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.20.2", "@babel/template": "^7.20.7", @@ -2204,6 +1664,7 @@ "resolved": "https://registry.npmjs.org/@graphql-codegen/typed-document-node/-/typed-document-node-5.1.2.tgz", "integrity": "sha512-jaxfViDqFRbNQmfKwUY8hDyjnLTw2Z7DhGutxoOiiAI0gE/LfPe0LYaVFKVmVOOD7M3bWxoWfu4slrkbWbUbEw==", "dev": true, + "license": "MIT", "dependencies": { "@graphql-codegen/plugin-helpers": "^5.1.0", "@graphql-codegen/visitor-plugin-common": "5.8.0", @@ -2222,7 +1683,8 @@ "version": "2.6.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", - "dev": true + "dev": true, + "license": "0BSD" }, "node_modules/@graphql-codegen/typescript": { "version": "4.1.6", @@ -2507,6 +1969,16 @@ "node": ">=8" } }, + "node_modules/@graphql-codegen/typescript-msw/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/@graphql-codegen/typescript-msw/node_modules/y18n": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", @@ -2634,13 +2106,13 @@ } }, "node_modules/@graphql-tools/apollo-engine-loader": { - "version": "8.0.20", - "resolved": "https://registry.npmjs.org/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-8.0.20.tgz", - "integrity": "sha512-m5k9nXSyjq31yNsEqDXLyykEjjn3K3Mo73oOKI+Xjy8cpnsgbT4myeUJIYYQdLrp7fr9Y9p7ZgwT5YcnwmnAbA==", + "version": "8.0.22", + "resolved": "https://registry.npmjs.org/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-8.0.22.tgz", + "integrity": "sha512-ssD2wNxeOTRcUEkuGcp0KfZAGstL9YLTe/y3erTDZtOs2wL1TJESw8NVAp+3oUHPeHKBZQB4Z6RFEbPgMdT2wA==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-tools/utils": "^10.8.6", + "@graphql-tools/utils": "^10.9.1", "@whatwg-node/fetch": "^0.10.0", "sync-fetch": "0.6.0-2", "tslib": "^2.4.0" @@ -2653,13 +2125,13 @@ } }, "node_modules/@graphql-tools/batch-execute": { - "version": "9.0.17", - "resolved": "https://registry.npmjs.org/@graphql-tools/batch-execute/-/batch-execute-9.0.17.tgz", - "integrity": "sha512-i7BqBkUP2+ex8zrQrCQTEt6nYHQmIey9qg7CMRRa1hXCY2X8ZCVjxsvbsi7gOLwyI/R3NHxSRDxmzZevE2cPLg==", + "version": "9.0.19", + "resolved": "https://registry.npmjs.org/@graphql-tools/batch-execute/-/batch-execute-9.0.19.tgz", + "integrity": "sha512-VGamgY4PLzSx48IHPoblRw0oTaBa7S26RpZXt0Y4NN90ytoE0LutlpB2484RbkfcTjv9wa64QD474+YP1kEgGA==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-tools/utils": "^10.8.1", + "@graphql-tools/utils": "^10.9.1", "@whatwg-node/promise-helpers": "^1.3.0", "dataloader": "^2.2.3", "tslib": "^2.8.1" @@ -2672,14 +2144,14 @@ } }, "node_modules/@graphql-tools/code-file-loader": { - "version": "8.1.20", - "resolved": "https://registry.npmjs.org/@graphql-tools/code-file-loader/-/code-file-loader-8.1.20.tgz", - "integrity": "sha512-GzIbjjWJIc04KWnEr8VKuPe0FA2vDTlkaeub5p4lLimljnJ6C0QSkOyCUnFmsB9jetQcHm0Wfmn/akMnFUG+wA==", + "version": "8.1.22", + "resolved": "https://registry.npmjs.org/@graphql-tools/code-file-loader/-/code-file-loader-8.1.22.tgz", + "integrity": "sha512-FSka29kqFkfFmw36CwoQ+4iyhchxfEzPbXOi37lCEjWLHudGaPkXc3RyB9LdmBxx3g3GHEu43a5n5W8gfcrMdA==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-tools/graphql-tag-pluck": "8.3.19", - "@graphql-tools/utils": "^10.8.6", + "@graphql-tools/graphql-tag-pluck": "8.3.21", + "@graphql-tools/utils": "^10.9.1", "globby": "^11.0.3", "tslib": "^2.4.0", "unixify": "^1.0.0" @@ -2692,16 +2164,16 @@ } }, "node_modules/@graphql-tools/delegate": { - "version": "10.2.19", - "resolved": "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-10.2.19.tgz", - "integrity": "sha512-aaCGAALTQmKctHwumbtz0c5XehGjYLSfoDx1IB2vdPt76Q0MKz2AiEDlENgzTVr4JHH7fd9YNrd+IO3D8tFlIg==", + "version": "10.2.23", + "resolved": "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-10.2.23.tgz", + "integrity": "sha512-xrPtl7f1LxS+B6o+W7ueuQh67CwRkfl+UKJncaslnqYdkxKmNBB4wnzVcW8ZsRdwbsla/v43PtwAvSlzxCzq2w==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-tools/batch-execute": "^9.0.17", - "@graphql-tools/executor": "^1.4.7", - "@graphql-tools/schema": "^10.0.11", - "@graphql-tools/utils": "^10.8.1", + "@graphql-tools/batch-execute": "^9.0.19", + "@graphql-tools/executor": "^1.4.9", + "@graphql-tools/schema": "^10.0.25", + "@graphql-tools/utils": "^10.9.1", "@repeaterjs/repeater": "^3.0.6", "@whatwg-node/promise-helpers": "^1.3.0", "dataloader": "^2.2.3", @@ -2733,13 +2205,13 @@ } }, "node_modules/@graphql-tools/executor": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/@graphql-tools/executor/-/executor-1.4.7.tgz", - "integrity": "sha512-U0nK9jzJRP9/9Izf1+0Gggd6K6RNRsheFo1gC/VWzfnsr0qjcOSS9qTjY0OTC5iTPt4tQ+W5Zpw/uc7mebI6aA==", + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/@graphql-tools/executor/-/executor-1.4.9.tgz", + "integrity": "sha512-SAUlDT70JAvXeqV87gGzvDzUGofn39nvaVcVhNf12Dt+GfWHtNNO/RCn/Ea4VJaSLGzraUd41ObnN3i80EBU7w==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-tools/utils": "^10.8.6", + "@graphql-tools/utils": "^10.9.1", "@graphql-typed-document-node/core": "^3.2.0", "@repeaterjs/repeater": "^3.0.4", "@whatwg-node/disposablestack": "^0.0.6", @@ -2771,19 +2243,36 @@ } }, "node_modules/@graphql-tools/executor-graphql-ws": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-2.0.5.tgz", - "integrity": "sha512-gI/D9VUzI1Jt1G28GYpvm5ckupgJ5O8mi5Y657UyuUozX34ErfVdZ81g6oVcKFQZ60LhCzk7jJeykK48gaLhDw==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-2.0.7.tgz", + "integrity": "sha512-J27za7sKF6RjhmvSOwOQFeNhNHyP4f4niqPnerJmq73OtLx9Y2PGOhkXOEB0PjhvPJceuttkD2O1yMgEkTGs3Q==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-tools/executor-common": "^0.0.4", - "@graphql-tools/utils": "^10.8.1", + "@graphql-tools/executor-common": "^0.0.6", + "@graphql-tools/utils": "^10.9.1", "@whatwg-node/disposablestack": "^0.0.6", - "graphql-ws": "^6.0.3", + "graphql-ws": "^6.0.6", "isomorphic-ws": "^5.0.0", "tslib": "^2.8.1", - "ws": "^8.17.1" + "ws": "^8.18.3" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, + "node_modules/@graphql-tools/executor-graphql-ws/node_modules/@graphql-tools/executor-common": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@graphql-tools/executor-common/-/executor-common-0.0.6.tgz", + "integrity": "sha512-JAH/R1zf77CSkpYATIJw+eOJwsbWocdDjY+avY7G+P5HCXxwQjAjWVkJI1QJBQYjPQDVxwf1fmTZlIN3VOadow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@envelop/core": "^5.3.0", + "@graphql-tools/utils": "^10.9.1" }, "engines": { "node": ">=18.0.0" @@ -2817,13 +2306,13 @@ } }, "node_modules/@graphql-tools/executor-legacy-ws": { - "version": "1.1.17", - "resolved": "https://registry.npmjs.org/@graphql-tools/executor-legacy-ws/-/executor-legacy-ws-1.1.17.tgz", - "integrity": "sha512-TvltY6eL4DY1Vt66Z8kt9jVmNcI+WkvVPQZrPbMCM3rv2Jw/sWvSwzUBezRuWX0sIckMifYVh23VPcGBUKX/wg==", + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/@graphql-tools/executor-legacy-ws/-/executor-legacy-ws-1.1.19.tgz", + "integrity": "sha512-bEbv/SlEdhWQD0WZLUX1kOenEdVZk1yYtilrAWjRUgfHRZoEkY9s+oiqOxnth3z68wC2MWYx7ykkS5hhDamixg==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-tools/utils": "^10.8.6", + "@graphql-tools/utils": "^10.9.1", "@types/ws": "^8.0.0", "isomorphic-ws": "^5.0.0", "tslib": "^2.4.0", @@ -2837,14 +2326,14 @@ } }, "node_modules/@graphql-tools/git-loader": { - "version": "8.0.24", - "resolved": "https://registry.npmjs.org/@graphql-tools/git-loader/-/git-loader-8.0.24.tgz", - "integrity": "sha512-ypLC9N2bKNC0QNbrEBTbWKwbV607f7vK2rSGi9uFeGr8E29tWplo6or9V/+TM0ZfIkUsNp/4QX/zKTgo8SbwQg==", + "version": "8.0.26", + "resolved": "https://registry.npmjs.org/@graphql-tools/git-loader/-/git-loader-8.0.26.tgz", + "integrity": "sha512-0g+9eng8DaT4ZmZvUmPgjLTgesUa6M8xrDjNBltRldZkB055rOeUgJiKmL6u8PjzI5VxkkVsn0wtAHXhDI2UXQ==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-tools/graphql-tag-pluck": "8.3.19", - "@graphql-tools/utils": "^10.8.6", + "@graphql-tools/graphql-tag-pluck": "8.3.21", + "@graphql-tools/utils": "^10.9.1", "is-glob": "4.0.3", "micromatch": "^4.0.8", "tslib": "^2.4.0", @@ -2858,15 +2347,15 @@ } }, "node_modules/@graphql-tools/github-loader": { - "version": "8.0.20", - "resolved": "https://registry.npmjs.org/@graphql-tools/github-loader/-/github-loader-8.0.20.tgz", - "integrity": "sha512-Icch8bKZ1iP3zXCB9I0ded1hda9NPskSSalw7ZM21kXvLiOR5nZhdqPF65gCFkIKo+O4NR4Bp51MkKj+wl+vpg==", + "version": "8.0.22", + "resolved": "https://registry.npmjs.org/@graphql-tools/github-loader/-/github-loader-8.0.22.tgz", + "integrity": "sha512-uQ4JNcNPsyMkTIgzeSbsoT9hogLjYrZooLUYd173l5eUGUi49EAcsGdiBCKaKfEjanv410FE8hjaHr7fjSRkJw==", "dev": true, "license": "MIT", "dependencies": { "@graphql-tools/executor-http": "^1.1.9", - "@graphql-tools/graphql-tag-pluck": "^8.3.19", - "@graphql-tools/utils": "^10.8.6", + "@graphql-tools/graphql-tag-pluck": "^8.3.21", + "@graphql-tools/utils": "^10.9.1", "@whatwg-node/fetch": "^0.10.0", "@whatwg-node/promise-helpers": "^1.0.0", "sync-fetch": "0.6.0-2", @@ -2880,14 +2369,14 @@ } }, "node_modules/@graphql-tools/graphql-file-loader": { - "version": "8.0.20", - "resolved": "https://registry.npmjs.org/@graphql-tools/graphql-file-loader/-/graphql-file-loader-8.0.20.tgz", - "integrity": "sha512-inds4My+JJxmg5mxKWYtMIJNRxa7MtX+XIYqqD/nu6G4LzQ5KGaBJg6wEl103KxXli7qNOWeVAUmEjZeYhwNEg==", + "version": "8.0.22", + "resolved": "https://registry.npmjs.org/@graphql-tools/graphql-file-loader/-/graphql-file-loader-8.0.22.tgz", + "integrity": "sha512-KFUbjXgWr5+w/AioOuIuULy4LwcyDuQqTRFQGe+US1d9Z4+ZopcJLwsJTqp5B+icDkCqld4paN0y0qi9MrIvbg==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-tools/import": "7.0.19", - "@graphql-tools/utils": "^10.8.6", + "@graphql-tools/import": "7.0.21", + "@graphql-tools/utils": "^10.9.1", "globby": "^11.0.3", "tslib": "^2.4.0", "unixify": "^1.0.0" @@ -2900,9 +2389,9 @@ } }, "node_modules/@graphql-tools/graphql-tag-pluck": { - "version": "8.3.19", - "resolved": "https://registry.npmjs.org/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-8.3.19.tgz", - "integrity": "sha512-LEw/6IYOUz48HjbWntZXDCzSXsOIM1AyWZrlLoJOrA8QAlhFd8h5Tny7opCypj8FO9VvpPFugWoNDh5InPOEQA==", + "version": "8.3.21", + "resolved": "https://registry.npmjs.org/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-8.3.21.tgz", + "integrity": "sha512-TJhELNvR1tmghXMi6HVKp/Swxbx1rcSp/zdkuJZT0DCM3vOY11FXY6NW3aoxumcuYDNN3jqXcCPKstYGFPi5GQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2911,7 +2400,7 @@ "@babel/plugin-syntax-import-assertions": "^7.26.0", "@babel/traverse": "^7.26.10", "@babel/types": "^7.26.10", - "@graphql-tools/utils": "^10.8.6", + "@graphql-tools/utils": "^10.9.1", "tslib": "^2.4.0" }, "engines": { @@ -2922,13 +2411,14 @@ } }, "node_modules/@graphql-tools/import": { - "version": "7.0.19", - "resolved": "https://registry.npmjs.org/@graphql-tools/import/-/import-7.0.19.tgz", - "integrity": "sha512-Xtku8G4bxnrr6I3hVf8RrBFGYIbQ1OYVjl7jgcy092aBkNZvy1T6EDmXmYXn5F+oLd9Bks3K3WaMm8gma/nM/Q==", + "version": "7.0.21", + "resolved": "https://registry.npmjs.org/@graphql-tools/import/-/import-7.0.21.tgz", + "integrity": "sha512-bcAqNWm/gLVEOy55o/WdaROERpDyUEmIfZ9E6NDjVk1ZGWfZe47+RgriTV80j6J5S5J1g+6loFkVWGAMqdN06g==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-tools/utils": "^10.8.6", + "@graphql-tools/utils": "^10.9.1", + "@theguild/federation-composition": "^0.19.0", "resolve-from": "5.0.0", "tslib": "^2.4.0" }, @@ -2940,13 +2430,13 @@ } }, "node_modules/@graphql-tools/json-file-loader": { - "version": "8.0.18", - "resolved": "https://registry.npmjs.org/@graphql-tools/json-file-loader/-/json-file-loader-8.0.18.tgz", - "integrity": "sha512-JjjIxxewgk8HeMR3npR3YbOkB7fxmdgmqB9kZLWdkRKBxrRXVzhryyq+mhmI0Evzt6pNoHIc3vqwmSctG2sddg==", + "version": "8.0.20", + "resolved": "https://registry.npmjs.org/@graphql-tools/json-file-loader/-/json-file-loader-8.0.20.tgz", + "integrity": "sha512-5v6W+ZLBBML5SgntuBDLsYoqUvwfNboAwL6BwPHi3z/hH1f8BS9/0+MCW9OGY712g7E4pc3y9KqS67mWF753eA==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-tools/utils": "^10.8.6", + "@graphql-tools/utils": "^10.9.1", "globby": "^11.0.3", "tslib": "^2.4.0", "unixify": "^1.0.0" @@ -2959,14 +2449,14 @@ } }, "node_modules/@graphql-tools/load": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@graphql-tools/load/-/load-8.1.0.tgz", - "integrity": "sha512-OGfOm09VyXdNGJS/rLqZ6ztCiG2g6AMxhwtET8GZXTbnjptFc17GtKwJ3Jv5w7mjJ8dn0BHydvIuEKEUK4ciYw==", + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/@graphql-tools/load/-/load-8.1.2.tgz", + "integrity": "sha512-WhDPv25/jRND+0uripofMX0IEwo6mrv+tJg6HifRmDu8USCD7nZhufT0PP7lIcuutqjIQFyogqT70BQsy6wOgw==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-tools/schema": "^10.0.23", - "@graphql-tools/utils": "^10.8.6", + "@graphql-tools/schema": "^10.0.25", + "@graphql-tools/utils": "^10.9.1", "p-limit": "3.1.0", "tslib": "^2.4.0" }, @@ -2978,13 +2468,13 @@ } }, "node_modules/@graphql-tools/merge": { - "version": "9.0.24", - "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.0.24.tgz", - "integrity": "sha512-NzWx/Afl/1qHT3Nm1bghGG2l4jub28AdvtG11PoUlmjcIjnFBJMv4vqL0qnxWe8A82peWo4/TkVdjJRLXwgGEw==", + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.1.1.tgz", + "integrity": "sha512-BJ5/7Y7GOhTuvzzO5tSBFL4NGr7PVqTJY3KeIDlVTT8YLcTXtBR+hlrC3uyEym7Ragn+zyWdHeJ9ev+nRX1X2w==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-tools/utils": "^10.8.6", + "@graphql-tools/utils": "^10.9.1", "tslib": "^2.4.0" }, "engines": { @@ -3042,14 +2532,14 @@ } }, "node_modules/@graphql-tools/relay-operation-optimizer": { - "version": "7.0.19", - "resolved": "https://registry.npmjs.org/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-7.0.19.tgz", - "integrity": "sha512-xnjLpfzw63yIX1bo+BVh4j1attSwqEkUbpJ+HAhdiSUa3FOQFfpWgijRju+3i87CwhjBANqdTZbcsqLT1hEXig==", + "version": "7.0.21", + "resolved": "https://registry.npmjs.org/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-7.0.21.tgz", + "integrity": "sha512-vMdU0+XfeBh9RCwPqRsr3A05hPA3MsahFn/7OAwXzMySA5EVnSH5R4poWNs3h1a0yT0tDPLhxORhK7qJdSWj2A==", "dev": true, "license": "MIT", "dependencies": { "@ardatan/relay-compiler": "^12.0.3", - "@graphql-tools/utils": "^10.8.6", + "@graphql-tools/utils": "^10.9.1", "tslib": "^2.4.0" }, "engines": { @@ -3060,14 +2550,14 @@ } }, "node_modules/@graphql-tools/schema": { - "version": "10.0.23", - "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.23.tgz", - "integrity": "sha512-aEGVpd1PCuGEwqTXCStpEkmheTHNdMayiIKH1xDWqYp9i8yKv9FRDgkGrY4RD8TNxnf7iII+6KOBGaJ3ygH95A==", + "version": "10.0.25", + "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.25.tgz", + "integrity": "sha512-/PqE8US8kdQ7lB9M5+jlW8AyVjRGCKU7TSktuW3WNKSKmDO0MK1wakvb5gGdyT49MjAIb4a3LWxIpwo5VygZuw==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-tools/merge": "^9.0.24", - "@graphql-tools/utils": "^10.8.6", + "@graphql-tools/merge": "^9.1.1", + "@graphql-tools/utils": "^10.9.1", "tslib": "^2.4.0" }, "engines": { @@ -3078,16 +2568,16 @@ } }, "node_modules/@graphql-tools/url-loader": { - "version": "8.0.31", - "resolved": "https://registry.npmjs.org/@graphql-tools/url-loader/-/url-loader-8.0.31.tgz", - "integrity": "sha512-QGP3py6DAdKERHO5D38Oi+6j+v0O3rkBbnLpyOo87rmIRbwE6sOkL5JeHegHs7EEJ279fBX6lMt8ry0wBMGtyA==", + "version": "8.0.33", + "resolved": "https://registry.npmjs.org/@graphql-tools/url-loader/-/url-loader-8.0.33.tgz", + "integrity": "sha512-Fu626qcNHcqAj8uYd7QRarcJn5XZ863kmxsg1sm0fyjyfBJnsvC7ddFt6Hayz5kxVKfsnjxiDfPMXanvsQVBKw==", "dev": true, "license": "MIT", "dependencies": { "@graphql-tools/executor-graphql-ws": "^2.0.1", "@graphql-tools/executor-http": "^1.1.9", - "@graphql-tools/executor-legacy-ws": "^1.1.17", - "@graphql-tools/utils": "^10.8.6", + "@graphql-tools/executor-legacy-ws": "^1.1.19", + "@graphql-tools/utils": "^10.9.1", "@graphql-tools/wrap": "^10.0.16", "@types/ws": "^8.0.0", "@whatwg-node/fetch": "^0.10.0", @@ -3105,9 +2595,9 @@ } }, "node_modules/@graphql-tools/utils": { - "version": "10.8.6", - "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.8.6.tgz", - "integrity": "sha512-Alc9Vyg0oOsGhRapfL3xvqh1zV8nKoFUdtLhXX7Ki4nClaIJXckrA86j+uxEuG3ic6j4jlM1nvcWXRn/71AVLQ==", + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.9.1.tgz", + "integrity": "sha512-B1wwkXk9UvU7LCBkPs8513WxOQ2H8Fo5p8HR1+Id9WmYE5+bd51vqN+MbrqvWczHCH2gwkREgHJN88tE0n1FCw==", "dev": true, "license": "MIT", "dependencies": { @@ -3125,15 +2615,15 @@ } }, "node_modules/@graphql-tools/wrap": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/@graphql-tools/wrap/-/wrap-10.1.0.tgz", - "integrity": "sha512-M7QolM/cJwM2PNAJS1vphT2/PDVSKtmg5m+fxHrFfKpp2RRosJSvYPzUD/PVPqF2rXTtnCwkgh1s5KIsOPCz+w==", + "version": "10.1.4", + "resolved": "https://registry.npmjs.org/@graphql-tools/wrap/-/wrap-10.1.4.tgz", + "integrity": "sha512-7pyNKqXProRjlSdqOtrbnFRMQAVamCmEREilOXtZujxY6kYit3tvWWSjUrcIOheltTffoRh7EQSjpy2JDCzasg==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-tools/delegate": "^10.2.19", - "@graphql-tools/schema": "^10.0.11", - "@graphql-tools/utils": "^10.8.1", + "@graphql-tools/delegate": "^10.2.23", + "@graphql-tools/schema": "^10.0.25", + "@graphql-tools/utils": "^10.9.1", "@whatwg-node/promise-helpers": "^1.3.0", "tslib": "^2.8.1" }, @@ -3168,14 +2658,14 @@ } }, "node_modules/@inquirer/confirm": { - "version": "5.1.12", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.12.tgz", - "integrity": "sha512-dpq+ielV9/bqgXRUbNH//KsY6WEw9DrGPmipkpmgC1Y46cwuBTNx7PXFWTjc3MQ+urcc0QxoVHcMI0FW4Ok0hg==", + "version": "5.1.16", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.16.tgz", + "integrity": "sha512-j1a5VstaK5KQy8Mu8cHmuQvN1Zc62TbLhjJxwHvKPPKEoowSF6h/0UdOpA9DNdWZ+9Inq73+puRq1df6OJ8Sag==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.13", - "@inquirer/type": "^3.0.7" + "@inquirer/core": "^10.2.0", + "@inquirer/type": "^3.0.8" }, "engines": { "node": ">=18" @@ -3190,14 +2680,14 @@ } }, "node_modules/@inquirer/core": { - "version": "10.1.13", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.13.tgz", - "integrity": "sha512-1viSxebkYN2nJULlzCxES6G9/stgHSepZ9LqqfdIGPHj5OHhiBUXVS0a6R0bEC2A+VL4D9w6QB66ebCr6HGllA==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.2.0.tgz", + "integrity": "sha512-NyDSjPqhSvpZEMZrLCYUquWNl+XC/moEcVFqS55IEYIYsY0a1cUCevSqk7ctOlnm/RaSBU5psFryNlxcmGrjaA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/figures": "^1.0.12", - "@inquirer/type": "^3.0.7", + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", "ansi-escapes": "^4.3.2", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", @@ -3238,9 +2728,9 @@ } }, "node_modules/@inquirer/external-editor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.0.tgz", - "integrity": "sha512-5v3YXc5ZMfL6OJqXPrX9csb4l7NlQA2doO1yynUjpUChT9hg4JcuBVP0RbsEJ/3SL/sxWEyFjT2W69ZhtoBWqg==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.1.tgz", + "integrity": "sha512-Oau4yL24d2B5IL4ma4UpbQigkVhzPDXLoqy1ggK4gnHg/stmkffJE4oOXHXF3uz0UEpywG68KcyXsyYpA1Re/Q==", "dev": true, "license": "MIT", "dependencies": { @@ -3252,12 +2742,17 @@ }, "peerDependencies": { "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, "node_modules/@inquirer/figures": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.12.tgz", - "integrity": "sha512-MJttijd8rMFcKJC8NYmprWr6hD3r9Gd9qUC0XwPNwoEPWSMVJwA2MlXxF+nhZZNMY+HXsWa+o7KY2emWYIn0jQ==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.13.tgz", + "integrity": "sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw==", "dev": true, "license": "MIT", "engines": { @@ -3265,9 +2760,9 @@ } }, "node_modules/@inquirer/type": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.7.tgz", - "integrity": "sha512-PfunHQcjwnju84L+ycmcMKB/pTPIngjUJvfnRhKY6FKPuYXlM4aQCb/nIdTFR6BEhMjFvngzvng/vBAJMZpLSA==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.8.tgz", + "integrity": "sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw==", "dev": true, "license": "MIT", "engines": { @@ -3282,6 +2777,29 @@ } } }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -3301,9 +2819,9 @@ } }, "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", + "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", "dev": true, "license": "MIT", "engines": { @@ -3400,6 +2918,7 @@ "resolved": "https://registry.npmjs.org/@joshwooding/vite-plugin-react-docgen-typescript/-/vite-plugin-react-docgen-typescript-0.6.1.tgz", "integrity": "sha512-J4BaTocTOYFkMHIra1JDWrMWpNmBl4EkplIwHEsV8aeUOtdWjwSnln9U7twjMFTAEB7mptNtSKyVi1Y2W9sDJw==", "dev": true, + "license": "MIT", "dependencies": { "glob": "^10.0.0", "magic-string": "^0.30.0", @@ -3416,15 +2935,27 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", - "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -3436,17 +2967,18 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "version": "0.3.30", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", + "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -3471,9 +3003,9 @@ } }, "node_modules/@mswjs/interceptors": { - "version": "0.39.2", - "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.39.2.tgz", - "integrity": "sha512-RuzCup9Ct91Y7V79xwCb146RaBRHZ7NBbrIUySumd1rpKqHL5OonaqrGIbug5hNwP/fRyxFMA6ISgw4FTtYFYg==", + "version": "0.39.6", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.39.6.tgz", + "integrity": "sha512-bndDP83naYYkfayr/qhBHMhk0YGwS1iv6vaEGcr0SQbO0IZtbOPqjKjds/WcG+bJA+1T5vCx6kprKOzn5Bg+Vw==", "dev": true, "license": "MIT", "dependencies": { @@ -3488,19 +3020,6 @@ "node": ">=18" } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.11.tgz", - "integrity": "sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.9.0" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -3550,9 +3069,9 @@ } }, "node_modules/@octokit/core": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.1.tgz", - "integrity": "sha512-dKYCMuPO1bmrpuogcjQ8z7ICCH3FP6WmxpwC03yjzGfZhj9fTJg6+bS1+UAplekbN2C+M61UNllGOOoAfGCrdQ==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.2.tgz", + "integrity": "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==", "dev": true, "license": "MIT", "dependencies": { @@ -3687,230 +3206,59 @@ } }, "node_modules/@octokit/request-error": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz", - "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/types": "^13.1.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/@octokit/types": { - "version": "13.10.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", - "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@octokit/openapi-types": "^24.2.0" - } - }, - "node_modules/@open-draft/deferred-promise": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", - "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@open-draft/logger": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", - "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-node-process": "^1.2.0", - "outvariant": "^1.4.0" - } - }, - "node_modules/@open-draft/until": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", - "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@oxc-resolver/binding-darwin-arm64": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.2.0.tgz", - "integrity": "sha512-ruKLkS+Dm/YIJaUhzEB7zPI+jh3EXxu0QnNV8I7t9jf0lpD2VnltuyRbhrbJEkksklZj//xCMyFFsILGjiU2Mg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@oxc-resolver/binding-darwin-x64": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.2.0.tgz", - "integrity": "sha512-0zhgNUm5bYezdSFOg3FYhtVP83bAq7FTV/3suGQDl/43MixfQG7+bl+hlrP4mz6WlD2SUb2u9BomnJWl1uey9w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@oxc-resolver/binding-freebsd-x64": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.2.0.tgz", - "integrity": "sha512-SHOxfCcZV1axeIGfyeD1BkdLvfQgjmPy18tO0OUXGElcdScxD6MqU5rj/AVtiuBT+51GtFfOKlwl1+BdVwhD1A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@oxc-resolver/binding-linux-arm-gnueabihf": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.2.0.tgz", - "integrity": "sha512-mgEkYrJ+N90sgEDqEZ07zH+4I1D28WjqAhdzfW3aS2x2vynVpoY9jWfHuH8S62vZt3uATJrTKTRa8CjPWEsrdw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@oxc-resolver/binding-linux-arm64-gnu": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.2.0.tgz", - "integrity": "sha512-BhEzNLjn4HjP8+Q18D3/jeIDBxW7OgoJYIjw2CaaysnYneoTlij8hPTKxHfyqq4IGM3fFs9TLR/k338M3zkQ7g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@oxc-resolver/binding-linux-arm64-musl": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.2.0.tgz", - "integrity": "sha512-yxbMYUgRmN2V8x8XoxmD/Qq6aG7YIW3ToMDILfmcfeeRRVieEJ3DOWBT0JSE+YgrOy79OyFDH/1lO8VnqLmDQQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@oxc-resolver/binding-linux-riscv64-gnu": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.2.0.tgz", - "integrity": "sha512-QG1UfgC2N2qhW1tOnDCgB/26vn1RCshR5sYPhMeaxO1gMQ3kEKbZ3QyBXxrG1IX5qsXYj5hPDJLDYNYUjRcOpg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@oxc-resolver/binding-linux-s390x-gnu": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.2.0.tgz", - "integrity": "sha512-uqTDsQdi6mrkSV1gvwbuT8jf/WFl6qVDVjNlx7IPSaAByrNiJfPrhTmH8b+Do58Dylz7QIRZgxQ8CHIZSyBUdg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@oxc-resolver/binding-linux-x64-gnu": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.2.0.tgz", - "integrity": "sha512-GZdHXhJ7p6GaQg9MjRqLebwBf8BLvGIagccI6z5yMj4fV3LU4QuDfwSEERG+R6oQ/Su9672MBqWwncvKcKT68w==", - "cpu": [ - "x64" - ], + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz", + "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "@octokit/types": "^13.1.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + }, + "engines": { + "node": ">= 18" + } }, - "node_modules/@oxc-resolver/binding-linux-x64-musl": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.2.0.tgz", - "integrity": "sha512-YBAC3GOicYznReG2twE7oFPSeK9Z1f507z1EYWKg6HpGYRYRlJyszViu7PrhMT85r/MumDTs429zm+CNqpFWOA==", - "cpu": [ - "x64" - ], + "node_modules/@octokit/types": { + "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } }, - "node_modules/@oxc-resolver/binding-wasm32-wasi": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.2.0.tgz", - "integrity": "sha512-+qlIg45CPVPy+Jn3vqU1zkxA/AAv6e/2Ax/ImX8usZa8Tr2JmQn/93bmSOOOnr9fXRV9d0n4JyqYzSWxWPYDEw==", - "cpu": [ - "wasm32" - ], + "node_modules/@open-draft/deferred-promise": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", + "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@open-draft/logger": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", + "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.11" - }, - "engines": { - "node": ">=14.0.0" + "is-node-process": "^1.2.0", + "outvariant": "^1.4.0" } }, - "node_modules/@oxc-resolver/binding-win32-arm64-msvc": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.2.0.tgz", - "integrity": "sha512-AI4KIpS8Zf6vwfOPk0uQPSC0pQ1m5HU4hCbtrgL21JgJSlnJaeEu3/aoOBB45AXKiExBU9R+CDR7aSnW7uhc5A==", - "cpu": [ - "arm64" - ], + "node_modules/@open-draft/until": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", + "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "license": "MIT" }, - "node_modules/@oxc-resolver/binding-win32-x64-msvc": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.2.0.tgz", - "integrity": "sha512-r19cQc7HaEJ76HFsMsbiKMTIV2YqFGSof8H5hB7e5Jkb/23Y8Isv1YrSzkDaGhcw02I/COsrPo+eEmjy35eFuA==", + "node_modules/@oxc-resolver/binding-darwin-x64": { + "version": "11.6.2", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.6.2.tgz", + "integrity": "sha512-UmGEeXk4/E3ubBWgoehVEQSBTEpl+UjZqY55sB+/5NHYFPMxY6PgG8y7dGZhyWPvwVW/pS/drnG3gptAjwF8cg==", "cpu": [ "x64" ], @@ -3918,7 +3266,7 @@ "license": "MIT", "optional": true, "os": [ - "win32" + "darwin" ] }, "node_modules/@pkgjs/parseargs": { @@ -3933,9 +3281,9 @@ } }, "node_modules/@radix-ui/primitive": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz", - "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", + "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", "license": "MIT" }, "node_modules/@radix-ui/react-arrow": { @@ -3962,16 +3310,16 @@ } }, "node_modules/@radix-ui/react-collapsible": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.11.tgz", - "integrity": "sha512-2qrRsVGSCYasSz1RFOorXwl0H7g7J1frQtgpQgYrt+MOidtPAINHn9CPovQXb83r8ahapdx3Tu0fa/pdFFSdPg==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.12.tgz", + "integrity": "sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.2", + "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1" @@ -4048,14 +3396,14 @@ } }, "node_modules/@radix-ui/react-context-menu": { - "version": "2.2.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context-menu/-/react-context-menu-2.2.15.tgz", - "integrity": "sha512-UsQUMjcYTsBjTSXw0P3GO0werEQvUY2plgRQuKoCTtkNr45q1DiL51j4m7gxhABzZ0BadoXNsIbg7F3KwiUBbw==", + "version": "2.2.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context-menu/-/react-context-menu-2.2.16.tgz", + "integrity": "sha512-O8morBEW+HsVG28gYDZPTrT9UUovQUlJue5YO836tiTJhuIWBm/zQHc7j388sHWtdH/xUZurK9olD2+pcqx5ww==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.2", + "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-menu": "2.1.15", + "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" @@ -4076,20 +3424,20 @@ } }, "node_modules/@radix-ui/react-dialog": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.14.tgz", - "integrity": "sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", + "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.2", + "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.10", - "@radix-ui/react-focus-guards": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", @@ -4127,12 +3475,12 @@ } }, "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.10.tgz", - "integrity": "sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", + "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.2", + "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", @@ -4154,16 +3502,16 @@ } }, "node_modules/@radix-ui/react-dropdown-menu": { - "version": "2.1.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.15.tgz", - "integrity": "sha512-mIBnOjgwo9AH3FyKaSWoSu/dYj6VdhJ7frEPiGTeXCdUFHjl9h3mFh2wwhEtINOmYXWhdpf1rY2minFsmaNgVQ==", + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.16.tgz", + "integrity": "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.2", + "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-menu": "2.1.15", + "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, @@ -4183,9 +3531,9 @@ } }, "node_modules/@radix-ui/react-focus-guards": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.2.tgz", - "integrity": "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", + "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -4223,12 +3571,12 @@ } }, "node_modules/@radix-ui/react-form": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-form/-/react-form-0.1.7.tgz", - "integrity": "sha512-IXLKFnaYvFg/KkeV5QfOX7tRnwHXp127koOFUjLWMTrRv5Rny3DQcAtIFFeA/Cli4HHM8DuJCXAUsgnFVJndlw==", + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-form/-/react-form-0.1.8.tgz", + "integrity": "sha512-QM70k4Zwjttifr5a4sZFts9fn8FzHYvQ5PiB19O2HsYibaHSVt9fH9rzB0XZo/YcM+b7t/p7lYCT/F5eOeF5yQ==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.2", + "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", @@ -4292,25 +3640,25 @@ } }, "node_modules/@radix-ui/react-menu": { - "version": "2.1.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.15.tgz", - "integrity": "sha512-tVlmA3Vb9n8SZSd+YSbuFR66l87Wiy4du+YE+0hzKQEANA+7cWKH1WgqcEX4pXqxUFQKrWQGHdvEfw00TjFiew==", + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.16.tgz", + "integrity": "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.2", + "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.10", - "@radix-ui/react-focus-guards": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.7", + "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-roving-focus": "1.1.10", + "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "aria-hidden": "^1.2.4", @@ -4332,9 +3680,9 @@ } }, "node_modules/@radix-ui/react-popper": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.7.tgz", - "integrity": "sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", + "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==", "license": "MIT", "dependencies": { "@floating-ui/react-dom": "^2.0.0", @@ -4388,9 +3736,9 @@ } }, "node_modules/@radix-ui/react-presence": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.4.tgz", - "integrity": "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", + "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", @@ -4459,12 +3807,12 @@ } }, "node_modules/@radix-ui/react-roving-focus": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.10.tgz", - "integrity": "sha512-dT9aOXUen9JSsxnMPv/0VqySQf5eDQ6LCk5Sw28kamz8wSOW2bJdlX2Bg5VUIIcV+6XlHpWTIuTPCf/UNIyq8Q==", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", + "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.2", + "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", @@ -4633,337 +3981,71 @@ } } }, - "node_modules/@radix-ui/react-use-size": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", - "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/rect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", - "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", - "license": "MIT" - }, - "node_modules/@repeaterjs/repeater": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@repeaterjs/repeater/-/repeater-3.0.6.tgz", - "integrity": "sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-beta.11", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.11.tgz", - "integrity": "sha512-L/gAA/hyCSuzTF1ftlzUSI/IKr2POHsv1Dd78GfqkR83KMNuswWD61JxGV2L7nRwBBBSDr6R1gCkdTmoN7W4ag==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rollup/pluginutils": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz", - "integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.43.0.tgz", - "integrity": "sha512-Krjy9awJl6rKbruhQDgivNbD1WuLb8xAclM4IR4cN5pHGAs2oIMMQJEiC3IC/9TZJ+QZkmZhlMO/6MBGxPidpw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.43.0.tgz", - "integrity": "sha512-ss4YJwRt5I63454Rpj+mXCXicakdFmKnUNxr1dLK+5rv5FJgAxnN7s31a5VchRYxCFWdmnDWKd0wbAdTr0J5EA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.43.0.tgz", - "integrity": "sha512-eKoL8ykZ7zz8MjgBenEF2OoTNFAPFz1/lyJ5UmmFSz5jW+7XbH1+MAgCVHy72aG59rbuQLcJeiMrP8qP5d/N0A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.43.0.tgz", - "integrity": "sha512-SYwXJgaBYW33Wi/q4ubN+ldWC4DzQY62S4Ll2dgfr/dbPoF50dlQwEaEHSKrQdSjC6oIe1WgzosoaNoHCdNuMg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.43.0.tgz", - "integrity": "sha512-SV+U5sSo0yujrjzBF7/YidieK2iF6E7MdF6EbYxNz94lA+R0wKl3SiixGyG/9Klab6uNBIqsN7j4Y/Fya7wAjQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.43.0.tgz", - "integrity": "sha512-J7uCsiV13L/VOeHJBo5SjasKiGxJ0g+nQTrBkAsmQBIdil3KhPnSE9GnRon4ejX1XDdsmK/l30IYLiAaQEO0Cg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.43.0.tgz", - "integrity": "sha512-gTJ/JnnjCMc15uwB10TTATBEhK9meBIY+gXP4s0sHD1zHOaIh4Dmy1X9wup18IiY9tTNk5gJc4yx9ctj/fjrIw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.43.0.tgz", - "integrity": "sha512-ZJ3gZynL1LDSIvRfz0qXtTNs56n5DI2Mq+WACWZ7yGHFUEirHBRt7fyIk0NsCKhmRhn7WAcjgSkSVVxKlPNFFw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.43.0.tgz", - "integrity": "sha512-8FnkipasmOOSSlfucGYEu58U8cxEdhziKjPD2FIa0ONVMxvl/hmONtX/7y4vGjdUhjcTHlKlDhw3H9t98fPvyA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.43.0.tgz", - "integrity": "sha512-KPPyAdlcIZ6S9C3S2cndXDkV0Bb1OSMsX0Eelr2Bay4EsF9yi9u9uzc9RniK3mcUGCLhWY9oLr6er80P5DE6XA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.43.0.tgz", - "integrity": "sha512-HPGDIH0/ZzAZjvtlXj6g+KDQ9ZMHfSP553za7o2Odegb/BEfwJcR0Sw0RLNpQ9nC6Gy8s+3mSS9xjZ0n3rhcYg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.43.0.tgz", - "integrity": "sha512-gEmwbOws4U4GLAJDhhtSPWPXUzDfMRedT3hFMyRAvM9Mrnj+dJIFIeL7otsv2WF3D7GrV0GIewW0y28dOYWkmw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.43.0.tgz", - "integrity": "sha512-XXKvo2e+wFtXZF/9xoWohHg+MuRnvO29TI5Hqe9xwN5uN8NKUYy7tXUG3EZAlfchufNCTHNGjEx7uN78KsBo0g==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.43.0.tgz", - "integrity": "sha512-ruf3hPWhjw6uDFsOAzmbNIvlXFXlBQ4nk57Sec8E8rUxs/AI4HD6xmiiasOOx/3QxS2f5eQMKTAwk7KHwpzr/Q==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.43.0.tgz", - "integrity": "sha512-QmNIAqDiEMEvFV15rsSnjoSmO0+eJLoKRD9EAa9rrYNwO/XRCtOGM3A5A0X+wmG+XRrw9Fxdsw+LnyYiZWWcVw==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.43.0.tgz", - "integrity": "sha512-jAHr/S0iiBtFyzjhOkAics/2SrXE092qyqEg96e90L3t9Op8OTzS6+IX0Fy5wCt2+KqeHAkti+eitV0wvblEoQ==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", + "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.43.0.tgz", - "integrity": "sha512-3yATWgdeXyuHtBhrLt98w+5fKurdqvs8B53LaoKD7P7H7FKOONLsBVMNl9ghPQZQuYcceV5CDyPfyfGpMWD9mQ==", - "cpu": [ - "x64" - ], + "node_modules/@radix-ui/rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", + "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", + "license": "MIT" + }, + "node_modules/@repeaterjs/repeater": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@repeaterjs/repeater/-/repeater-3.0.6.tgz", + "integrity": "sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "license": "MIT" }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.43.0.tgz", - "integrity": "sha512-wVzXp2qDSCOpcBCT5WRWLmpJRIzv23valvcTwMHEobkjippNf+C3ys/+wf07poPkeNix0paTNemB2XrHr2TnGw==", - "cpu": [ - "arm64" - ], + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "license": "MIT" }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.43.0.tgz", - "integrity": "sha512-fYCTEyzf8d+7diCw8b+asvWDCLMjsCEA8alvtAutqJOJp/wL5hs1rWSqJ1vkjgW0L2NB4bsYJrpKkiIPRR9dvw==", - "cpu": [ - "ia32" - ], + "node_modules/@rollup/pluginutils": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.2.0.tgz", + "integrity": "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.43.0.tgz", - "integrity": "sha512-SnGhLiE5rlK0ofq8kzuDkM0g7FN1s5VYY+YSMTibP7CqShxCQvqtNxTARS4xX4PFJfHjG0ZQYX9iGzI3FQh5Aw==", + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.48.1.tgz", + "integrity": "sha512-upGEY7Ftw8M6BAJyGwnwMw91rSqXTcOKZnnveKrVWsMTF8/k5mleKSuh7D4v4IV1pLxKAk3Tbs0Lo9qYmii5mQ==", "cpu": [ "x64" ], @@ -4971,7 +4053,7 @@ "license": "MIT", "optional": true, "os": [ - "win32" + "darwin" ] }, "node_modules/@scarf/scarf": { @@ -5252,43 +4334,13 @@ "react-dom": ">=18.0.0 || >=19.0.0" } }, - "node_modules/@tanstack/react-router-devtools/node_modules/@tanstack/router-devtools-core": { - "version": "1.131.27", - "resolved": "https://registry.npmjs.org/@tanstack/router-devtools-core/-/router-devtools-core-1.131.27.tgz", - "integrity": "sha512-upoMv/uq1CQdrOyBO2h6CLXI1Ym7Rawoovt26fN1Wl+RMXqKGVpHAXYuKpugdFMFhFieccKVYcrj9NP4V5BIDw==", - "dev": true, - "license": "MIT", - "dependencies": { - "clsx": "^2.1.1", - "goober": "^2.1.16", - "solid-js": "^1.9.5" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "@tanstack/router-core": "^1.131.27", - "csstype": "^3.0.10", - "solid-js": ">=1.9.5", - "tiny-invariant": "^1.3.3" - }, - "peerDependenciesMeta": { - "csstype": { - "optional": true - } - } - }, "node_modules/@tanstack/react-store": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@tanstack/react-store/-/react-store-0.7.1.tgz", - "integrity": "sha512-qUTEKdId6QPWGiWyKAPf/gkN29scEsz6EUSJ0C3HgLMgaqTAyBsQ2sMCfGVcqb+kkhEXAdjleCgH6LAPD6f2sA==", + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@tanstack/react-store/-/react-store-0.7.4.tgz", + "integrity": "sha512-DyG1e5Qz/c1cNLt/NdFbCA7K1QGuFXQYT6EfUltYMJoQ4LzBOGnOl5IjuxepNcRtmIKkGpmdMzdFZEkevgU9bQ==", "license": "MIT", "dependencies": { - "@tanstack/store": "0.7.1", + "@tanstack/store": "0.7.4", "use-sync-external-store": "^1.5.0" }, "funding": { @@ -5322,6 +4374,36 @@ "url": "https://github.com/sponsors/tannerlinsley" } }, + "node_modules/@tanstack/router-devtools-core": { + "version": "1.131.27", + "resolved": "https://registry.npmjs.org/@tanstack/router-devtools-core/-/router-devtools-core-1.131.27.tgz", + "integrity": "sha512-upoMv/uq1CQdrOyBO2h6CLXI1Ym7Rawoovt26fN1Wl+RMXqKGVpHAXYuKpugdFMFhFieccKVYcrj9NP4V5BIDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "clsx": "^2.1.1", + "goober": "^2.1.16", + "solid-js": "^1.9.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "@tanstack/router-core": "^1.131.27", + "csstype": "^3.0.10", + "solid-js": ">=1.9.5", + "tiny-invariant": "^1.3.3" + }, + "peerDependenciesMeta": { + "csstype": { + "optional": true + } + } + }, "node_modules/@tanstack/router-generator": { "version": "1.131.27", "resolved": "https://registry.npmjs.org/@tanstack/router-generator/-/router-generator-1.131.27.tgz", @@ -5401,14 +4483,15 @@ } }, "node_modules/@tanstack/router-plugin/node_modules/unplugin": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.5.tgz", - "integrity": "sha512-RyWSb5AHmGtjjNQ6gIlA67sHOsWpsbWpwDokLwTcejVdOjEkJZh7QKu14J00gDDVSh8kGH4KYC/TNBceXFZhtw==", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.8.tgz", + "integrity": "sha512-lkaSIlxceytPyt9yfb1h7L9jDFqwMqvUZeGsKB7Z8QrvAO3xZv2S+xMQQYzxk0AGJHcQhbcvhKEstrMy99jnuQ==", "dev": true, "license": "MIT", "dependencies": { - "acorn": "^8.14.1", - "picomatch": "^4.0.2", + "@jridgewell/remapping": "^2.3.5", + "acorn": "^8.15.0", + "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" }, "engines": { @@ -5438,9 +4521,9 @@ } }, "node_modules/@tanstack/store": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@tanstack/store/-/store-0.7.1.tgz", - "integrity": "sha512-PjUQKXEXhLYj2X5/6c1Xn/0/qKY0IVFxTJweopRfF26xfjVyb14yALydJrHupDh3/d+1WKmfEgZPBVCmDkzzwg==", + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@tanstack/store/-/store-0.7.4.tgz", + "integrity": "sha512-F1XqZQici1Aq6WigEfcxJSml92nW+85Om8ElBMokPNg5glCYVOmPkZGIQeieYFxcPiKTfwo0MTOQpUyJtwncrg==", "license": "MIT", "funding": { "type": "github", @@ -5462,9 +4545,9 @@ } }, "node_modules/@testing-library/dom": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", - "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", "dev": true, "license": "MIT", "peer": true, @@ -5473,9 +4556,9 @@ "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", - "chalk": "^4.1.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", + "picocolors": "1.1.1", "pretty-format": "^27.0.2" }, "engines": { @@ -5551,15 +4634,23 @@ "@testing-library/dom": ">=7.21.4" } }, - "node_modules/@tybys/wasm-util": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", - "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==", + "node_modules/@theguild/federation-composition": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@theguild/federation-composition/-/federation-composition-0.19.1.tgz", + "integrity": "sha512-E4kllHSRYh+FsY0VR+fwl0rmWhDV8xUgWawLZTXmy15nCWQwj0BDsoEpdEXjPh7xes+75cRaeJcSbZ4jkBuSdg==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "tslib": "^2.4.0" + "constant-case": "^3.0.4", + "debug": "4.4.1", + "json5": "^2.2.3", + "lodash.sortby": "^4.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "graphql": "^16.0.0" } }, "node_modules/@types/aria-query": { @@ -5606,13 +4697,13 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", - "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.20.7" + "@babel/types": "^7.28.2" } }, "node_modules/@types/chai": { @@ -5688,7 +4779,6 @@ "version": "19.1.10", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.10.tgz", "integrity": "sha512-EhBeSYX0Y6ye8pNebpKrwFJq7BoQ8J5SO6NlvNwwHjSj6adXJViPQrKlsyPw7hLBLvckEMO1yxeGdR82YBBlDg==", - "devOptional": true, "license": "MIT", "dependencies": { "csstype": "^3.0.2" @@ -5698,7 +4788,6 @@ "version": "19.1.7", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.7.tgz", "integrity": "sha512-i5ZzwYpqjmrKenzkoLM2Ibzt6mAsM7pxB6BCIouEVVmgiqaMj1TjaK7hnA36hbW5aZv20kx7Lw6hWzPWg0Rurw==", - "devOptional": true, "license": "MIT", "peerDependencies": { "@types/react": "^19.0.0" @@ -5750,9 +4839,8 @@ } }, "node_modules/@vector-im/compound-design-tokens": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@vector-im/compound-design-tokens/-/compound-design-tokens-5.0.2.tgz", - "integrity": "sha512-LcdrGY9qktuSs9TNX+DdGGq64vP7Qk5FiiqtZBr4PEk+hCQPEyRtKDfkXbAST+0tpAjUqVp5pzlOqNUKhpIhfg==", + "version": "4.0.4", + "resolved": "git+ssh://git@github.com/tchapgouv/compound-design-tokens.git#3d218b2fe64614467f296b8a8c975f59db8c2914", "license": "SEE LICENSE IN README.md", "peerDependencies": { "@types/react": "*", @@ -5797,16 +4885,16 @@ } }, "node_modules/@vitejs/plugin-react": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.5.2.tgz", - "integrity": "sha512-QNVT3/Lxx99nMQWJWF7K4N6apUEuT0KlZA3mx/mVaoGj3smm/8rc8ezz15J1pcbcjDK0V15rpHetVfya08r76Q==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.27.4", + "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", - "@rolldown/pluginutils": "1.0.0-beta.11", + "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, @@ -5814,7 +4902,7 @@ "node": "^14.18.0 || >=16.0.0" }, "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "node_modules/@vitest/coverage-v8": { @@ -5856,6 +4944,7 @@ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", "dev": true, + "license": "MIT", "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", @@ -5909,6 +4998,7 @@ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", "dev": true, + "license": "MIT", "dependencies": { "tinyrainbow": "^2.0.0" }, @@ -5951,6 +5041,7 @@ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", "dev": true, + "license": "MIT", "dependencies": { "tinyspy": "^4.0.3" }, @@ -5963,6 +5054,7 @@ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", "dev": true, + "license": "MIT", "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", @@ -5987,13 +5079,13 @@ } }, "node_modules/@whatwg-node/fetch": { - "version": "0.10.8", - "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.8.tgz", - "integrity": "sha512-Rw9z3ctmeEj8QIB9MavkNJqekiu9usBCSMZa+uuAvM0lF3v70oQVCXNppMIqaV6OTZbdaHF1M2HLow58DEw+wg==", + "version": "0.10.10", + "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.10.tgz", + "integrity": "sha512-watz4i/Vv4HpoJ+GranJ7HH75Pf+OkPQ63NoVmru6Srgc8VezTArB00i/oQlnn0KWh14gM42F22Qcc9SU9mo/w==", "dev": true, "license": "MIT", "dependencies": { - "@whatwg-node/node-fetch": "^0.7.21", + "@whatwg-node/node-fetch": "^0.7.25", "urlpattern-polyfill": "^10.0.0" }, "engines": { @@ -6001,9 +5093,9 @@ } }, "node_modules/@whatwg-node/node-fetch": { - "version": "0.7.21", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.21.tgz", - "integrity": "sha512-QC16IdsEyIW7kZd77aodrMO7zAoDyyqRCTLg+qG4wqtP4JV9AA+p7/lgqMdD29XyiYdVvIdFrfI9yh7B1QvRvw==", + "version": "0.7.25", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.7.25.tgz", + "integrity": "sha512-szCTESNJV+Xd56zU6ShOi/JWROxE9IwCic8o5D9z5QECZloas6Ez5tUuKqXTAdu6fHFx1t6C+5gwj8smzOLjtg==", "dev": true, "license": "MIT", "dependencies": { @@ -6058,9 +5150,9 @@ } }, "node_modules/agent-base": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", - "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "dev": true, "license": "MIT", "engines": { @@ -6244,13 +5336,13 @@ } }, "node_modules/ast-v8-to-istanbul": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.3.tgz", - "integrity": "sha512-MuXMrSLVVoA6sYN/6Hke18vMzrT4TZNbZIj/hvh0fnYFpO+/kFXcLIaiPwXXWaQUPg4yJD8fj+lfJ7/1EBconw==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.4.tgz", + "integrity": "sha512-cxrAnZNLBnQwBPByK4CeDaw5sWZtMilJE/Q3iDA0aamgaIVNDF9T6K2/8DfYDZEejZ2jNnDrG9m8MY72HFd0KA==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", + "@jridgewell/trace-mapping": "^0.3.29", "estree-walker": "^3.0.3", "js-tokens": "^9.0.1" } @@ -6407,9 +5499,9 @@ "license": "MIT" }, "node_modules/bare-events": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.4.tgz", - "integrity": "sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.6.1.tgz", + "integrity": "sha512-AuTJkq9XmE6Vk0FJVNq5QxETrSA/vKHarWVBG5l/JbdCL1prJemiyJqUS0jrlXO0MftuPq4m3YVYhoNc5+aE/g==", "dev": true, "license": "Apache-2.0", "optional": true @@ -6660,9 +5752,9 @@ } }, "node_modules/browserslist": { - "version": "4.25.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz", - "integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==", + "version": "4.25.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.3.tgz", + "integrity": "sha512-cDGv1kkDI4/0e5yON9yM5G/0A5u8sf5TnmdX5C9qHzI9PPu++sQ9zjm1k9NiOrf3riY4OkK0zSGqfvJyJsgCBQ==", "dev": true, "funding": [ { @@ -6680,8 +5772,8 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001718", - "electron-to-chromium": "^1.5.160", + "caniuse-lite": "^1.0.30001735", + "electron-to-chromium": "^1.5.204", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.3" }, @@ -6798,9 +5890,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001722", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001722.tgz", - "integrity": "sha512-DCQHBBZtiK6JVkAGw7drvAMK0Q0POD/xZvEmDp6baiMMP6QXXk9HpD6mNYBZWhOPG6LvIDb82ITqtWjhDckHCA==", + "version": "1.0.30001737", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001737.tgz", + "integrity": "sha512-BiloLiXtQNrY5UyF0+1nSJLXUENuhka2pzy2Fx5pGxqavdrxSCW4U6Pn/PoG3Efspi2frRbHpBV2XsrPE6EDlw==", "dev": true, "funding": [ { @@ -6831,9 +5923,9 @@ } }, "node_modules/chai": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", - "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", "dev": true, "license": "MIT", "dependencies": { @@ -6844,7 +5936,7 @@ "pathval": "^2.0.0" }, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/chalk": { @@ -6922,9 +6014,9 @@ } }, "node_modules/cheerio": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.1.0.tgz", - "integrity": "sha512-+0hMx9eYhJvWbgpKV9hN7jg0JcwydpopZE4hgi+KvQtByZXPp04NiCWU0LzcAbP63abZckIHkTQaXVF52mX3xQ==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.1.2.tgz", + "integrity": "sha512-IkxPpb5rS/d1IiLbHMgfPuS0FgiWTtFIm/Nj+2woXDLTZ7fOT2eqzgYbdMlLweqlHbsZjxEChoVK+7iph7jyQg==", "dev": true, "license": "MIT", "dependencies": { @@ -6932,16 +6024,16 @@ "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", - "encoding-sniffer": "^0.2.0", + "encoding-sniffer": "^0.2.1", "htmlparser2": "^10.0.0", "parse5": "^7.3.0", "parse5-htmlparser2-tree-adapter": "^7.1.0", "parse5-parser-stream": "^7.1.2", - "undici": "^7.10.0", + "undici": "^7.12.0", "whatwg-mimetype": "^4.0.0" }, "engines": { - "node": ">=18.17" + "node": ">=20.18.1" }, "funding": { "url": "https://github.com/cheeriojs/cheerio?sponsor=1" @@ -6966,9 +6058,9 @@ } }, "node_modules/cheerio/node_modules/undici": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.10.0.tgz", - "integrity": "sha512-u5otvFBOBZvmdjWLVW+5DAc9Nkq8f24g0O9oY7qw2JVIF1VocIFoyz9JFkuVOS2j41AufeO0xnlweJ2RLT8nGw==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.15.0.tgz", + "integrity": "sha512-7oZJCPvvMvTd0OlqWsIxTuItTpJBpU1tcbVl24FMn3xt3+VSunwUasmfPJRE57oNO1KsZ4PgA1xTdAX4hq8NyQ==", "dev": true, "license": "MIT", "engines": { @@ -7228,7 +6320,8 @@ "node_modules/cookie-es": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.2.tgz", - "integrity": "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==" + "integrity": "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==", + "license": "MIT" }, "node_modules/core-util-is": { "version": "1.0.3", @@ -7303,9 +6396,9 @@ } }, "node_modules/css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -7320,9 +6413,9 @@ } }, "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -7356,7 +6449,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "devOptional": true, "license": "MIT" }, "node_modules/data-uri-to-buffer": { @@ -7636,9 +6728,9 @@ } }, "node_modules/dotenv": { - "version": "16.5.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", - "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", "dev": true, "license": "BSD-2-Clause", "engines": { @@ -7666,9 +6758,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.166", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.166.tgz", - "integrity": "sha512-QPWqHL0BglzPYyJJ1zSSmwFFL6MFXhbACOCcsCdUMCkzPdS9/OIBVxg516X/Ado2qwAq8k0nJJ7phQPCqiaFAw==", + "version": "1.5.208", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.208.tgz", + "integrity": "sha512-ozZyibehoe7tOhNaf16lKmljVf+3npZcJIEbJRVftVsmAg5TeA1mGS9dVCZzOwr2xT7xK15V0p7+GZqSPgkuPg==", "dev": true, "license": "ISC" }, @@ -7738,9 +6830,9 @@ "license": "MIT" }, "node_modules/esbuild": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz", - "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -7751,31 +6843,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.5", - "@esbuild/android-arm": "0.25.5", - "@esbuild/android-arm64": "0.25.5", - "@esbuild/android-x64": "0.25.5", - "@esbuild/darwin-arm64": "0.25.5", - "@esbuild/darwin-x64": "0.25.5", - "@esbuild/freebsd-arm64": "0.25.5", - "@esbuild/freebsd-x64": "0.25.5", - "@esbuild/linux-arm": "0.25.5", - "@esbuild/linux-arm64": "0.25.5", - "@esbuild/linux-ia32": "0.25.5", - "@esbuild/linux-loong64": "0.25.5", - "@esbuild/linux-mips64el": "0.25.5", - "@esbuild/linux-ppc64": "0.25.5", - "@esbuild/linux-riscv64": "0.25.5", - "@esbuild/linux-s390x": "0.25.5", - "@esbuild/linux-x64": "0.25.5", - "@esbuild/netbsd-arm64": "0.25.5", - "@esbuild/netbsd-x64": "0.25.5", - "@esbuild/openbsd-arm64": "0.25.5", - "@esbuild/openbsd-x64": "0.25.5", - "@esbuild/sunos-x64": "0.25.5", - "@esbuild/win32-arm64": "0.25.5", - "@esbuild/win32-ia32": "0.25.5", - "@esbuild/win32-x64": "0.25.5" + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" } }, "node_modules/esbuild-register": { @@ -7843,9 +6936,9 @@ } }, "node_modules/expect-type": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.1.tgz", - "integrity": "sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", + "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -7939,11 +7032,14 @@ } }, "node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -8024,16 +7120,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/find-up/node_modules/path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, "node_modules/foreground-child": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", @@ -8052,9 +7138,9 @@ } }, "node_modules/formatly": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/formatly/-/formatly-0.2.4.tgz", - "integrity": "sha512-lIN7GpcvX/l/i24r/L9bnJ0I8Qn01qijWpQpDDvTLL29nKqSaJJu4h20+7VJ6m2CAhQ2/En/GbxDiHCzq/0MyA==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/formatly/-/formatly-0.3.0.tgz", + "integrity": "sha512-9XNj/o4wrRFyhSMJOvsuyMwy8aUfBaZ1VrqHVfohyXf0Sw0e+yfKG+xZaY3arGCOMdwFsqObtzVOc1gU9KiT9w==", "dev": true, "license": "MIT", "dependencies": { @@ -8095,9 +7181,9 @@ } }, "node_modules/fs-extra": { - "version": "11.3.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", - "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.1.tgz", + "integrity": "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==", "dev": true, "license": "MIT", "dependencies": { @@ -8330,16 +7416,6 @@ "node": ">=10.13.0" } }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/globby": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", @@ -8421,9 +7497,9 @@ } }, "node_modules/graphql-config/node_modules/jiti": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", - "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", + "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", "dev": true, "license": "MIT", "bin": { @@ -8461,9 +7537,9 @@ } }, "node_modules/graphql-ws": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-6.0.5.tgz", - "integrity": "sha512-HzYw057ch0hx2gZjkbgk1pur4kAtgljlWRP+Gccudqm3BRrTpExjWCQ9OHdIsq47Y6lHL++1lTvuQHhgRRcevw==", + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-6.0.6.tgz", + "integrity": "sha512-zgfER9s+ftkGKUZgc0xbx8T7/HMO4AV5/YuYiFc+AtgcO5T0v8AxYYNQ+ltzuzDZgNkYJaFspm5MMYLjQzrkmw==", "dev": true, "license": "MIT", "engines": { @@ -8517,9 +7593,9 @@ } }, "node_modules/happy-dom/node_modules/@types/node": { - "version": "20.19.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.0.tgz", - "integrity": "sha512-hfrc+1tud1xcdVTABC2JiomZJEklMcXYNTVtZLAeqTVWD+qL5jkHKT+1lOtqDdGxt+mB53DTtiz673vfjU8D1Q==", + "version": "20.19.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.11.tgz", + "integrity": "sha512-uug3FEEGv0r+jrecvUUpbY8lLisvIjg6AAic6a2bSP5OEOLeJsDSnvhCDov7ipFFMXS3orMpzlmi0ZcuGkBbow==", "dev": true, "license": "MIT", "dependencies": { @@ -9235,9 +8311,10 @@ "license": "MIT" }, "node_modules/isbot": { - "version": "5.1.28", - "resolved": "https://registry.npmjs.org/isbot/-/isbot-5.1.28.tgz", - "integrity": "sha512-qrOp4g3xj8YNse4biorv6O5ZShwsJM0trsoda4y7j/Su7ZtTTfVXFzbKkpgcSoDrHS8FcTuUwcU04YimZlZOxw==", + "version": "5.1.30", + "resolved": "https://registry.npmjs.org/isbot/-/isbot-5.1.30.tgz", + "integrity": "sha512-3wVJEonAns1OETX83uWsk5IAne2S5zfDcntD2hbtU23LelSqNXzXs9zKjMPOLMzroCgIjCfjYAEHrd2D6FOkiA==", + "license": "Unlicense", "engines": { "node": ">=18" } @@ -9300,9 +8377,9 @@ } }, "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -9417,9 +8494,9 @@ } }, "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", "dev": true, "license": "MIT", "dependencies": { @@ -9430,9 +8507,9 @@ } }, "node_modules/knip": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/knip/-/knip-5.62.0.tgz", - "integrity": "sha512-hfTUVzmrMNMT1khlZfAYmBABeehwWUUrizLQoLamoRhSFkygsGIXWx31kaWKBgEaIVL77T3Uz7IxGvSw+CvQ6A==", + "version": "5.63.0", + "resolved": "https://registry.npmjs.org/knip/-/knip-5.63.0.tgz", + "integrity": "sha512-xIFIi/uvLW0S/AQqwggN6UVRKBOQ1Ya7jBfQzllswZplr2si5C616/5wCcWc/eoi1PLJgPgJQLxqYq1aiYpqwg==", "dev": true, "funding": [ { @@ -9442,24 +8519,20 @@ { "type": "opencollective", "url": "https://opencollective.com/knip" - }, - { - "type": "polar", - "url": "https://polar.sh/webpro-nl" } ], "license": "ISC", "dependencies": { "@nodelib/fs.walk": "^1.2.3", "fast-glob": "^3.3.3", - "formatly": "^0.2.4", - "jiti": "^2.4.2", + "formatly": "^0.3.0", + "jiti": "^2.5.1", "js-yaml": "^4.1.0", "minimist": "^1.2.8", - "oxc-resolver": "^11.1.0", + "oxc-resolver": "^11.6.2", "picocolors": "^1.1.1", "picomatch": "^4.0.1", - "smol-toml": "^1.3.4", + "smol-toml": "^1.4.1", "strip-json-comments": "5.0.2", "zod": "^3.22.4", "zod-validation-error": "^3.0.3" @@ -9477,9 +8550,9 @@ } }, "node_modules/knip/node_modules/jiti": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", - "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", + "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", "dev": true, "license": "MIT", "bin": { @@ -9660,9 +8733,9 @@ } }, "node_modules/loupe": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.4.tgz", - "integrity": "sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", "dev": true, "license": "MIT" }, @@ -9708,13 +8781,13 @@ } }, "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "version": "0.30.18", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz", + "integrity": "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" + "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/magicast": { @@ -9935,9 +9008,9 @@ "license": "MIT" }, "node_modules/msw": { - "version": "2.11.1", - "resolved": "https://registry.npmjs.org/msw/-/msw-2.11.1.tgz", - "integrity": "sha512-dGSRx0AJmQVQfpGXTsAAq4JFdwdhOBdJ6sJS/jnN0ac3s0NZB6daacHF1z5Pefx+IejmvuiLWw260RlyQOf3sQ==", + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.11.2.tgz", + "integrity": "sha512-MI54hLCsrMwiflkcqlgYYNJJddY5/+S0SnONvhv1owOplvqohKSQyGejpNdUGyCwgs4IH7PqaNbPw/sKOEze9Q==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -9956,6 +9029,7 @@ "outvariant": "^1.4.3", "path-to-regexp": "^6.3.0", "picocolors": "^1.1.1", + "rettime": "^0.7.0", "strict-event-emitter": "^0.5.1", "tough-cookie": "^6.0.0", "type-fest": "^4.26.1", @@ -10043,6 +9117,22 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/napi-postinstall": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.3.tgz", + "integrity": "sha512-uTp172LLXSxuSYHv/kou+f6KW3SMppU9ivthaVTXian9sOt3XM/zHYHpRZiLgQoxeWfYUnslNWQHF1+G71xcow==", + "dev": true, + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, "node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -10259,28 +9349,38 @@ "license": "MIT" }, "node_modules/oxc-resolver": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.2.0.tgz", - "integrity": "sha512-3iJYyIdDZMDoj0ZSVBrI1gUvPBMkDC4gxonBG+7uqUyK5EslG0mCwnf6qhxK8oEU7jLHjbRBNyzflPSd3uvH7Q==", + "version": "11.6.2", + "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.6.2.tgz", + "integrity": "sha512-9lXwNQUzgPs5UgjKig5+EINESHYJCFsRQLzPyjWLc7sshl6ZXvXPiQfEGqUIs2fsd9SdV/jYmL7IuaK43cL0SA==", "dev": true, + "hasInstallScript": true, "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.0" + }, "funding": { "url": "https://github.com/sponsors/Boshen" }, "optionalDependencies": { - "@oxc-resolver/binding-darwin-arm64": "11.2.0", - "@oxc-resolver/binding-darwin-x64": "11.2.0", - "@oxc-resolver/binding-freebsd-x64": "11.2.0", - "@oxc-resolver/binding-linux-arm-gnueabihf": "11.2.0", - "@oxc-resolver/binding-linux-arm64-gnu": "11.2.0", - "@oxc-resolver/binding-linux-arm64-musl": "11.2.0", - "@oxc-resolver/binding-linux-riscv64-gnu": "11.2.0", - "@oxc-resolver/binding-linux-s390x-gnu": "11.2.0", - "@oxc-resolver/binding-linux-x64-gnu": "11.2.0", - "@oxc-resolver/binding-linux-x64-musl": "11.2.0", - "@oxc-resolver/binding-wasm32-wasi": "11.2.0", - "@oxc-resolver/binding-win32-arm64-msvc": "11.2.0", - "@oxc-resolver/binding-win32-x64-msvc": "11.2.0" + "@oxc-resolver/binding-android-arm-eabi": "11.6.2", + "@oxc-resolver/binding-android-arm64": "11.6.2", + "@oxc-resolver/binding-darwin-arm64": "11.6.2", + "@oxc-resolver/binding-darwin-x64": "11.6.2", + "@oxc-resolver/binding-freebsd-x64": "11.6.2", + "@oxc-resolver/binding-linux-arm-gnueabihf": "11.6.2", + "@oxc-resolver/binding-linux-arm-musleabihf": "11.6.2", + "@oxc-resolver/binding-linux-arm64-gnu": "11.6.2", + "@oxc-resolver/binding-linux-arm64-musl": "11.6.2", + "@oxc-resolver/binding-linux-ppc64-gnu": "11.6.2", + "@oxc-resolver/binding-linux-riscv64-gnu": "11.6.2", + "@oxc-resolver/binding-linux-riscv64-musl": "11.6.2", + "@oxc-resolver/binding-linux-s390x-gnu": "11.6.2", + "@oxc-resolver/binding-linux-x64-gnu": "11.6.2", + "@oxc-resolver/binding-linux-x64-musl": "11.6.2", + "@oxc-resolver/binding-wasm32-wasi": "11.6.2", + "@oxc-resolver/binding-win32-arm64-msvc": "11.6.2", + "@oxc-resolver/binding-win32-ia32-msvc": "11.6.2", + "@oxc-resolver/binding-win32-x64-msvc": "11.6.2" } }, "node_modules/p-limit": { @@ -10511,13 +9611,13 @@ } }, "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, "node_modules/path-is-absolute": { @@ -10626,9 +9726,9 @@ "license": "MIT" }, "node_modules/pathval": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", - "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", "dev": true, "license": "MIT", "engines": { @@ -10643,9 +9743,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { @@ -10868,9 +9968,9 @@ "license": "MIT" }, "node_modules/prettier": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", - "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", "bin": { @@ -11043,17 +10143,17 @@ } }, "node_modules/react-docgen": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-8.0.0.tgz", - "integrity": "sha512-kmob/FOTwep7DUWf9KjuenKX0vyvChr3oTdvvPt09V60Iz75FJp+T/0ZeHMbAfJj2WaVWqAPP5Hmm3PYzSPPKg==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-8.0.1.tgz", + "integrity": "sha512-kQKsqPLplY3Hx4jGnM3jpQcG3FQDt7ySz32uTHt3C9HAe45kNXG+3o16Eqn3Fw1GtMfHoN3b4J/z2e6cZJCmqQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.18.9", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9", - "@types/babel__core": "^7.18.0", - "@types/babel__traverse": "^7.18.0", + "@babel/core": "^7.28.0", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.2", + "@types/babel__core": "^7.20.5", + "@types/babel__traverse": "^7.20.7", "@types/doctrine": "^0.0.9", "@types/resolve": "^1.20.2", "doctrine": "^3.0.0", @@ -11069,6 +10169,7 @@ "resolved": "https://registry.npmjs.org/react-docgen-typescript/-/react-docgen-typescript-2.4.0.tgz", "integrity": "sha512-ZtAp5XTO5HRzQctjPU0ybY0RRCQO19X/8fxn3w7y2VVTUbGHDKULPTL4ky3vB05euSgG5NpALhEhDPvQ56wvXg==", "dev": true, + "license": "MIT", "peerDependencies": { "typescript": ">= 4.3.x" } @@ -11441,6 +10542,13 @@ "dev": true, "license": "ISC" }, + "node_modules/rettime": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/rettime/-/rettime-0.7.0.tgz", + "integrity": "sha512-LPRKoHnLKd/r3dVxcwO7vhCW+orkOGj9ViueosEBK6ie89CijnfRlhaDhHq/3Hxu4CkWQtxwlBG0mzTQY6uQjw==", + "dev": true, + "license": "MIT" + }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -11479,39 +10587,16 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rimraf/node_modules/balanced-match": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-3.0.1.tgz", - "integrity": "sha512-vjtV3hiLqYDNRoiAv0zC4QaGAMPomEoq83PRmYIofPswwZurCeWR5LByXm7SyoL0Zh5+2z0+HC7jG8gSZJUh0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 16" - } - }, - "node_modules/rimraf/node_modules/brace-expansion": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-4.0.1.tgz", - "integrity": "sha512-YClrbvTCXGe70pU2JiEiPLYXO9gQkyxYeKpJIQHVS/gOs6EWMQP2RYBwjFLNT322Ji8TOC3IMPfsYCedNpzKfA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^3.0.0" - }, - "engines": { - "node": ">= 18" - } - }, "node_modules/rimraf/node_modules/glob": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.2.tgz", - "integrity": "sha512-YT7U7Vye+t5fZ/QMkBFrTJ7ZQxInIUjwyAjVj84CYXqgBdv30MFUPGnBR6sQaVq6Is15wYJUsnzTuWaGRBhBAQ==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", + "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", "dev": true, "license": "ISC", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^4.0.1", - "minimatch": "^10.0.0", + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.0.3", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" @@ -11553,13 +10638,13 @@ } }, "node_modules/rimraf/node_modules/minimatch": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.2.tgz", - "integrity": "sha512-+9TJCIYXgZ2Dm5LxVCFsa8jOm+evMwXHFI0JM1XROmkfkpz8/iLLDh+TwSmyIBrs6C6Xu9294/fq8cBA+P6AqA==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", + "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^4.0.1" + "@isaacs/brace-expansion": "^5.0.0" }, "engines": { "node": "20 || >=22" @@ -11586,13 +10671,13 @@ } }, "node_modules/rollup": { - "version": "4.43.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.43.0.tgz", - "integrity": "sha512-wdN2Kd3Twh8MAEOEJZsuxuLKCsBEo4PVNLK6tQWAn10VhsVewQLzcucMgLolRlhFybGxfclbPeEYBaP6RvUFGg==", + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.48.1.tgz", + "integrity": "sha512-jVG20NvbhTYDkGAty2/Yh7HK6/q3DGSRH4o8ALKGArmMuaauM9kLfoMZ+WliPwA5+JHr2lTn3g557FxBV87ifg==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.7" + "@types/estree": "1.0.8" }, "bin": { "rollup": "dist/bin/rollup" @@ -11602,36 +10687,29 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.43.0", - "@rollup/rollup-android-arm64": "4.43.0", - "@rollup/rollup-darwin-arm64": "4.43.0", - "@rollup/rollup-darwin-x64": "4.43.0", - "@rollup/rollup-freebsd-arm64": "4.43.0", - "@rollup/rollup-freebsd-x64": "4.43.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.43.0", - "@rollup/rollup-linux-arm-musleabihf": "4.43.0", - "@rollup/rollup-linux-arm64-gnu": "4.43.0", - "@rollup/rollup-linux-arm64-musl": "4.43.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.43.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.43.0", - "@rollup/rollup-linux-riscv64-gnu": "4.43.0", - "@rollup/rollup-linux-riscv64-musl": "4.43.0", - "@rollup/rollup-linux-s390x-gnu": "4.43.0", - "@rollup/rollup-linux-x64-gnu": "4.43.0", - "@rollup/rollup-linux-x64-musl": "4.43.0", - "@rollup/rollup-win32-arm64-msvc": "4.43.0", - "@rollup/rollup-win32-ia32-msvc": "4.43.0", - "@rollup/rollup-win32-x64-msvc": "4.43.0", + "@rollup/rollup-android-arm-eabi": "4.48.1", + "@rollup/rollup-android-arm64": "4.48.1", + "@rollup/rollup-darwin-arm64": "4.48.1", + "@rollup/rollup-darwin-x64": "4.48.1", + "@rollup/rollup-freebsd-arm64": "4.48.1", + "@rollup/rollup-freebsd-x64": "4.48.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.48.1", + "@rollup/rollup-linux-arm-musleabihf": "4.48.1", + "@rollup/rollup-linux-arm64-gnu": "4.48.1", + "@rollup/rollup-linux-arm64-musl": "4.48.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.48.1", + "@rollup/rollup-linux-ppc64-gnu": "4.48.1", + "@rollup/rollup-linux-riscv64-gnu": "4.48.1", + "@rollup/rollup-linux-riscv64-musl": "4.48.1", + "@rollup/rollup-linux-s390x-gnu": "4.48.1", + "@rollup/rollup-linux-x64-gnu": "4.48.1", + "@rollup/rollup-linux-x64-musl": "4.48.1", + "@rollup/rollup-win32-arm64-msvc": "4.48.1", + "@rollup/rollup-win32-ia32-msvc": "4.48.1", + "@rollup/rollup-win32-x64-msvc": "4.48.1", "fsevents": "~2.3.2" } }, - "node_modules/rollup/node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", - "dev": true, - "license": "MIT" - }, "node_modules/rsvp": { "version": "4.8.5", "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", @@ -11756,6 +10834,7 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.3.2.tgz", "integrity": "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==", + "license": "MIT", "engines": { "node": ">=10" } @@ -11764,6 +10843,7 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.3.2.tgz", "integrity": "sha512-0QvCV2lM3aj/U3YozDiVwx9zpH0q8A60CTWIv4Jszj/givcudPb48B+rkU5D51NJ0pTpweGMttHjboPa9/zoIQ==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -11874,9 +10954,9 @@ } }, "node_modules/smol-toml": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.3.4.tgz", - "integrity": "sha512-UOPtVuYkzYGee0Bd2Szz8d2G3RfMfJ2t3qVdZUAozZyAk+a0Sxa+QKix0YCwjL/A1RR0ar44nCxaoN9FxdJGwA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.4.2.tgz", + "integrity": "sha512-rInDH6lCNiEyn3+hH8KVGFdbjc099j47+OSgbMrfDYX1CmXLfdKd7qi6IfcWj2wFxvSVkuI46M+wPGYfEOEj6g==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -12623,6 +11703,7 @@ "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.3.tgz", "integrity": "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.0.0" } @@ -12749,9 +11830,9 @@ "license": "0BSD" }, "node_modules/tsx": { - "version": "4.20.2", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.2.tgz", - "integrity": "sha512-He0ZWr41gLa4vD30Au3yuwpe0HXaCZbclvl8RBieUiJ9aFnPMWUPIyvw3RU8+1Crjfcrauvitae2a4tUzRAGsw==", + "version": "4.20.5", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.5.tgz", + "integrity": "sha512-+wKjMNU9w/EaQayHXb7WA7ZaHY6hN8WgfvHNQ3t1PnU91/7O8TcTnIhCDYTZwnt8JsO9IBqZ30Ln1r7pPF52Aw==", "dev": true, "license": "MIT", "dependencies": { @@ -12795,7 +11876,6 @@ "version": "5.9.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", - "devOptional": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -12806,9 +11886,9 @@ } }, "node_modules/ua-parser-js": { - "version": "1.0.40", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.40.tgz", - "integrity": "sha512-z6PJ8Lml+v3ichVojCiB8toQJBuwR42ySM4ezjXIqXK3M0HczmKQ3LF4rhU55PfD99KEEXQG6yb7iOMyvYuHew==", + "version": "1.0.41", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.41.tgz", + "integrity": "sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug==", "dev": true, "funding": [ { @@ -13350,15 +12430,15 @@ } }, "node_modules/vite-plugin-graphql-codegen": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/vite-plugin-graphql-codegen/-/vite-plugin-graphql-codegen-3.6.1.tgz", - "integrity": "sha512-6uTRv8jD1pp9kt6StjOL6BGj166qVXmRwe06m9I1qtxjIVf+i7aF95gFv0NKxhEXXaDr1hFVlpp+3Ts+SQAy4g==", + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/vite-plugin-graphql-codegen/-/vite-plugin-graphql-codegen-3.6.3.tgz", + "integrity": "sha512-A6C5fEGg26jG4bUhxTRH3KegFXNJ4Vxy4x/F/UKTfl73wOeb64qA3/rJFybyambvjNFndyameNazyeUglfH4Qg==", "dev": true, "license": "MIT", "peerDependencies": { "@graphql-codegen/cli": ">=1.0.0 <6.0.0", "graphql": ">=14.0.0 <17.0.0", - "vite": ">=2.7.0 <7.0.0" + "vite": ">=2.7.0 <8.0.0" } }, "node_modules/vite-plugin-manifest-sri": { @@ -13650,9 +12730,9 @@ "license": "ISC" }, "node_modules/ws": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", - "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "dev": true, "license": "MIT", "engines": { @@ -13699,9 +12779,9 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", - "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", "dev": true, "license": "ISC", "bin": { @@ -13761,9 +12841,9 @@ } }, "node_modules/yoctocolors-cjs": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", - "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", + "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", "dev": true, "license": "MIT", "engines": { @@ -13774,9 +12854,9 @@ } }, "node_modules/zod": { - "version": "3.25.63", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.63.tgz", - "integrity": "sha512-3ttCkqhtpncYXfP0f6dsyabbYV/nEUW+Xlu89jiXbTBifUfjaSqXOG6JnQPLtqt87n7KAmnMqcjay6c0Wq0Vbw==", + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "dev": true, "license": "MIT", "funding": { @@ -13784,16 +12864,16 @@ } }, "node_modules/zod-validation-error": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.5.0.tgz", - "integrity": "sha512-IWK6O51sRkq0YsnYD2oLDuK2BNsIjYUlR0+1YSd4JyBzm6/892IWroUnLc7oW4FU+b0f6948BHi6H8MDcqpOGw==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.5.3.tgz", + "integrity": "sha512-OT5Y8lbUadqVZCsnyFaTQ4/O2mys4tj7PqhdbBCp7McPwvIEKfPtdA6QfPeFQK2/Rz5LgwmAXRJTugBNBi0btw==", "dev": true, "license": "MIT", "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "zod": "^3.25.0" + "zod": "^3.25.0 || ^4.0.0" } } } diff --git a/frontend/package.json b/frontend/package.json index 6a5d40c66..fde35a7ae 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -9,6 +9,8 @@ "lint": "graphql-codegen && biome check && tsc && i18next --fail-on-warnings --fail-on-update", "format": "biome format --write", "build": "rimraf ./dist/ && vite build", + "build-tchap": "rimraf ./dist/ && vite build -c ./tchap/vite.tchap.config.js ", + "update-snapshot-tchap": "vitest --update", "preview": "vite preview", "test": "vitest", "coverage": "vitest run --coverage", @@ -20,11 +22,12 @@ "dependencies": { "@fontsource/inconsolata": "^5.2.6", "@fontsource/inter": "^5.2.6", + "@gouvfr-lasuite/integration": "^1.0.3", "@radix-ui/react-collapsible": "^1.1.11", "@radix-ui/react-dialog": "^1.1.14", "@tanstack/react-query": "^5.85.5", "@tanstack/react-router": "^1.131.27", - "@vector-im/compound-design-tokens": "5.0.2", + "@vector-im/compound-design-tokens": "git+https://github.com/tchapgouv/compound-design-tokens.git", "@vector-im/compound-web": "^8.2.0", "@zxcvbn-ts/core": "^3.0.4", "@zxcvbn-ts/language-common": "^3.0.4", diff --git a/frontend/src/components/Layout/Layout.tsx b/frontend/src/components/Layout/Layout.tsx index 039bee28a..64e55ccd4 100644 --- a/frontend/src/components/Layout/Layout.tsx +++ b/frontend/src/components/Layout/Layout.tsx @@ -7,6 +7,10 @@ import { queryOptions, useSuspenseQuery } from "@tanstack/react-query"; import cx from "classnames"; import { Suspense } from "react"; +//:tchap: +import FooterTchap from "../../../tchap/components/Footer"; +import HeaderTchap from "../../../tchap/components/Header"; +//:tchap: end import { graphql } from "../../gql"; import { graphqlRequest } from "../../graphql"; import Footer from "../Footer"; @@ -49,9 +53,15 @@ const Layout: React.FC<{ wide?: boolean; }> = ({ children, wide }) => (
+ {/* :tchap: */} + + {/* :tchap: end */} {children} - + {/* :tchap: */} + {/* */} + + {/* :tchap: end */}
); diff --git a/frontend/tchap/components/Footer/Footer.module.css b/frontend/tchap/components/Footer/Footer.module.css new file mode 100644 index 000000000..dda648d7c --- /dev/null +++ b/frontend/tchap/components/Footer/Footer.module.css @@ -0,0 +1 @@ +/* This file is kept for potential custom styling */ diff --git a/frontend/tchap/components/Footer/Footer.tsx b/frontend/tchap/components/Footer/Footer.tsx new file mode 100644 index 000000000..ffacb24ae --- /dev/null +++ b/frontend/tchap/components/Footer/Footer.tsx @@ -0,0 +1,47 @@ +// +// MIT License +// +// Copyright (c) 2025, Direction interministérielle du numérique - Gouvernement +// Français +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +import type React from "react"; +import "@gouvfr-lasuite/integration/dist/css/homepage-full.css"; + +const Footer: React.FC = () => ( + +); + +export default Footer; diff --git a/frontend/tchap/components/Footer/index.ts b/frontend/tchap/components/Footer/index.ts new file mode 100644 index 000000000..f6ea17e9c --- /dev/null +++ b/frontend/tchap/components/Footer/index.ts @@ -0,0 +1,26 @@ +// +// MIT License +// +// Copyright (c) 2025, Direction interministérielle du numérique - Gouvernement +// Français +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +export { default } from "./Footer"; diff --git a/frontend/tchap/components/Header/Header.module.css b/frontend/tchap/components/Header/Header.module.css new file mode 100644 index 000000000..da5b10a3e --- /dev/null +++ b/frontend/tchap/components/Header/Header.module.css @@ -0,0 +1,45 @@ +/* + * MIT License + * + * Copyright (c) 2025, Direction interministérielle du numérique - Gouvernement + * Français + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +.header-tchap { + display: flex; + align-items: center; + gap: var(--cpd-space-3x); + padding: var(--cpd-space-2x) 0; +} + +.logo-container { + display: flex; + align-items: center; +} + +.logo { + width: 32px; +} + +.title { + font: var(--cpd-font-heading-md-semibold); + color: var(--cpd-color-text-primary); +} diff --git a/frontend/tchap/components/Header/Header.tsx b/frontend/tchap/components/Header/Header.tsx new file mode 100644 index 000000000..56ded1aba --- /dev/null +++ b/frontend/tchap/components/Header/Header.tsx @@ -0,0 +1,42 @@ +// +// MIT License +// +// Copyright (c) 2025, Direction interministérielle du numérique - Gouvernement +// Français +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +import type React from "react"; +import styles from "./Header.module.css"; + +const Header: React.FC = () => ( +
+
+ Logo Tchap +
+
Tchap
+
+); + +export default Header; diff --git a/frontend/tchap/components/Header/index.ts b/frontend/tchap/components/Header/index.ts new file mode 100644 index 000000000..930f1257b --- /dev/null +++ b/frontend/tchap/components/Header/index.ts @@ -0,0 +1,26 @@ +// +// MIT License +// +// Copyright (c) 2025, Direction interministérielle du numérique - Gouvernement +// Français +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +export { default } from "./Header"; diff --git a/frontend/tchap/css/tchap.css b/frontend/tchap/css/tchap.css new file mode 100644 index 000000000..394a515df --- /dev/null +++ b/frontend/tchap/css/tchap.css @@ -0,0 +1,91 @@ +/* + * MIT License + * + * Copyright (c) 2025, Direction interministérielle du numérique - Gouvernement + * Français + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* used by the html templates */ + +/*Align button style with LaSuite UI Kit*/ +button.cpd-button, +a.cpd-button, +.cpd-text-control { + border-radius: 4px; +} + +/*Other fixes*/ +.lasuite-header { + border-top: 0 !important; +} +.cpd-form-root.lasuite { + align-items: center; + flex-direction: column; + display: flex; + justify-content: center; + width: 100%; +} +.cpd-form-root.lasuite a { + font-size: 90%; +} +p.cpd-text-secondary, +.cpd-link { + font-size: 95%; +} +footer { + border-top: 2px solid #313178; +} +.fr-footer__body { + padding: 3rem 1.625rem 1rem; +} +.layout-container { + padding-bottom: 8em; +} + +/* proconnect button */ +.proconnect-sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} + +.proconnect-button { + background-color: transparent !important; + background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScyMTEnIGhlaWdodD0nNTgnIGZpbGw9J25vbmUnPjxwYXRoIGZpbGw9JyMwMDAwOTEnIGQ9J00wIDBoMjExdjU4SDB6Jy8+PHBhdGggZmlsbD0nI2ZmZicgZD0nbTY5Ljk4NiAyNi4zNjggMS4xNTYtMS4wNzFjLjgzMyAxLjA1NCAxLjgxOSAxLjU5OCAyLjk0MSAxLjU5OCAxLjI5MiAwIDIuMDQtLjgxNiAyLjA0LTEuOTA0IDAtMi41NS01LjYyNy0yLjI0NC01LjYyNy02LjAzNSAwLTEuNzM0IDEuNDI4LTMuMTk2IDMuNDUxLTMuMTk2IDEuNjgzIDAgMi45MDcuNzY1IDMuNzkxIDEuOTM4bC0xLjE5IDEuMDM3Yy0uNjk3LTEuMDAzLTEuNTQ3LTEuNTQ3LTIuNTg0LTEuNTQ3LTEuMTA1IDAtMS44MzYuNzQ4LTEuODM2IDEuNzM0IDAgMi41NjcgNS42MjcgMi4yNDQgNS42MjcgNi4wNTIgMCAyLjAyMy0xLjU4MSAzLjM0OS0zLjY1NSAzLjM0OS0xLjc2OCAwLTMuMDc3LS42NjMtNC4xMTQtMS45NTVabTEwLjgxNy01LjcxMkg3OS40NmwxLjQ0NS00LjU1NmgxLjY0OWwtMS43NTEgNC41NTZabTQuODE4LTMuNDUxYy0uNTYgMC0xLjAyLS40NTktMS4wMi0xLjAyYTEuMDIgMS4wMiAwIDAgMSAxLjAyLTEuMDAzYy41NjEgMCAxLjAwMy40NTkgMS4wMDMgMS4wMDMgMCAuNTYxLS40NDIgMS4wMi0xLjAwMyAxLjAyWk04NC44OTEgMjh2LTguNTY4aDEuNDQ0VjI4SDg0Ljg5Wm0zLjc2Ny00LjI4NGMwLTIuNDk5IDEuNzE3LTQuNjI0IDQuNDAzLTQuNjI0IDEuMjQxIDAgMi4yNjEuNDU5IDMuMDQzIDEuMjkyVjE1LjI1aDEuNDQ1VjI4aC0xLjQ0NXYtLjk1MmMtLjc4Mi44MzMtMS44MDIgMS4yOTItMy4wNDMgMS4yOTItMi42ODYgMC00LjQwMy0yLjEyNS00LjQwMy00LjYyNFptMS41MyAwYzAgMS44MTkgMS4yMjQgMy4yNjQgMy4wNDMgMy4yNjQgMS4xOSAwIDIuMjEtLjU3OCAyLjg3My0xLjU5OFYyMi4wNWMtLjY4LTEuMDM3LTEuNy0xLjU5OC0yLjg3My0xLjU5OC0xLjgxOSAwLTMuMDQzIDEuNDQ1LTMuMDQzIDMuMjY0Wm0xOC4wMjMgMi44NzNjLS43OTkgMS4wNzEtMi4wNzQgMS43NTEtMy42NzIgMS43NTEtMi44OSAwLTQuNjc1LTIuMTI1LTQuNjc1LTQuNjI0IDAtMi42MDEgMS42NjYtNC42MjQgNC4zMTgtNC42MjQgMi4zMjkgMCAzLjg0MiAxLjU4MSAzLjg0MiAzLjcyMyAwIC4zNC0uMDUxLjY4LS4xMDIuOTE4aC02LjU2MnYuMDM0YzAgMS44ODcgMS4yOTIgMy4yNjQgMy4yMTMgMy4yNjQgMS4wODggMCAyLjAwNi0uNTEgMi41NjctMS4yNzVsMS4wNzEuODMzWm0tNC4wMTItNi4yNTZjLTEuMzk0IDAtMi4zOC43ODItMi43MiAyLjI2MWg1LjA4M2MtLjA1MS0xLjI0MS0uOTUyLTIuMjYxLTIuMzYzLTIuMjYxWk0xMTAuNDczIDI4di04LjU2OGgxLjQ0NXYuOTY5Yy42OTctLjc2NSAxLjU4MS0xLjMwOSAyLjg1Ni0xLjMwOSAxLjkyMSAwIDMuMzQ5IDEuMjkyIDMuMzQ5IDMuNzIzVjI4aC0xLjQ2MnYtNS4xMzRjMC0xLjUzLS44NS0yLjQxNC0yLjE3Ni0yLjQxNC0xLjI0MSAwLTIuMDIzLjcxNC0yLjU2NyAxLjYxNVYyOGgtMS40NDVabTExLjA1Mi0yLjg3M3YtNC4zNjloLTEuNjE1di0xLjMyNmgxLjYxNVYxNy4yOWgxLjQ2MnYyLjE0MmgyLjk3NXYxLjMyNmgtMi45NzV2NC4zNjljMCAxLjM0My42OCAxLjcxNyAxLjcxNyAxLjcxNy41NjEgMCAuOTUyLS4wNjggMS4yNzUtLjIwNHYxLjI5MmMtLjQwOC4xNy0uODY3LjIzOC0xLjQ3OS4yMzgtMS45MDQgMC0yLjk3NS0uOTUyLTIuOTc1LTMuMDQzWm03LjM3Ny03LjkyMmMtLjU2MSAwLTEuMDItLjQ1OS0xLjAyLTEuMDJhMS4wMiAxLjAyIDAgMCAxIDEuMDItMS4wMDNjLjU2MSAwIDEuMDAzLjQ1OSAxLjAwMyAxLjAwMyAwIC41NjEtLjQ0MiAxLjAyLTEuMDAzIDEuMDJaTTEyOC4xNzEgMjh2LTguNTY4aDEuNDQ1VjI4aC0xLjQ0NVptMy4zNzctOC41NjhoMS42MTV2LTEuMDU0YzAtMS44MzYgMS4yMDctMy4xMjggMy4wNDMtMy4xMjguOTUyIDAgMS43LjM0IDIuMjEuODMzbC0uOTAxIDEuMDU0YTEuNjMzIDEuNjMzIDAgMCAwLTEuMjkyLS41NzhjLS45MzUgMC0xLjU5OC42OC0xLjU5OCAxLjc4NXYxLjA4OGgyLjk3NXYxLjMyNmgtMi45NzVWMjhoLTEuNDYydi03LjI0MmgtMS42MTV2LTEuMzI2Wm04LjU0My0yLjIyN2MtLjU2MSAwLTEuMDItLjQ1OS0xLjAyLTEuMDJhMS4wMiAxLjAyIDAgMCAxIDEuMDItMS4wMDNjLjU2MSAwIDEuMDAzLjQ1OSAxLjAwMyAxLjAwMyAwIC41NjEtLjQ0MiAxLjAyLTEuMDAzIDEuMDJaTTEzOS4zNiAyOHYtOC41NjhoMS40NDVWMjhoLTEuNDQ1Wm0xMi4xMTUtMS40MTFjLS43OTkgMS4wNzEtMi4wNzQgMS43NTEtMy42NzIgMS43NTEtMi44OSAwLTQuNjc1LTIuMTI1LTQuNjc1LTQuNjI0IDAtMi42MDEgMS42NjYtNC42MjQgNC4zMTgtNC42MjQgMi4zMjkgMCAzLjg0MiAxLjU4MSAzLjg0MiAzLjcyMyAwIC4zNC0uMDUxLjY4LS4xMDIuOTE4aC02LjU2MnYuMDM0YzAgMS44ODcgMS4yOTIgMy4yNjQgMy4yMTMgMy4yNjQgMS4wODggMCAyLjAwNi0uNTEgMi41NjctMS4yNzVsMS4wNzEuODMzWm0tNC4wMTItNi4yNTZjLTEuMzk0IDAtMi4zOC43ODItMi43MiAyLjI2MWg1LjA4M2MtLjA1MS0xLjI0MS0uOTUyLTIuMjYxLTIuMzYzLTIuMjYxWk0xNTMuNzM3IDI4di04LjU2OGgxLjQ0NXYxLjA3MWMuNjI5LS43NDggMS40MTEtMS4yNDEgMi40OTktMS4yNDEuMjcyIDAgLjUyNy4wMzQuNzMxLjEwMnYxLjQ5NmEzLjEwNSAzLjEwNSAwIDAgMC0uODUtLjExOWMtMS4xMjIgMC0xLjg1My41NzgtMi4zOCAxLjQ0NVYyOGgtMS40NDVabTEzLjY4NS4zNGMtMS42ODMgMC0yLjgyMi0uOTUyLTIuODIyLTIuNDQ4IDAtMS4zMjYuOTg2LTIuMjc4IDIuODIyLTIuNTY3bDIuODczLS40NzZ2LS41OTVjMC0xLjE5LS44NS0xLjg3LTIuMDU3LTEuODctMS4wMDMgMC0xLjgzNi40NDItMi4zMjkgMS4xOWwtMS4wODgtLjgzM2MuNzQ4LTEuMDIgMS45NTUtMS42NDkgMy40NTEtMS42NDkgMi4xNzYgMCAzLjQ2OCAxLjI3NSAzLjQ2OCAzLjE2MlYyOGgtMS40NDV2LTEuMDg4Yy0uNjQ2LjkwMS0xLjcxNyAxLjQyOC0yLjg3MyAxLjQyOFptLTEuMzc3LTIuNDk5YzAgLjczMS42MjkgMS4yOTIgMS42MTUgMS4yOTIgMS4xMzkgMCAyLjA0LS41OTUgMi42MzUtMS41ODFWMjMuOTJsLTIuNTMzLjQ0MmMtMS4xOS4xODctMS43MTcuNzMxLTEuNzE3IDEuNDc5Wm03LjI1Mi02LjQwOWgxLjU2NGwyLjczNyA3LjA1NSAyLjczNy03LjA1NWgxLjU2NEwxNzguNTUgMjhoLTEuOTA0bC0zLjM0OS04LjU2OFptMTcuODU2IDcuMTU3Yy0uNzk5IDEuMDcxLTIuMDc0IDEuNzUxLTMuNjcyIDEuNzUxLTIuODkgMC00LjY3NS0yLjEyNS00LjY3NS00LjYyNCAwLTIuNjAxIDEuNjY2LTQuNjI0IDQuMzE4LTQuNjI0IDIuMzI5IDAgMy44NDIgMS41ODEgMy44NDIgMy43MjMgMCAuMzQtLjA1MS42OC0uMTAyLjkxOGgtNi41NjJ2LjAzNGMwIDEuODg3IDEuMjkyIDMuMjY0IDMuMjEzIDMuMjY0IDEuMDg4IDAgMi4wMDYtLjUxIDIuNTY3LTEuMjc1bDEuMDcxLjgzM1ptLTQuMDEyLTYuMjU2Yy0xLjM5NCAwLTIuMzguNzgyLTIuNzIgMi4yNjFoNS4wODNjLS4wNTEtMS4yNDEtLjk1Mi0yLjI2MS0yLjM2My0yLjI2MVptMTAuMTg1IDYuNjQ3YzEuMDU0IDAgMS45MDQtLjUxIDIuNDMxLTEuMjc1bDEuMTU2Ljg4NGMtLjc5OSAxLjA3MS0yLjA0IDEuNzUxLTMuNjA0IDEuNzUxLTIuODM5IDAtNC42NTgtMi4xMjUtNC42NTgtNC42MjQgMC0yLjQ5OSAxLjgxOS00LjYyNCA0LjY1OC00LjYyNCAxLjU0NyAwIDIuODA1LjY5NyAzLjYwNCAxLjc1MWwtMS4xNTYuODg0YTIuOTI1IDIuOTI1IDAgMCAwLTIuNDQ4LTEuMjc1Yy0xLjgzNiAwLTMuMTQ1IDEuNDQ1LTMuMTQ1IDMuMjY0IDAgMS44MzYgMS4zMDkgMy4yNjQgMy4xNjIgMy4yNjRaTTcwLjg1NCA0NVYzMi40aDQuMTU4YzIuNzcyIDAgNC40NjQgMS40MjIgNC40NjQgMy43NjIgMCAyLjMyMi0xLjY5MiAzLjc0NC00LjQ2NCAzLjc0NEg3My40MVY0NWgtMi41NTZabTQuMjY2LTEwLjQyMmgtMS43MXYzLjE1aDEuNzFjMS4wOCAwIDEuNzI4LS41NzYgMS43MjgtMS42MDIgMC0uOTU0LS42NDgtMS41NDgtMS43MjgtMS41NDhaTTgxLjI0OSA0NXYtOS4wNzJoMi4yODZ2LjljLjU5NC0uNjEyIDEuMzY4LTEuMDggMi4zOTQtMS4wOC4zMDYgMCAuNTc2LjA1NC43OTIuMTI2djIuMzk0YTMuOTM4IDMuOTM4IDAgMCAwLTEuMDA4LS4xMjZjLTEuMTE2IDAtMS44MzYuNjEyLTIuMTc4IDEuMTdWNDVoLTIuMjg2Wm0xMS4zODYtOS40MzJjMi45NTIgMCA0Ljk2OCAyLjE3OCA0Ljk2OCA0Ljg5NnMtMi4wMTYgNC44OTYtNC45NjggNC44OTYtNC45NjgtMi4xNzgtNC45NjgtNC44OTYgMi4wMTYtNC44OTYgNC45NjgtNC44OTZabS4wMzYgNy42MzJjMS40NTggMCAyLjU1Ni0xLjE3IDIuNTU2LTIuNzM2IDAtMS41ODQtMS4wOTgtMi43MzYtMi41NTYtMi43MzYtMS41MTIgMC0yLjYyOCAxLjE1Mi0yLjYyOCAyLjczNiAwIDEuNTg0IDEuMTE2IDIuNzM2IDIuNjI4IDIuNzM2Wm0xMy4xNzItLjIzNGMxLjQ0IDAgMi41NzQtLjcwMiAzLjI5NC0xLjcyOGwyLjAxNiAxLjU0OGMtMS4xNTIgMS41NjYtMy4wMjQgMi41NzQtNS4zMSAyLjU3NC0zLjk3OCAwLTYuNjk2LTMuMDYtNi42OTYtNi42NnMyLjcxOC02LjY2IDYuNjk2LTYuNjZjMi4yODYgMCA0LjE1OCAxLjAyNiA1LjMxIDIuNTU2bC0yLjAxNiAxLjU2NmMtLjcyLTEuMDI2LTEuODU0LTEuNzI4LTMuMjk0LTEuNzI4LTIuMzc2IDAtNC4wNjggMS44NTQtNC4wNjggNC4yNjZzMS42OTIgNC4yNjYgNC4wNjggNC4yNjZabTExLjM2Ni03LjM5OGMyLjk1MiAwIDQuOTY4IDIuMTc4IDQuOTY4IDQuODk2cy0yLjAxNiA0Ljg5Ni00Ljk2OCA0Ljg5Ni00Ljk2OC0yLjE3OC00Ljk2OC00Ljg5NiAyLjAxNi00Ljg5NiA0Ljk2OC00Ljg5NlptLjAzNiA3LjYzMmMxLjQ1OCAwIDIuNTU2LTEuMTcgMi41NTYtMi43MzYgMC0xLjU4NC0xLjA5OC0yLjczNi0yLjU1Ni0yLjczNi0xLjUxMiAwLTIuNjI4IDEuMTUyLTIuNjI4IDIuNzM2IDAgMS41ODQgMS4xMTYgMi43MzYgMi42MjggMi43MzZabTcuMDE4IDEuOHYtOS4wNzJoMi4yODZ2LjcyYy42My0uNjEyIDEuNDc2LTEuMDggMi42ODItMS4wOCAxLjk2MiAwIDMuNTI4IDEuMzUgMy41MjggNC4wMzJWNDVoLTIuMzIydi01LjMxYzAtMS4yMDYtLjY2Ni0xLjk2Mi0xLjc4Mi0xLjk2Mi0xLjE1MiAwLTEuNzY0Ljc3NC0yLjEwNiAxLjM1VjQ1aC0yLjI4NlptMTEuMDkxIDB2LTkuMDcyaDIuMjg2di43MmMuNjMtLjYxMiAxLjQ3Ni0xLjA4IDIuNjgyLTEuMDggMS45NjIgMCAzLjUyOCAxLjM1IDMuNTI4IDQuMDMyVjQ1aC0yLjMyMnYtNS4zMWMwLTEuMjA2LS42NjYtMS45NjItMS43ODItMS45NjItMS4xNTIgMC0xLjc2NC43NzQtMi4xMDYgMS4zNVY0NWgtMi4yODZabTE5LjQ0NC0xLjQ3NmMtLjg0NiAxLjEzNC0yLjI1IDEuODM2LTMuOTYgMS44MzYtMy4yMjIgMC01LjA0LTIuMjUtNS4wNC00Ljg5NiAwLTIuNjgyIDEuNjkyLTQuODk2IDQuNjYyLTQuODk2IDIuNTIgMCA0LjE3NiAxLjY5MiA0LjE3NiA0LjA2OCAwIC41MDQtLjA3Mi45OS0uMTQ0IDEuMjk2aC02LjM1NGMuMTQ0IDEuNDk0IDEuMTg4IDIuMzc2IDIuNzM2IDIuMzc2Ljk5IDAgMS44LS40MzIgMi4yODYtMS4wOGwxLjYzOCAxLjI5NlptLTQuMzM4LTYuMDQ4Yy0xLjExNiAwLTEuODcyLjU0LTIuMTc4IDEuNzI4aDQuMDg2Yy0uMDM2LS45LS43MDItMS43MjgtMS45MDgtMS43MjhabTEwLjY5NiA1LjcyNGMuODgyIDAgMS41ODQtLjQzMiAyLjAxNi0xLjA2MmwxLjgxOCAxLjM4NmMtLjg0NiAxLjExNi0yLjE3OCAxLjgzNi0zLjgzNCAxLjgzNi0zLjEzMiAwLTUuMDA0LTIuMjUtNS4wMDQtNC44OTZzMS44NzItNC44OTYgNS4wMDQtNC44OTZjMS42NTYgMCAyLjk4OC43MiAzLjgzNCAxLjgzNmwtMS44MTggMS4zODZjLS40MzItLjYzLTEuMTE2LTEuMDYyLTIuMDUyLTEuMDYyLTEuNDk0IDAtMi41OTIgMS4xNTItMi41OTIgMi43MzYgMCAxLjYwMiAxLjA5OCAyLjczNiAyLjYyOCAyLjczNlptNi4yMDQtMS41MTJ2LTMuNjcyaC0xLjY5MnYtMi4wODhoMS42OTJWMzMuNjZoMi4zMDR2Mi4yNjhoMi43NzJ2Mi4wODhoLTIuNzcydjMuNjcyYzAgMS4wMDguNTQgMS40MDQgMS40NCAxLjQwNC42MyAwIDEuMDQ0LS4wNzIgMS4zNS0uMTk4djEuOTk4Yy0uNDUuMTk4LS45OS4yODgtMS43NDYuMjg4LTIuMjY4IDAtMy4zNDgtMS4yNzgtMy4zNDgtMy40OTJaJy8+PHBhdGggZmlsbD0nIzAwMDA5MScgZD0nTTQ2Ljk5MiAxOS4wOTggMzEuOTk4IDEwLjQybC0xNC45OTQgOC43NmEuNjA2LjYwNiAwIDAgMC0uMzA2LjUyNXYxNi45NDhhLjY2Ni42NjYgMCAwIDAgLjMwNi41MjRsMTQuOTkyIDguNiAxNC45OTQtOC43MDZhLjY2Ni42NjYgMCAwIDAgLjMwNi0uNTI0VjE5LjYyNmEuNjA0LjYwNCAwIDAgMC0uMzA0LS41MjhaJy8+PHBhdGggZmlsbD0nI0ZDQzYzQScgZD0nbTI2LjY0MSAxOS41OTgtNS4wMjkgOC42MjgtNC41NTctOS4xNzUgNS4zOS0zLjExMyA0LjQ4OSAzLjE2LS4yOTMuNVptMjAuNjU2IDE2Ljk4VjE5LjYyYS42LjYgMCAwIDAtLjMwNi0uNTIzTDMxLjk5OCAxMC40MicvPjxwYXRoIGZpbGw9JyMwMDYzQ0InIGQ9J00xNi43IDM2LjU3OCAzMiAxMC40MnYzNS4zNjJsLTE0Ljk5Ni04LjYwNWEuNjY1LjY2NSAwIDAgMS0uMzA2LS41MjRWMTkuNzA2bC4wMDIgMTYuODcyWm0yNC42NjktMjAuNzM1IDUuNDU4IDMuMTU1LTQuNDg5IDkuMTUtNS4zODctOS4yMzYgNC40MTgtMy4wN1onLz48cGF0aCBmaWxsPScjZmZmJyBkPSdtNTEuNjA2IDE2LjMwMy0xOS4xOS0xMS4wMmEuOTMzLjkzMyAwIDAgMC0uODMyIDBsLTE5LjE5IDExLjAyYS44ODcuODg3IDAgMCAwLS4zOTQuNjk1djIyYS44ODUuODg1IDAgMCAwIC4zOTQuN2wxOS4xODkgMTEuMDJhLjkzMi45MzIgMCAwIDAgLjgzMiAwbDE5LjE5MS0xMS4wMmEuODg2Ljg4NiAwIDAgMCAuMzk0LS43di0yMmEuODg3Ljg4NyAwIDAgMC0uMzk0LS42OTVaTTIyLjc4OSAzNC4wNTloLjA3OWMtLjA0MiAwLS4wNzkuMDA3LS4wNzkuMDUgMCAuMS4xNTEgMCAuMi4xYS45MTIuOTEyIDAgMCAwLS42MjkuMjc2YzAgLjA1LjEuMDUuMTUxLjA1LS4wNzUuMS0uMjI2LjA1LS4yNzcuMTUyYS4xNzYuMTc2IDAgMCAwIC4xLjA1Yy0uMDUgMC0uMSAwLS4xLjA1di4xNTJjLS4xMjYgMC0uMTc2LjEtLjI3Ny4xNS4yLjE1Mi4zMjcgMCAuNTI4IDAtLjUyOC4yLS45NTYuNDc5LTEuNDg0LjYzLS4xIDAgMCAuMTUtLjEuMTUuMTUxLjEuMjI3LS4wNS4zNzctLjA1LS42NTQuMzc4LTEuMzMzLjctMi4wMzcgMS4xMzNhLjM1MS4zNTEgMCAwIDAtLjEuMmgtLjJjLS4xLjA1LS4wNS4xNzYtLjE1MS4yNzcuMjI2LjE1LjUtLjIuNjU0IDAgLjA1IDAtLjEuMDUtLjIuMDUtLjA1IDAtLjA1LjEtLjEuMWgtLjE1NGMtLjEuMDc1LS4yLjEyNi0uMi4yNzZhLjIyLjIyIDAgMCAwLS4yMjYuMSA5LjAzMSA5LjAzMSAwIDAgMCAzLjE0NC0uNTc4IDcuNjgzIDcuNjgzIDAgMCAwIDIuMDg4LTEuNTYuMTc2LjE3NiAwIDAgMSAuMDUuMWMtLjE0Ny40MzctLjQzLjgxNi0uODA2IDEuMDgtLjI3Ny4xNTItLjQ3OC4zNzgtLjcuNDc5YTQuMDU3IDQuMDU3IDAgMCAwLS40MjguMjc2Yy0uNjMyLjE5Ny0xLjI4MS4zMzUtMS45MzkuNDEybC0uMzA1LjA0NGMtLjIyNS4wMzMtLjQ0OS4wNjktLjY3MS4xMDhsLTEuOTkzLTEuMTM4YS42NDcuNjQ3IDAgMCAxLS4yODgtLjQxMS41Ny41NyAwIDAgMCAuMDk0LS4wNjMuMjY2LjI2NiAwIDAgMC0uMTEzLS4wNzF2LS42NWExMi43ODIgMTIuNzgyIDAgMCAwIDMuMDM4LS45NDIgOC43NDYgOC43NDYgMCAwIDAtMy4wMzctMS4zNDN2LTEuNTE1YTExLjY3IDExLjY3IDAgMCAxIDEuNjM5LjM5MiA2LjQyIDYuNDIgMCAwIDEgMS4xODIuNTc4Yy4xNDcuMTQuMzA3LjI2Ny40NzguMzc3YS45MS45MSAwIDAgMCAuOC4wNWguMzNhMy45NjEgMy45NjEgMCAwIDAgMS45MzctLjkwNWMwIC4wNS4wNS4wNS4xLjA1YTMuNjI5IDMuNjI5IDAgMCAxLS40MjggMS4xMzJjLjAwMy4wNS0uMDQ4LjE1Mi4wNTMuMjAyWm0yLjgxNyAzLjU3Yy4yNTEtLjEuNC0uMjc2LjYyOS0uMzc2LS4wNS4wNS0uMDUuMTUtLjEuMmEzLjY5OSAzLjY5OSAwIDAgMC0uNTI4LjQgMTUuOTY1IDE1Ljk2NSAwIDAgMC0xLjU4NSAxLjYxYy0uMjUyLjMtLjUyOC41NzgtLjguODU1LS4wOTYuMDktLjIuMTcyLS4zMS4yNDVsLTIuNTI3LTEuNDVjLjM2LjAzLjcyMS4wMTMgMS4wNzYtLjA1My4yOTQtLjA4My41OC0uMTkyLjg1NS0uMzI3di4xYy43LS4yNzcgMS4yMzItLjkwNiAxLjkzNy0xLjEzMi4wMjUgMCAuMTI2LjEuMjI2LjA1YTEuODgzIDEuODgzIDAgMCAxIDEuNTA5LS43YzAgLjA1IDAgLjEuMDUuMWguMDI1Yy0uMTUxLjEyNi0uMzI3LjI1LS41LjM3Ny0uMDU3LjA1Mi0uMDA3LjEwMi4wNDMuMTAyWm0tOC45MDgtNi4xNjN2LS4xODZhNS44MTcgNS44MTcgMCAwIDEgMS41ODgtLjE4OCAxLjUyIDEuNTIgMCAwIDEgLjQ3OCAwIDUuODYgNS44NiAwIDAgMC0yLjA2Ni4zNzRabTMwLjYgNS4wODhhLjY2NS42NjUgMCAwIDEtLjMwNi41MjRsLTEwLjA3OSA1Ljg1YTMyLjI5NiAzMi4yOTYgMCAwIDEtMy40MDgtMS4xODQgMi44MjYgMi44MjYgMCAwIDEtLjA1LTIuMjQ1Yy4wOC0uMzA4LjE5OC0uNjA1LjM1Mi0uODgzLjAyNS0uMDI1LjA1LS4wNS4wNS0uMDc2YS4wMjUuMDI1IDAgMCAwIC4wMjUtLjAyNSA0LjMyIDQuMzIgMCAwIDEgLjM3Ny0uNTU1bC4wMTUtLjAxNS4wMi0uMDIxLjAxNS0uMDE1YzAtLjAyNS4wMjUtLjA1LjA1LS4wNzYuMDI1LS4wNTEuMDc1LS4wNzYuMS0uMTI2LjE3Ni0uMTg2LjM3LS4zNTQuNTc5LS41LjIxMy0uMDc3LjQzMS0uMTM2LjY1NC0uMTc3LjgxMS4wNiAxLjYxNy4xNyAyLjQxNS4zMjhhLjc1Mi43NTIgMCAwIDEgLjI3Ny4xYy4zMDEuMDU5LjYxMi4wNDEuOTA1LS4wNWExLjEzNyAxLjEzNyAwIDAgMCAuODU1LS43MDYgMS4yMTIgMS4yMTIgMCAwIDAgLjA1LTEuMDZjLS4xNzgtLjI3NS0uMDEzLS40MzYuMTgxLS41OWwuMDY4LS4wNTRjLjA4Ni0uMDYxLjE2NC0uMTM0LjIzMS0uMjE2LjEyNi0uMjUyLS4xLS40LS4xNTEtLjYzLS4wNS0uMS0uMjI2LS4wNS0uMzI3LS4yLjM1Mi0uMTUxLjg1NS0uNDMuNjI5LS44NTctLjE1MS0uMjI3LS4zNzctLjYzLS4xLS44NTcuMzUyLS4yLjg1NS0uMTUxIDEuMDA2LS40OGExLjEzNyAxLjEzNyAwIDAgMC0uMjkyLTEuMDg0bC0uMDc1LS4xMDhhNC43NTQgNC43NTQgMCAwIDEtLjIxMS0uMzIgNi45MDUgNi45MDUgMCAwIDAtLjUyOC0uNzU3IDQuMjk3IDQuMjk3IDAgMCAxLS41MjgtMS4wMWMtLjE1MS0uMzc3LjA1LS43MDUuMDUtMS4wODNhNi4zNDcgNi4zNDcgMCAwIDAtLjMyNy0yLjE0NGMtLjEyNi0uMzUzLS4xNzYtLjczMS0uMzI3LTEuMDZhMS4xMiAxLjEyIDAgMCAwLS4yMjYtLjU4LjM3NC4zNzQgMCAwIDEgMC0uMzI3Yy4yMDUtLjE0NS4zOTktLjMwNS41NzktLjQ4YS41NjcuNTY3IDAgMCAwLS4yLS43MDVjLS4zMjctLjE1MS0uMy4zMjgtLjUyOC40MjloLS4xNTFjLS4wNS0uMTI2LjA1LS4xNzcuMTUxLS4yNzcgMC0uMDUgMC0uMTUxLS4wNS0uMTUxLS4yIDAtLjM3Ny0uMDUxLS40MjgtLjE1MWEzLjk1NyAzLjk1NyAwIDAgMC0xLjg2MS0xLjI4NmMuMTg4LjA1OC4zODIuMDkxLjU3OS4xLjMzOC4wNzEuNjkuMDM2IDEuMDA2LS4xLjIyNy0uMDc2LjI3Ny0uNDguMzc3LS43MDZhLjguOCAwIDAgMC0uMTUxLS42MzEgMi4xOSAyLjE5IDAgMCAwLS45MDYtLjc1NiA5LjEzIDkuMTMgMCAwIDEtLjY3OS0uMzUzLjk1Ni45NTYgMCAwIDAtLjI1MS0uMTI2Yy0yLjk2NS0xLjQ4NS05LjA2OS0uMi05LjUzNCAwaC0uMDA5YTguMjU0IDguMjU0IDAgMCAwLTEuMjQ5LjQ3NSAzLjkyMiAzLjkyMiAwIDAgMC0yLjM2NSAyLjQ2NSAzLjgzIDMuODMgMCAwIDAtMS4zMzMgMS41MDljLS40MjguOC0xLjA1NiAxLjUwOS0uOTU2IDIuNDE0LjEuNzguMjc3IDEuNDg0LjQyOCAyLjI4OS4wNDMuMjcyLjExLjU0LjIuOC4xLjI3NiAwIC42MjkuMTUxLjg1NS4wNzUuMTUuMDI1LjMyNy4yMjcuNDI4di4yYy4wNS4wNS4wNS4xLjE1MS4xdi4yYy40MzUuNDIzLjgwNy45MDYgMS4xMDcgMS40MzQuMS4yNzYtLjQ3OC4xNS0uNy4wNWE1Ljk3NyA1Ljk3NyAwIDAgMS0xLjEzMi0uOTU2LjE3Ni4xNzYgMCAwIDAtLjA1MS4xYy4yLjM1Mi45MDYuNzguNTI4IDEuMDA2LS4yLjEtLjQyOC0uMTUxLS42MjkuMDUtLjA1LjA3NiAwIC4xNzcgMCAuMjc3LS4yNzctLjItLjU3OC0uMS0uODU1LS4yLS4yLS4wNS0uMjUyLS40MjctLjQ3OC0uNDI3YTE1LjE5MSAxNS4xOTEgMCAwIDAtMS44MTEtLjMyNyAxNS4xNDQgMTUuMTQ0IDAgMCAwLTEuNzM5LS4xNlYxOS43MDdhLjYwNi42MDYgMCAwIDEgLjMwNi0uNTI0bDE0Ljk4Ny04Ljc2MSAxNC45OTQgOC42NzdhLjYwNS42MDUgMCAwIDEgLjMwNi41MjR2MTYuOTMyWm0tNy45NTQtOC4yNjFhLjMyNS4zMjUgMCAwIDEtLjI4Mi4xNDkgMi44NCAyLjg0IDAgMCAwLS4yODIuMjczYy4xIDAgMCAuMTQ5LjEuMTQ5LS4yMDUuMjIzLjA3Ny42OTQtLjIwNS43OTMtLjM3LjA5OS0uNzU4LjA5OS0xLjEyNyAwYS43MjcuNzI3IDAgMCAxIC4xNjctLjAxNmguMDg1YS4zODIuMzgyIDAgMCAwIC4zMzctLjEzMnYtLjJjMC0uMDUtLjA1MS0uMDUtLjEtLjA1YS4xNi4xNiAwIDAgMS0uMS4wNS4yMjMuMjIzIDAgMCAwLS4xNTQtLjIuODA2LjgwNiAwIDAgMS0uNzE4LS4yNzMuNjcuNjcgMCAwIDEgLjQzNi0uMDVjLjEyOCAwIC4wNzctLjIyMy4yMzEtLjMyMmguMTU0Yy4zMDctLjM3Mi44NzEtLjQ3MS45NzQtLjg0MyAwLS4xLS4yODItLjEtLjQ4Ny0uMTVhMi4yNiAyLjI2IDAgMCAwLS44Mi4wNWMtLjM2LjA1LS43MTIuMTQyLTEuMDUxLjI3NC4yOC0uMjA2LjU5Mi0uMzY1LjkyMy0uNDcxLjIzMi0uMDkuNDczLS4xNTcuNzE4LS4ybC4xMzItLjAyNi4xMzMtLjAyN2EuOTcuOTcgMCAwIDEgLjU1NiAwYy4yMzEuMS42MTUuMS42NjYuMjQ4LjEuMjczLS4xNTQuNTQ1LS40MzUuNzQ0LS4wNTcuMDguMTQ5LjEzNS4xNDkuMjNaJy8+PHJlY3Qgd2lkdGg9JzI5LjU2JyBoZWlnaHQ9JzEzLjMwMicgeD0nMzcnIHk9JzUnIGZpbGw9JyNGQ0M2M0EnIHJ4PScyJy8+PHBhdGggZmlsbD0nIzE2MTYxNicgZD0nTTM5LjU2MiAxNi4xNjhWNy4zMTZoMi45MjFjLjk3IDAgMS43MzIuMjM2IDIuMjg5LjcwOC41NjUuNDcyLjg0NyAxLjExNy44NDcgMS45MzUgMCAuODEtLjI4MiAxLjQ1LS44NDcgMS45MjItLjU1Ny40NzItMS4zMi43MDgtMi4yODkuNzA4aC0xLjEyNXYzLjU3OWgtMS43OTZabTIuOTk3LTcuMzIyaC0xLjIwMXYyLjIxM2gxLjJjLjM4IDAgLjY3NS0uMDk3Ljg4Ni0uMjkuMjItLjE5NS4zMjktLjQ3My4zMjktLjgzNiAwLS4zMzctLjExLS42MDItLjMyOS0uNzk2LS4yMS0uMTk0LS41MDYtLjI5MS0uODg1LS4yOTFaTTQ3LjIzIDE2LjE2OFY3LjMxNmgyLjcwN2MuOTcgMCAxLjczNi4yMzYgMi4zMDEuNzA4LjU2NS40NzIuODQ3IDEuMTE3Ljg0NyAxLjkzNSAwIC41My0uMTI2Ljk5NS0uMzc5IDEuMzktLjI0NC4zODktLjU5LjY4OC0xLjAzNy44OTlsMi43ODIgMy45MmgtMi4xNWwtMi4zNTItMy41NzloLS45MjN2My41NzloLTEuNzk1Wm0yLjgwOC03LjMyMmgtMS4wMTJ2Mi4yMTNoMS4wMTJjLjM4IDAgLjY3NC0uMDk3Ljg4NS0uMjkuMjEtLjE5NS4zMTYtLjQ3My4zMTYtLjgzNiAwLS4zMzctLjEwNS0uNjAyLS4zMTYtLjc5Ni0uMjEtLjE5NC0uNTA2LS4yOTEtLjg4NS0uMjkxWk01OS41NDkgNy4wNjNjLjY5IDAgMS4zMjMuMTI2IDEuODk2LjM4LjU4Mi4yNTIgMS4wOC41OSAxLjQ5MiAxLjAxMS40MTQuNDIxLjczNC45MTkuOTYyIDEuNDkyLjIyNy41NjUuMzQxIDEuMTY0LjM0MSAxLjc5NiAwIC42MzItLjExNCAxLjIzNS0uMzQxIDEuODA4YTQuNDg1IDQuNDg1IDAgMCAxLS45NjIgMS40OGMtLjQxMy40MjEtLjkxLjc1OC0xLjQ5MiAxLjAxMWE0LjY0OCA0LjY0OCAwIDAgMS0xLjg5Ni4zOCA0LjczOCA0LjczOCAwIDAgMS0zLjQwMi0xLjM5MSA0LjQ4NCA0LjQ4NCAwIDAgMS0uOTYxLTEuNDggNC44NTUgNC44NTUgMCAwIDEtLjM0Mi0xLjgwOGMwLS42MzMuMTE0LTEuMjMxLjM0Mi0xLjc5Ni4yMjctLjU3My41NDgtMS4wNy45NjEtMS40OTIuNDEzLS40MjIuOTEtLjc1OSAxLjQ5Mi0xLjAxMmE0LjczNyA0LjczNyAwIDAgMSAxLjkxLS4zNzlabTAgNy42NzZhMi44IDIuOCAwIDAgMCAxLjEzOC0uMjI4Yy4zNTQtLjE2LjY1My0uMzcuODk4LS42MzIuMjUyLS4yNy40NS0uNTg2LjU5NC0uOTQ5YTMuMjcgMy4yNyAwIDAgMCAuMjE1LTEuMTg4IDMuMTcgMy4xNyAwIDAgMC0uMjE1LTEuMTc2IDIuNzkxIDIuNzkxIDAgMCAwLS41OTUtLjk0OSAyLjU0OCAyLjU0OCAwIDAgMC0uODk3LS42MzIgMi42NzMgMi42NzMgMCAwIDAtMS4xMzgtLjI0Yy0uNDEzIDAtLjc5Ny4wOC0xLjE1MS4yNGEyLjY3OCAyLjY3OCAwIDAgMC0uOTEuNjMyIDIuODk5IDIuODk5IDAgMCAwLS41ODIuOTQ5IDMuMTcgMy4xNyAwIDAgMC0uMjE1IDEuMTc2YzAgLjQyMS4wNzEuODE3LjIxNSAxLjE4OC4xNDMuMzYzLjMzNy42NzkuNTgxLjk0OS4yNTMuMjYxLjU1Ny40NzIuOTEuNjMyLjM1NS4xNTIuNzM5LjIyOCAxLjE1Mi4yMjhaJy8+PC9zdmc+"); + background-position: 50% 50%; + background-repeat: no-repeat; + width: 214px; + height: 56px; + border: none; + border-radius: 4px; +} + +.proconnect-button:hover { + background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScyMTEnIGhlaWdodD0nNTgnIGZpbGw9J25vbmUnPjxnIGNsaXAtcGF0aD0ndXJsKCNhKSc+PHBhdGggZmlsbD0nIzEyMTJGRicgZD0nTTIxMSAwSDB2NThoMjExVjBaJy8+PHBhdGggZmlsbD0nI2ZmZicgZD0nbTY5Ljk4NiAyNi4zNjggMS4xNTYtMS4wNzFjLjgzMyAxLjA1NCAxLjgxOSAxLjU5OCAyLjk0MSAxLjU5OCAxLjI5MiAwIDIuMDQtLjgxNiAyLjA0LTEuOTA0IDAtMi41NS01LjYyNy0yLjI0NC01LjYyNy02LjAzNSAwLTEuNzM0IDEuNDI4LTMuMTk2IDMuNDUxLTMuMTk2IDEuNjgzIDAgMi45MDcuNzY1IDMuNzkxIDEuOTM4bC0xLjE5IDEuMDM3Yy0uNjk3LTEuMDAzLTEuNTQ3LTEuNTQ3LTIuNTg0LTEuNTQ3LTEuMTA1IDAtMS44MzYuNzQ4LTEuODM2IDEuNzM0IDAgMi41NjcgNS42MjcgMi4yNDQgNS42MjcgNi4wNTIgMCAyLjAyMy0xLjU4MSAzLjM0OS0zLjY1NSAzLjM0OS0xLjc2OCAwLTMuMDc3LS42NjMtNC4xMTQtMS45NTVabTEwLjgxNy01LjcxMkg3OS40NmwxLjQ0NS00LjU1NmgxLjY0OWwtMS43NTEgNC41NTZabTQuODE4LTMuNDUxYy0uNTYgMC0xLjAyLS40NTktMS4wMi0xLjAyYTEuMDIgMS4wMiAwIDAgMSAxLjAyLTEuMDAzYy41NjEgMCAxLjAwMy40NTkgMS4wMDMgMS4wMDMgMCAuNTYxLS40NDIgMS4wMi0xLjAwMyAxLjAyWk04NC44OTEgMjh2LTguNTY4aDEuNDQ0VjI4SDg0Ljg5Wm0zLjc2Ny00LjI4NGMwLTIuNDk5IDEuNzE3LTQuNjI0IDQuNDAzLTQuNjI0IDEuMjQxIDAgMi4yNjEuNDU5IDMuMDQzIDEuMjkyVjE1LjI1aDEuNDQ1VjI4aC0xLjQ0NXYtLjk1MmMtLjc4Mi44MzMtMS44MDIgMS4yOTItMy4wNDMgMS4yOTItMi42ODYgMC00LjQwMy0yLjEyNS00LjQwMy00LjYyNFptMS41MyAwYzAgMS44MTkgMS4yMjQgMy4yNjQgMy4wNDMgMy4yNjQgMS4xOSAwIDIuMjEtLjU3OCAyLjg3My0xLjU5OFYyMi4wNWMtLjY4LTEuMDM3LTEuNy0xLjU5OC0yLjg3My0xLjU5OC0xLjgxOSAwLTMuMDQzIDEuNDQ1LTMuMDQzIDMuMjY0Wm0xOC4wMjMgMi44NzNjLS43OTkgMS4wNzEtMi4wNzQgMS43NTEtMy42NzIgMS43NTEtMi44OSAwLTQuNjc1LTIuMTI1LTQuNjc1LTQuNjI0IDAtMi42MDEgMS42NjYtNC42MjQgNC4zMTgtNC42MjQgMi4zMjkgMCAzLjg0MiAxLjU4MSAzLjg0MiAzLjcyMyAwIC4zNC0uMDUxLjY4LS4xMDIuOTE4aC02LjU2MnYuMDM0YzAgMS44ODcgMS4yOTIgMy4yNjQgMy4yMTMgMy4yNjQgMS4wODggMCAyLjAwNi0uNTEgMi41NjctMS4yNzVsMS4wNzEuODMzWm0tNC4wMTItNi4yNTZjLTEuMzk0IDAtMi4zOC43ODItMi43MiAyLjI2MWg1LjA4M2MtLjA1MS0xLjI0MS0uOTUyLTIuMjYxLTIuMzYzLTIuMjYxWk0xMTAuNDczIDI4di04LjU2OGgxLjQ0NXYuOTY5Yy42OTctLjc2NSAxLjU4MS0xLjMwOSAyLjg1Ni0xLjMwOSAxLjkyMSAwIDMuMzQ5IDEuMjkyIDMuMzQ5IDMuNzIzVjI4aC0xLjQ2MnYtNS4xMzRjMC0xLjUzLS44NS0yLjQxNC0yLjE3Ni0yLjQxNC0xLjI0MSAwLTIuMDIzLjcxNC0yLjU2NyAxLjYxNVYyOGgtMS40NDVabTExLjA1Mi0yLjg3M3YtNC4zNjloLTEuNjE1di0xLjMyNmgxLjYxNVYxNy4yOWgxLjQ2MnYyLjE0MmgyLjk3NXYxLjMyNmgtMi45NzV2NC4zNjljMCAxLjM0My42OCAxLjcxNyAxLjcxNyAxLjcxNy41NjEgMCAuOTUyLS4wNjggMS4yNzUtLjIwNHYxLjI5MmMtLjQwOC4xNy0uODY3LjIzOC0xLjQ3OS4yMzgtMS45MDQgMC0yLjk3NS0uOTUyLTIuOTc1LTMuMDQzWm03LjM3Ny03LjkyMmMtLjU2MSAwLTEuMDItLjQ1OS0xLjAyLTEuMDJhMS4wMiAxLjAyIDAgMCAxIDEuMDItMS4wMDNjLjU2MSAwIDEuMDAzLjQ1OSAxLjAwMyAxLjAwMyAwIC41NjEtLjQ0MiAxLjAyLTEuMDAzIDEuMDJaTTEyOC4xNzEgMjh2LTguNTY4aDEuNDQ1VjI4aC0xLjQ0NVptMy4zNzctOC41NjhoMS42MTV2LTEuMDU0YzAtMS44MzYgMS4yMDctMy4xMjggMy4wNDMtMy4xMjguOTUyIDAgMS43LjM0IDIuMjEuODMzbC0uOTAxIDEuMDU0YTEuNjMzIDEuNjMzIDAgMCAwLTEuMjkyLS41NzhjLS45MzUgMC0xLjU5OC42OC0xLjU5OCAxLjc4NXYxLjA4OGgyLjk3NXYxLjMyNmgtMi45NzVWMjhoLTEuNDYydi03LjI0MmgtMS42MTV2LTEuMzI2Wm04LjU0My0yLjIyN2MtLjU2MSAwLTEuMDItLjQ1OS0xLjAyLTEuMDJhMS4wMiAxLjAyIDAgMCAxIDEuMDItMS4wMDNjLjU2MSAwIDEuMDAzLjQ1OSAxLjAwMyAxLjAwMyAwIC41NjEtLjQ0MiAxLjAyLTEuMDAzIDEuMDJaTTEzOS4zNiAyOHYtOC41NjhoMS40NDVWMjhoLTEuNDQ1Wm0xMi4xMTUtMS40MTFjLS43OTkgMS4wNzEtMi4wNzQgMS43NTEtMy42NzIgMS43NTEtMi44OSAwLTQuNjc1LTIuMTI1LTQuNjc1LTQuNjI0IDAtMi42MDEgMS42NjYtNC42MjQgNC4zMTgtNC42MjQgMi4zMjkgMCAzLjg0MiAxLjU4MSAzLjg0MiAzLjcyMyAwIC4zNC0uMDUxLjY4LS4xMDIuOTE4aC02LjU2MnYuMDM0YzAgMS44ODcgMS4yOTIgMy4yNjQgMy4yMTMgMy4yNjQgMS4wODggMCAyLjAwNi0uNTEgMi41NjctMS4yNzVsMS4wNzEuODMzWm0tNC4wMTItNi4yNTZjLTEuMzk0IDAtMi4zOC43ODItMi43MiAyLjI2MWg1LjA4M2MtLjA1MS0xLjI0MS0uOTUyLTIuMjYxLTIuMzYzLTIuMjYxWk0xNTMuNzM3IDI4di04LjU2OGgxLjQ0NXYxLjA3MWMuNjI5LS43NDggMS40MTEtMS4yNDEgMi40OTktMS4yNDEuMjcyIDAgLjUyNy4wMzQuNzMxLjEwMnYxLjQ5NmEzLjEwNSAzLjEwNSAwIDAgMC0uODUtLjExOWMtMS4xMjIgMC0xLjg1My41NzgtMi4zOCAxLjQ0NVYyOGgtMS40NDVabTEzLjY4NS4zNGMtMS42ODMgMC0yLjgyMi0uOTUyLTIuODIyLTIuNDQ4IDAtMS4zMjYuOTg2LTIuMjc4IDIuODIyLTIuNTY3bDIuODczLS40NzZ2LS41OTVjMC0xLjE5LS44NS0xLjg3LTIuMDU3LTEuODctMS4wMDMgMC0xLjgzNi40NDItMi4zMjkgMS4xOWwtMS4wODgtLjgzM2MuNzQ4LTEuMDIgMS45NTUtMS42NDkgMy40NTEtMS42NDkgMi4xNzYgMCAzLjQ2OCAxLjI3NSAzLjQ2OCAzLjE2MlYyOGgtMS40NDV2LTEuMDg4Yy0uNjQ2LjkwMS0xLjcxNyAxLjQyOC0yLjg3MyAxLjQyOFptLTEuMzc3LTIuNDk5YzAgLjczMS42MjkgMS4yOTIgMS42MTUgMS4yOTIgMS4xMzkgMCAyLjA0LS41OTUgMi42MzUtMS41ODFWMjMuOTJsLTIuNTMzLjQ0MmMtMS4xOS4xODctMS43MTcuNzMxLTEuNzE3IDEuNDc5Wm03LjI1Mi02LjQwOWgxLjU2NGwyLjczNyA3LjA1NSAyLjczNy03LjA1NWgxLjU2NEwxNzguNTUgMjhoLTEuOTA0bC0zLjM0OS04LjU2OFptMTcuODU2IDcuMTU3Yy0uNzk5IDEuMDcxLTIuMDc0IDEuNzUxLTMuNjcyIDEuNzUxLTIuODkgMC00LjY3NS0yLjEyNS00LjY3NS00LjYyNCAwLTIuNjAxIDEuNjY2LTQuNjI0IDQuMzE4LTQuNjI0IDIuMzI5IDAgMy44NDIgMS41ODEgMy44NDIgMy43MjMgMCAuMzQtLjA1MS42OC0uMTAyLjkxOGgtNi41NjJ2LjAzNGMwIDEuODg3IDEuMjkyIDMuMjY0IDMuMjEzIDMuMjY0IDEuMDg4IDAgMi4wMDYtLjUxIDIuNTY3LTEuMjc1bDEuMDcxLjgzM1ptLTQuMDEyLTYuMjU2Yy0xLjM5NCAwLTIuMzguNzgyLTIuNzIgMi4yNjFoNS4wODNjLS4wNTEtMS4yNDEtLjk1Mi0yLjI2MS0yLjM2My0yLjI2MVptMTAuMTg1IDYuNjQ3YzEuMDU0IDAgMS45MDQtLjUxIDIuNDMxLTEuMjc1bDEuMTU2Ljg4NGMtLjc5OSAxLjA3MS0yLjA0IDEuNzUxLTMuNjA0IDEuNzUxLTIuODM5IDAtNC42NTgtMi4xMjUtNC42NTgtNC42MjQgMC0yLjQ5OSAxLjgxOS00LjYyNCA0LjY1OC00LjYyNCAxLjU0NyAwIDIuODA1LjY5NyAzLjYwNCAxLjc1MWwtMS4xNTYuODg0YTIuOTI1IDIuOTI1IDAgMCAwLTIuNDQ4LTEuMjc1Yy0xLjgzNiAwLTMuMTQ1IDEuNDQ1LTMuMTQ1IDMuMjY0IDAgMS44MzYgMS4zMDkgMy4yNjQgMy4xNjIgMy4yNjRaTTcwLjg1NCA0NVYzMi40aDQuMTU4YzIuNzcyIDAgNC40NjQgMS40MjIgNC40NjQgMy43NjIgMCAyLjMyMi0xLjY5MiAzLjc0NC00LjQ2NCAzLjc0NEg3My40MVY0NWgtMi41NTZabTQuMjY2LTEwLjQyMmgtMS43MXYzLjE1aDEuNzFjMS4wOCAwIDEuNzI4LS41NzYgMS43MjgtMS42MDIgMC0uOTU0LS42NDgtMS41NDgtMS43MjgtMS41NDhaTTgxLjI0OSA0NXYtOS4wNzJoMi4yODZ2LjljLjU5NC0uNjEyIDEuMzY4LTEuMDggMi4zOTQtMS4wOC4zMDYgMCAuNTc2LjA1NC43OTIuMTI2djIuMzk0YTMuOTM4IDMuOTM4IDAgMCAwLTEuMDA4LS4xMjZjLTEuMTE2IDAtMS44MzYuNjEyLTIuMTc4IDEuMTdWNDVoLTIuMjg2Wm0xMS4zODYtOS40MzJjMi45NTIgMCA0Ljk2OCAyLjE3OCA0Ljk2OCA0Ljg5NnMtMi4wMTYgNC44OTYtNC45NjggNC44OTYtNC45NjgtMi4xNzgtNC45NjgtNC44OTYgMi4wMTYtNC44OTYgNC45NjgtNC44OTZabS4wMzYgNy42MzJjMS40NTggMCAyLjU1Ni0xLjE3IDIuNTU2LTIuNzM2IDAtMS41ODQtMS4wOTgtMi43MzYtMi41NTYtMi43MzYtMS41MTIgMC0yLjYyOCAxLjE1Mi0yLjYyOCAyLjczNiAwIDEuNTg0IDEuMTE2IDIuNzM2IDIuNjI4IDIuNzM2Wm0xMy4xNzItLjIzNGMxLjQ0IDAgMi41NzQtLjcwMiAzLjI5NC0xLjcyOGwyLjAxNiAxLjU0OGMtMS4xNTIgMS41NjYtMy4wMjQgMi41NzQtNS4zMSAyLjU3NC0zLjk3OCAwLTYuNjk2LTMuMDYtNi42OTYtNi42NnMyLjcxOC02LjY2IDYuNjk2LTYuNjZjMi4yODYgMCA0LjE1OCAxLjAyNiA1LjMxIDIuNTU2bC0yLjAxNiAxLjU2NmMtLjcyLTEuMDI2LTEuODU0LTEuNzI4LTMuMjk0LTEuNzI4LTIuMzc2IDAtNC4wNjggMS44NTQtNC4wNjggNC4yNjZzMS42OTIgNC4yNjYgNC4wNjggNC4yNjZabTExLjM2Ni03LjM5OGMyLjk1MiAwIDQuOTY4IDIuMTc4IDQuOTY4IDQuODk2cy0yLjAxNiA0Ljg5Ni00Ljk2OCA0Ljg5Ni00Ljk2OC0yLjE3OC00Ljk2OC00Ljg5NiAyLjAxNi00Ljg5NiA0Ljk2OC00Ljg5NlptLjAzNiA3LjYzMmMxLjQ1OCAwIDIuNTU2LTEuMTcgMi41NTYtMi43MzYgMC0xLjU4NC0xLjA5OC0yLjczNi0yLjU1Ni0yLjczNi0xLjUxMiAwLTIuNjI4IDEuMTUyLTIuNjI4IDIuNzM2IDAgMS41ODQgMS4xMTYgMi43MzYgMi42MjggMi43MzZabTcuMDE4IDEuOHYtOS4wNzJoMi4yODZ2LjcyYy42My0uNjEyIDEuNDc2LTEuMDggMi42ODItMS4wOCAxLjk2MiAwIDMuNTI4IDEuMzUgMy41MjggNC4wMzJWNDVoLTIuMzIydi01LjMxYzAtMS4yMDYtLjY2Ni0xLjk2Mi0xLjc4Mi0xLjk2Mi0xLjE1MiAwLTEuNzY0Ljc3NC0yLjEwNiAxLjM1VjQ1aC0yLjI4NlptMTEuMDkxIDB2LTkuMDcyaDIuMjg2di43MmMuNjMtLjYxMiAxLjQ3Ni0xLjA4IDIuNjgyLTEuMDggMS45NjIgMCAzLjUyOCAxLjM1IDMuNTI4IDQuMDMyVjQ1aC0yLjMyMnYtNS4zMWMwLTEuMjA2LS42NjYtMS45NjItMS43ODItMS45NjItMS4xNTIgMC0xLjc2NC43NzQtMi4xMDYgMS4zNVY0NWgtMi4yODZabTE5LjQ0NC0xLjQ3NmMtLjg0NiAxLjEzNC0yLjI1IDEuODM2LTMuOTYgMS44MzYtMy4yMjIgMC01LjA0LTIuMjUtNS4wNC00Ljg5NiAwLTIuNjgyIDEuNjkyLTQuODk2IDQuNjYyLTQuODk2IDIuNTIgMCA0LjE3NiAxLjY5MiA0LjE3NiA0LjA2OCAwIC41MDQtLjA3Mi45OS0uMTQ0IDEuMjk2aC02LjM1NGMuMTQ0IDEuNDk0IDEuMTg4IDIuMzc2IDIuNzM2IDIuMzc2Ljk5IDAgMS44LS40MzIgMi4yODYtMS4wOGwxLjYzOCAxLjI5NlptLTQuMzM4LTYuMDQ4Yy0xLjExNiAwLTEuODcyLjU0LTIuMTc4IDEuNzI4aDQuMDg2Yy0uMDM2LS45LS43MDItMS43MjgtMS45MDgtMS43MjhabTEwLjY5NiA1LjcyNGMuODgyIDAgMS41ODQtLjQzMiAyLjAxNi0xLjA2MmwxLjgxOCAxLjM4NmMtLjg0NiAxLjExNi0yLjE3OCAxLjgzNi0zLjgzNCAxLjgzNi0zLjEzMiAwLTUuMDA0LTIuMjUtNS4wMDQtNC44OTZzMS44NzItNC44OTYgNS4wMDQtNC44OTZjMS42NTYgMCAyLjk4OC43MiAzLjgzNCAxLjgzNmwtMS44MTggMS4zODZjLS40MzItLjYzLTEuMTE2LTEuMDYyLTIuMDUyLTEuMDYyLTEuNDk0IDAtMi41OTIgMS4xNTItMi41OTIgMi43MzYgMCAxLjYwMiAxLjA5OCAyLjczNiAyLjYyOCAyLjczNlptNi4yMDQtMS41MTJ2LTMuNjcyaC0xLjY5MnYtMi4wODhoMS42OTJWMzMuNjZoMi4zMDR2Mi4yNjhoMi43NzJ2Mi4wODhoLTIuNzcydjMuNjcyYzAgMS4wMDguNTQgMS40MDQgMS40NCAxLjQwNC42MyAwIDEuMDQ0LS4wNzIgMS4zNS0uMTk4djEuOTk4Yy0uNDUuMTk4LS45OS4yODgtMS43NDYuMjg4LTIuMjY4IDAtMy4zNDgtMS4yNzgtMy4zNDgtMy40OTJaJy8+PHBhdGggZmlsbD0nIzAwMDA5MScgZD0nTTQ2Ljk5MiAxOS4wOTggMzEuOTk4IDEwLjQybC0xNC45OTQgOC43NmEuNjA2LjYwNiAwIDAgMC0uMzA2LjUyNXYxNi45NDhhLjY2Ni42NjYgMCAwIDAgLjMwNi41MjRsMTQuOTkyIDguNiAxNC45OTQtOC43MDZhLjY2Ni42NjYgMCAwIDAgLjMwNi0uNTI0VjE5LjYyNmEuNjA0LjYwNCAwIDAgMC0uMzA0LS41MjhaJy8+PHBhdGggZmlsbD0nI0ZDQzYzQScgZD0nbTI2LjY0MSAxOS41OTgtNS4wMjkgOC42MjgtNC41NTctOS4xNzUgNS4zOS0zLjExMyA0LjQ4OSAzLjE2LS4yOTMuNVptMjAuNjU2IDE2Ljk4VjE5LjYyYS42LjYgMCAwIDAtLjMwNi0uNTIzTDMxLjk5OCAxMC40MicvPjxwYXRoIGZpbGw9JyMwMDYzQ0InIGQ9J00xNi43IDM2LjU3OCAzMiAxMC40MnYzNS4zNjJsLTE0Ljk5Ni04LjYwNWEuNjY1LjY2NSAwIDAgMS0uMzA2LS41MjRWMTkuNzA2bC4wMDIgMTYuODcyWm0yNC42NjktMjAuNzM1IDUuNDU4IDMuMTU1LTQuNDg5IDkuMTUtNS4zODctOS4yMzYgNC40MTgtMy4wN1onLz48cGF0aCBmaWxsPScjZmZmJyBkPSdtNTEuNjA2IDE2LjMwMy0xOS4xOS0xMS4wMmEuOTMzLjkzMyAwIDAgMC0uODMyIDBsLTE5LjE5IDExLjAyYS44ODcuODg3IDAgMCAwLS4zOTQuNjk1djIyYS44ODUuODg1IDAgMCAwIC4zOTQuN2wxOS4xODkgMTEuMDJhLjkzMi45MzIgMCAwIDAgLjgzMiAwbDE5LjE5MS0xMS4wMmEuODg2Ljg4NiAwIDAgMCAuMzk0LS43di0yMmEuODg3Ljg4NyAwIDAgMC0uMzk0LS42OTVaTTIyLjc4OSAzNC4wNTloLjA3OWMtLjA0MiAwLS4wNzkuMDA3LS4wNzkuMDUgMCAuMS4xNTEgMCAuMi4xYS45MTIuOTEyIDAgMCAwLS42MjkuMjc2YzAgLjA1LjEuMDUuMTUxLjA1LS4wNzUuMS0uMjI2LjA1LS4yNzcuMTUyYS4xNzYuMTc2IDAgMCAwIC4xLjA1Yy0uMDUgMC0uMSAwLS4xLjA1di4xNTJjLS4xMjYgMC0uMTc2LjEtLjI3Ny4xNS4yLjE1Mi4zMjcgMCAuNTI4IDAtLjUyOC4yLS45NTYuNDc5LTEuNDg0LjYzLS4xIDAgMCAuMTUtLjEuMTUuMTUxLjEuMjI3LS4wNS4zNzctLjA1LS42NTQuMzc4LTEuMzMzLjctMi4wMzcgMS4xMzNhLjM1MS4zNTEgMCAwIDAtLjEuMmgtLjJjLS4xLjA1LS4wNS4xNzYtLjE1MS4yNzcuMjI2LjE1LjUtLjIuNjU0IDAgLjA1IDAtLjEuMDUtLjIuMDUtLjA1IDAtLjA1LjEtLjEuMWgtLjE1NGMtLjEuMDc1LS4yLjEyNi0uMi4yNzZhLjIyLjIyIDAgMCAwLS4yMjYuMSA5LjAzMSA5LjAzMSAwIDAgMCAzLjE0NC0uNTc4IDcuNjgzIDcuNjgzIDAgMCAwIDIuMDg4LTEuNTYuMTc2LjE3NiAwIDAgMSAuMDUuMWMtLjE0Ny40MzctLjQzLjgxNi0uODA2IDEuMDgtLjI3Ny4xNTItLjQ3OC4zNzgtLjcuNDc5YTQuMDU3IDQuMDU3IDAgMCAwLS40MjguMjc2Yy0uNjMyLjE5Ny0xLjI4MS4zMzUtMS45MzkuNDEybC0uMzA1LjA0NGMtLjIyNS4wMzMtLjQ0OS4wNjktLjY3MS4xMDhsLTEuOTkzLTEuMTM4YS42NDcuNjQ3IDAgMCAxLS4yODgtLjQxMS41Ny41NyAwIDAgMCAuMDk0LS4wNjMuMjY2LjI2NiAwIDAgMC0uMTEzLS4wNzF2LS42NWExMi43ODIgMTIuNzgyIDAgMCAwIDMuMDM4LS45NDIgOC43NDYgOC43NDYgMCAwIDAtMy4wMzctMS4zNDN2LTEuNTE1YTExLjY3IDExLjY3IDAgMCAxIDEuNjM5LjM5MiA2LjQyIDYuNDIgMCAwIDEgMS4xODIuNTc4Yy4xNDcuMTQuMzA3LjI2Ny40NzguMzc3YS45MS45MSAwIDAgMCAuOC4wNWguMzNhMy45NjEgMy45NjEgMCAwIDAgMS45MzctLjkwNWMwIC4wNS4wNS4wNS4xLjA1YTMuNjI5IDMuNjI5IDAgMCAxLS40MjggMS4xMzJjLjAwMy4wNS0uMDQ4LjE1Mi4wNTMuMjAyWm0yLjgxNyAzLjU3Yy4yNTEtLjEuNC0uMjc2LjYyOS0uMzc2LS4wNS4wNS0uMDUuMTUtLjEuMmEzLjY5OSAzLjY5OSAwIDAgMC0uNTI4LjQgMTUuOTY1IDE1Ljk2NSAwIDAgMC0xLjU4NSAxLjYxYy0uMjUyLjMtLjUyOC41NzgtLjguODU1LS4wOTYuMDktLjIuMTcyLS4zMS4yNDVsLTIuNTI3LTEuNDVjLjM2LjAzLjcyMS4wMTMgMS4wNzYtLjA1My4yOTQtLjA4My41OC0uMTkyLjg1NS0uMzI3di4xYy43LS4yNzcgMS4yMzItLjkwNiAxLjkzNy0xLjEzMi4wMjUgMCAuMTI2LjEuMjI2LjA1YTEuODgzIDEuODgzIDAgMCAxIDEuNTA5LS43YzAgLjA1IDAgLjEuMDUuMWguMDI1Yy0uMTUxLjEyNi0uMzI3LjI1LS41LjM3Ny0uMDU3LjA1Mi0uMDA3LjEwMi4wNDMuMTAyWm0tOC45MDgtNi4xNjN2LS4xODZhNS44MTcgNS44MTcgMCAwIDEgMS41ODgtLjE4OCAxLjUyIDEuNTIgMCAwIDEgLjQ3OCAwIDUuODYgNS44NiAwIDAgMC0yLjA2Ni4zNzRabTMwLjYgNS4wODhhLjY2NS42NjUgMCAwIDEtLjMwNi41MjRsLTEwLjA3OSA1Ljg1YTMyLjI5NiAzMi4yOTYgMCAwIDEtMy40MDgtMS4xODQgMi44MjYgMi44MjYgMCAwIDEtLjA1LTIuMjQ1Yy4wOC0uMzA4LjE5OC0uNjA1LjM1Mi0uODgzLjAyNS0uMDI1LjA1LS4wNS4wNS0uMDc2YS4wMjUuMDI1IDAgMCAwIC4wMjUtLjAyNSA0LjMyIDQuMzIgMCAwIDEgLjM3Ny0uNTU1bC4wMTUtLjAxNS4wMi0uMDIxLjAxNS0uMDE1YzAtLjAyNS4wMjUtLjA1LjA1LS4wNzYuMDI1LS4wNTEuMDc1LS4wNzYuMS0uMTI2LjE3Ni0uMTg2LjM3LS4zNTQuNTc5LS41LjIxMy0uMDc3LjQzMS0uMTM2LjY1NC0uMTc3LjgxMS4wNiAxLjYxNy4xNyAyLjQxNS4zMjhhLjc1Mi43NTIgMCAwIDEgLjI3Ny4xYy4zMDEuMDU5LjYxMi4wNDEuOTA1LS4wNWExLjEzNyAxLjEzNyAwIDAgMCAuODU1LS43MDYgMS4yMTIgMS4yMTIgMCAwIDAgLjA1LTEuMDZjLS4xNzgtLjI3NS0uMDEzLS40MzYuMTgxLS41OWwuMDY4LS4wNTRjLjA4Ni0uMDYxLjE2NC0uMTM0LjIzMS0uMjE2LjEyNi0uMjUyLS4xLS40LS4xNTEtLjYzLS4wNS0uMS0uMjI2LS4wNS0uMzI3LS4yLjM1Mi0uMTUxLjg1NS0uNDMuNjI5LS44NTctLjE1MS0uMjI3LS4zNzctLjYzLS4xLS44NTcuMzUyLS4yLjg1NS0uMTUxIDEuMDA2LS40OGExLjEzNyAxLjEzNyAwIDAgMC0uMjkyLTEuMDg0bC0uMDc1LS4xMDhhNC43NTQgNC43NTQgMCAwIDEtLjIxMS0uMzIgNi45MDUgNi45MDUgMCAwIDAtLjUyOC0uNzU3IDQuMjk3IDQuMjk3IDAgMCAxLS41MjgtMS4wMWMtLjE1MS0uMzc3LjA1LS43MDUuMDUtMS4wODNhNi4zNDcgNi4zNDcgMCAwIDAtLjMyNy0yLjE0NGMtLjEyNi0uMzUzLS4xNzYtLjczMS0uMzI3LTEuMDZhMS4xMiAxLjEyIDAgMCAwLS4yMjYtLjU4LjM3NC4zNzQgMCAwIDEgMC0uMzI3Yy4yMDUtLjE0NS4zOTktLjMwNS41NzktLjQ4YS41NjcuNTY3IDAgMCAwLS4yLS43MDVjLS4zMjctLjE1MS0uMy4zMjgtLjUyOC40MjloLS4xNTFjLS4wNS0uMTI2LjA1LS4xNzcuMTUxLS4yNzcgMC0uMDUgMC0uMTUxLS4wNS0uMTUxLS4yIDAtLjM3Ny0uMDUxLS40MjgtLjE1MWEzLjk1NyAzLjk1NyAwIDAgMC0xLjg2MS0xLjI4NmMuMTg4LjA1OC4zODIuMDkxLjU3OS4xLjMzOC4wNzEuNjkuMDM2IDEuMDA2LS4xLjIyNy0uMDc2LjI3Ny0uNDguMzc3LS43MDZhLjguOCAwIDAgMC0uMTUxLS42MzEgMi4xOSAyLjE5IDAgMCAwLS45MDYtLjc1NiA5LjEzIDkuMTMgMCAwIDEtLjY3OS0uMzUzLjk1Ni45NTYgMCAwIDAtLjI1MS0uMTI2Yy0yLjk2NS0xLjQ4NS05LjA2OS0uMi05LjUzNCAwaC0uMDA5YTguMjU0IDguMjU0IDAgMCAwLTEuMjQ5LjQ3NSAzLjkyMiAzLjkyMiAwIDAgMC0yLjM2NSAyLjQ2NSAzLjgzIDMuODMgMCAwIDAtMS4zMzMgMS41MDljLS40MjguOC0xLjA1NiAxLjUwOS0uOTU2IDIuNDE0LjEuNzguMjc3IDEuNDg0LjQyOCAyLjI4OS4wNDMuMjcyLjExLjU0LjIuOC4xLjI3NiAwIC42MjkuMTUxLjg1NS4wNzUuMTUuMDI1LjMyNy4yMjcuNDI4di4yYy4wNS4wNS4wNS4xLjE1MS4xdi4yYy40MzUuNDIzLjgwNy45MDYgMS4xMDcgMS40MzQuMS4yNzYtLjQ3OC4xNS0uNy4wNWE1Ljk3NyA1Ljk3NyAwIDAgMS0xLjEzMi0uOTU2LjE3Ni4xNzYgMCAwIDAtLjA1MS4xYy4yLjM1Mi45MDYuNzguNTI4IDEuMDA2LS4yLjEtLjQyOC0uMTUxLS42MjkuMDUtLjA1LjA3NiAwIC4xNzcgMCAuMjc3LS4yNzctLjItLjU3OC0uMS0uODU1LS4yLS4yLS4wNS0uMjUyLS40MjctLjQ3OC0uNDI3YTE1LjE5MSAxNS4xOTEgMCAwIDAtMS44MTEtLjMyNyAxNS4xNDQgMTUuMTQ0IDAgMCAwLTEuNzM5LS4xNlYxOS43MDdhLjYwNi42MDYgMCAwIDEgLjMwNi0uNTI0bDE0Ljk4Ny04Ljc2MSAxNC45OTQgOC42NzdhLjYwNS42MDUgMCAwIDEgLjMwNi41MjR2MTYuOTMyWm0tNy45NTQtOC4yNjFhLjMyNS4zMjUgMCAwIDEtLjI4Mi4xNDkgMi44NCAyLjg0IDAgMCAwLS4yODIuMjczYy4xIDAgMCAuMTQ5LjEuMTQ5LS4yMDUuMjIzLjA3Ny42OTQtLjIwNS43OTMtLjM3LjA5OS0uNzU4LjA5OS0xLjEyNyAwYS43MjcuNzI3IDAgMCAxIC4xNjctLjAxNmguMDg1YS4zODIuMzgyIDAgMCAwIC4zMzctLjEzMnYtLjJjMC0uMDUtLjA1MS0uMDUtLjEtLjA1YS4xNi4xNiAwIDAgMS0uMS4wNS4yMjMuMjIzIDAgMCAwLS4xNTQtLjIuODA2LjgwNiAwIDAgMS0uNzE4LS4yNzMuNjcuNjcgMCAwIDEgLjQzNi0uMDVjLjEyOCAwIC4wNzctLjIyMy4yMzEtLjMyMmguMTU0Yy4zMDctLjM3Mi44NzEtLjQ3MS45NzQtLjg0MyAwLS4xLS4yODItLjEtLjQ4Ny0uMTVhMi4yNiAyLjI2IDAgMCAwLS44Mi4wNWMtLjM2LjA1LS43MTIuMTQyLTEuMDUxLjI3NC4yOC0uMjA2LjU5Mi0uMzY1LjkyMy0uNDcxLjIzMi0uMDkuNDczLS4xNTcuNzE4LS4ybC4xMzItLjAyNi4xMzMtLjAyN2EuOTcuOTcgMCAwIDEgLjU1NiAwYy4yMzEuMS42MTUuMS42NjYuMjQ4LjEuMjczLS4xNTQuNTQ1LS40MzUuNzQ0LS4wNTcuMDguMTQ5LjEzNS4xNDkuMjNaJy8+PHBhdGggZmlsbD0nI0ZDQzYzQScgZD0nTTY0LjU2IDVIMzlhMiAyIDAgMCAwLTIgMnY5LjMwMmEyIDIgMCAwIDAgMiAyaDI1LjU2YTIgMiAwIDAgMCAyLTJWN2EyIDIgMCAwIDAtMi0yWicvPjxwYXRoIGZpbGw9JyMxNjE2MTYnIGQ9J00zOS41NjIgMTYuMTY4VjcuMzE2aDIuOTIxYy45NyAwIDEuNzMyLjIzNiAyLjI4OS43MDguNTY1LjQ3Mi44NDcgMS4xMTcuODQ3IDEuOTM1IDAgLjgxLS4yODIgMS40NS0uODQ3IDEuOTIyLS41NTcuNDcyLTEuMzIuNzA4LTIuMjg5LjcwOGgtMS4xMjV2My41NzloLTEuNzk2Wm0yLjk5Ny03LjMyMmgtMS4yMDF2Mi4yMTNoMS4yYy4zOCAwIC42NzUtLjA5Ny44ODYtLjI5LjIyLS4xOTUuMzI5LS40NzMuMzI5LS44MzYgMC0uMzM3LS4xMS0uNjAyLS4zMjktLjc5Ni0uMjEtLjE5NC0uNTA2LS4yOTEtLjg4NS0uMjkxWk00Ny4yMyAxNi4xNjhWNy4zMTZoMi43MDdjLjk3IDAgMS43MzYuMjM2IDIuMzAxLjcwOC41NjUuNDcyLjg0NyAxLjExNy44NDcgMS45MzUgMCAuNTMtLjEyNi45OTUtLjM3OSAxLjM5LS4yNDQuMzg5LS41OS42ODgtMS4wMzcuODk5bDIuNzgyIDMuOTJoLTIuMTVsLTIuMzUyLTMuNTc5aC0uOTIzdjMuNTc5aC0xLjc5NVptMi44MDgtNy4zMjJoLTEuMDEydjIuMjEzaDEuMDEyYy4zOCAwIC42NzQtLjA5Ny44ODUtLjI5LjIxLS4xOTUuMzE2LS40NzMuMzE2LS44MzYgMC0uMzM3LS4xMDUtLjYwMi0uMzE2LS43OTYtLjIxLS4xOTQtLjUwNi0uMjkxLS44ODUtLjI5MVpNNTkuNTQ5IDcuMDYzYy42OSAwIDEuMzIzLjEyNiAxLjg5Ni4zOC41ODIuMjUyIDEuMDguNTkgMS40OTIgMS4wMTEuNDE0LjQyMS43MzQuOTE5Ljk2MiAxLjQ5Mi4yMjcuNTY1LjM0MSAxLjE2NC4zNDEgMS43OTYgMCAuNjMyLS4xMTQgMS4yMzUtLjM0MSAxLjgwOGE0LjQ4NSA0LjQ4NSAwIDAgMS0uOTYyIDEuNDhjLS40MTMuNDIxLS45MS43NTgtMS40OTIgMS4wMTFhNC42NDggNC42NDggMCAwIDEtMS44OTYuMzggNC43MzggNC43MzggMCAwIDEtMy40MDItMS4zOTEgNC40ODQgNC40ODQgMCAwIDEtLjk2MS0xLjQ4IDQuODU1IDQuODU1IDAgMCAxLS4zNDItMS44MDhjMC0uNjMzLjExNC0xLjIzMS4zNDItMS43OTYuMjI3LS41NzMuNTQ4LTEuMDcuOTYxLTEuNDkyLjQxMy0uNDIyLjkxLS43NTkgMS40OTItMS4wMTJhNC43MzcgNC43MzcgMCAwIDEgMS45MS0uMzc5Wm0wIDcuNjc2YTIuOCAyLjggMCAwIDAgMS4xMzgtLjIyOGMuMzU0LS4xNi42NTMtLjM3Ljg5OC0uNjMyLjI1Mi0uMjcuNDUtLjU4Ni41OTQtLjk0OWEzLjI3IDMuMjcgMCAwIDAgLjIxNS0xLjE4OCAzLjE3IDMuMTcgMCAwIDAtLjIxNS0xLjE3NiAyLjc5MSAyLjc5MSAwIDAgMC0uNTk1LS45NDkgMi41NDggMi41NDggMCAwIDAtLjg5Ny0uNjMyIDIuNjczIDIuNjczIDAgMCAwLTEuMTM4LS4yNGMtLjQxMyAwLS43OTcuMDgtMS4xNTEuMjRhMi42NzggMi42NzggMCAwIDAtLjkxLjYzMiAyLjg5OSAyLjg5OSAwIDAgMC0uNTgyLjk0OSAzLjE3IDMuMTcgMCAwIDAtLjIxNSAxLjE3NmMwIC40MjEuMDcxLjgxNy4yMTUgMS4xODguMTQzLjM2My4zMzcuNjc5LjU4MS45NDkuMjUzLjI2MS41NTcuNDcyLjkxLjYzMi4zNTUuMTUyLjczOS4yMjggMS4xNTIuMjI4WicvPjwvZz48ZGVmcz48Y2xpcFBhdGggaWQ9J2EnPjxwYXRoIGZpbGw9JyNmZmYnIGQ9J00wIDBoMjExdjU4SDB6Jy8+PC9jbGlwUGF0aD48L2RlZnM+PC9zdmc+"); +} + +/* proconnect button end */ diff --git a/frontend/tchap/vite.tchap.config.ts b/frontend/tchap/vite.tchap.config.ts new file mode 100644 index 000000000..ce760257e --- /dev/null +++ b/frontend/tchap/vite.tchap.config.ts @@ -0,0 +1,24 @@ +import { resolve } from "node:path"; +import { defineConfig, mergeConfig } from "vite"; +import viteConfig from "../vite.config"; + +export default defineConfig((env) => + mergeConfig( + viteConfig(env), + defineConfig({ + //tchap config + build: { + rollupOptions: { + input: [ + resolve(__dirname, "css/tchap.css"), + //resolve(__dirname, "../node_modules/@gouvfr-lasuite/integration/dist/css/prefixed-dsfr.css"), + resolve( + __dirname, + "../node_modules/@gouvfr-lasuite/integration/dist/css/homepage-full.css", + ), + ], + }, + }, + }), + ), +); diff --git a/frontend/tests/routes/__snapshots__/reset-cross-signing.test.tsx.snap b/frontend/tests/routes/__snapshots__/reset-cross-signing.test.tsx.snap index 35ec9d823..2fe079026 100644 --- a/frontend/tests/routes/__snapshots__/reset-cross-signing.test.tsx.snap +++ b/frontend/tests/routes/__snapshots__/reset-cross-signing.test.tsx.snap @@ -5,6 +5,24 @@ exports[`Reset cross signing > renders the cancelled page 1`] = `
+
+
+ Logo Tchap +
+
+ Tchap +
+
@@ -44,41 +62,32 @@ exports[`Reset cross signing > renders the cancelled page 1`] = ` If you're signed out everywhere and don't remember your recovery code, you'll still need to reset your identity.

@@ -89,6 +98,24 @@ exports[`Reset cross signing > renders the deep link page 1`] = `
+
+
+ Logo Tchap +
+
+ Tchap +
+
@@ -217,41 +244,32 @@ exports[`Reset cross signing > renders the deep link page 1`] = ` Cancel
@@ -262,6 +280,24 @@ exports[`Reset cross signing > renders the errored page 1`] = `
+
+
+ Logo Tchap +
+
+ Tchap +
+
@@ -305,41 +341,32 @@ exports[`Reset cross signing > renders the errored page 1`] = ` Back
@@ -350,6 +377,24 @@ exports[`Reset cross signing > renders the page 1`] = `
+
+
+ Logo Tchap +
+
+ Tchap +
+
@@ -478,41 +523,32 @@ exports[`Reset cross signing > renders the page 1`] = ` Back
@@ -523,6 +559,24 @@ exports[`Reset cross signing > renders the success page 1`] = `
+
+
+ Logo Tchap +
+
+ Tchap +
+
@@ -556,6 +610,34 @@ exports[`Reset cross signing > renders the success page 1`] = ` > The identity reset has been approved for the next 10 minutes. You can close this window and go back to the app to continue.

+
`; @@ -565,6 +647,24 @@ exports[`Reset cross signing > renders the success page 2`] = `
+
+
+ Logo Tchap +
+
+ Tchap +
+
@@ -598,6 +698,34 @@ exports[`Reset cross signing > renders the success page 2`] = ` > The identity reset has been approved for the next 10 minutes. You can close this window and go back to the app to continue.

+
`; diff --git a/frontend/tests/routes/account/__snapshots__/index.test.tsx.snap b/frontend/tests/routes/account/__snapshots__/index.test.tsx.snap index 2fd8ac416..50453d9a2 100644 --- a/frontend/tests/routes/account/__snapshots__/index.test.tsx.snap +++ b/frontend/tests/routes/account/__snapshots__/index.test.tsx.snap @@ -297,6 +297,24 @@ exports[`Account home page > renders the page 1`] = `
+
+
+ Logo Tchap +
+
+ Tchap +
+
@@ -738,41 +756,32 @@ exports[`Account home page > renders the page 1`] = ` />
diff --git a/tchap/.env.sample b/tchap/.env.sample new file mode 100644 index 000000000..ce65135d7 --- /dev/null +++ b/tchap/.env.sample @@ -0,0 +1,2 @@ +#Copy synapse secret from `element-docker-demo/data/mas/config.yaml` +HOMESERVER_SECRET= \ No newline at end of file diff --git a/tchap/.gitignore b/tchap/.gitignore new file mode 100644 index 000000000..d89b5bf40 --- /dev/null +++ b/tchap/.gitignore @@ -0,0 +1,3 @@ +tmp + +.env \ No newline at end of file diff --git a/tchap/README.md b/tchap/README.md new file mode 100644 index 000000000..57cf21edb --- /dev/null +++ b/tchap/README.md @@ -0,0 +1,74 @@ +# Tchap customizations + +All customization in the upstream codebase are to be surrounded with `:tchap:` tag + +## Server side logic + +Tchap customization in the Rust server codebase are located in the [tchap crate](../crates/tchap). + +## Building templates with Tchap customization + +[Tchap customizations](resources/templates) override some of the default templates. + +MAS templates relies on static resources from the frontend app (mainly css), therefore some Tchap resources have been added there via the default manifest.json + +The manifest.json is extended for Tchap in the vite config [vite.tchap.config.ts](../frontend/tchap/vite.tchap.config.ts) + + +## Customizations in the frontend app + +Tchap custom React components are located in a [subdirectory](../frontend/tchap) of the frontend app. + +## Custom pipeline + +For building the Docker image, the [`build` github action](../.github/workflows/build.yaml) packages all MAS resources enhanced with Tchap customizations. + +## Web dev + +- start your docker engine, on Macos Docker Desktop + +- [install rust](https://www.rust-lang.org/tools/install) : curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + +- on MacOs install `brew install fswatch` + +- run `start.sh`, if you work on templates: `start-with-hot-reload.sh` + +- edit templates in `./tchap/resources/templates` + +If Synapse integration is needed, install the environment from element-docker-demo and run it (see README.md) + +Copy synapse secret from `element-docker-demo/data/mas/config.yaml` to .env file : HOMESERVER_SECRET= + + +# Important knowledge + +Serve directory is defined in the config.yaml, by default it is `path: "./frontend/dist/"` + +``` +http: + listeners: + - name: web + resources: + - name: assets + path: "/resources/manifest.json" + +``` + +See in the logs + +``` +2025-06-03T12:28:33.967776Z INFO mas_cli::commands::server:297 Listening on http://[::]:8080 with resources [Discovery, Human, OAuth, Compat, GraphQL { playground: false, undocumented_oauth2_access: false }, Assets { path: "./frontend/dist/" }, AdminApi] + +``` + +# troubleshoot + + +### error : This endpoint must only be called by MAS + +hint : your HOMESERVER_SECRET is probably not correct + +` +M_UNKNOWN: This endpoint must only be called by MAS +HTTP status client error (403 Forbidden) for url (https://matrix.tchapgouv.com/_synapse/mas/is_localpart_available?localpart=olivierdelcroix) +` \ No newline at end of file diff --git a/tchap/build_conf.sh b/tchap/build_conf.sh new file mode 100755 index 000000000..081947ea8 --- /dev/null +++ b/tchap/build_conf.sh @@ -0,0 +1,61 @@ +#!/bin/sh + +set -e + +echo "Starting configuration build process..." + +# Get the directory where this script is located +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Set MAS_HOME to the parent directory (project root) +export MAS_HOME="$(dirname "$SCRIPT_DIR")" +export MAS_TCHAP_HOME=$SCRIPT_DIR + +echo "Step 1/7: Loading environment variables..." +# Source the .env file to load environment variables +if [ -f $MAS_TCHAP_HOME/.env ]; then + source $MAS_TCHAP_HOME/.env +else + echo "Error: .env file not found. Please create a $MAS_TCHAP_HOME/.env file with the required environment variables." + exit 1 +fi + +echo "Step 2/7: Preparing template directories..." +# New template directory +MAS_TCHAP_DATA="$MAS_TCHAP_HOME/tmp" + +# Create tmp directory +if [ ! -d "$MAS_TCHAP_DATA" ]; then + echo "Creating MAS tchap temp folder..." + mkdir -p "$MAS_TCHAP_DATA" +fi + +echo "Step 3/7: Copying MAS templates..." +cp -r "$MAS_HOME/templates" "$MAS_TCHAP_DATA" + +echo "Step 4/7: Overriding with custom Tchap templates..." +cp -r "$MAS_HOME/tchap/resources/templates" "$MAS_TCHAP_DATA" + +echo "Step 5/7: Building MAS config file..." +template_yaml_file="$MAS_TCHAP_HOME/conf/config.template.yaml" +yaml_file="$MAS_TCHAP_DATA/config.local.dev.yaml" +cp $template_yaml_file $yaml_file +MAS_TCHAP_TEMPLATES="$MAS_TCHAP_DATA/templates" +sed -i '' -E "/^templates:/,/^[^[:space:]]/ s|^[[:space:]]*path:.*| path: \"$MAS_TCHAP_TEMPLATES\"|" "$yaml_file" + +echo "Step 6/7: Updating translations..." +MAS_TCHAP_TRANSLATIONS="$MAS_HOME/tchap/resources/translations" +cargo run -p mas-i18n-scan -- --update "${MAS_TCHAP_TEMPLATES}" "${MAS_TCHAP_TRANSLATIONS}/en.json" +sed -i '' -E "/^templates:/,/^[^[:space:]]/ s|^[[:space:]]*translations_path:.*| translations_path: \"$MAS_TCHAP_TRANSLATIONS\"|" "$yaml_file" + +echo "Step 7/7: Updating matrix secret..." +# Replace the placeholder secret value with the environment variable or warning message +if [ -n "${HOMESERVER_SECRET+x}" ] && [ -n "$HOMESERVER_SECRET" ]; then + # HOMESERVER_SECRET is defined and not empty + sed -i '' -E "s|secret: 'TO BE COPY'|secret: '$HOMESERVER_SECRET'|" "$yaml_file" +else + sed -i '' -E "s|secret: 'TO BE COPY'|secret: 'WARNING NO HOMESERVER SECRET DEFINED'|" "$yaml_file" + echo "WARNING: HOMESERVER_SECRET is not defined or empty. Using warning message instead." +fi + +echo "Configuration build completed successfully!" diff --git a/tchap/clean.sh b/tchap/clean.sh new file mode 100755 index 000000000..7d9556df5 --- /dev/null +++ b/tchap/clean.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +echo "Starting cleanup process..." + +echo "Step 1/6: Stopping Docker containers..." +docker compose down + +echo "Step 2/6: Removing policy WASM file..." +rm -rf ../policies/policy.wasm + +#echo "Step 3/6: Removing Rust build artifacts..." +#rm -rf ../target/ + +echo "Step 4/6: Removing temporary files..." +rm -rf tmp/ + +echo "Step 5/6: Removing frontend dependencies..." +rm -rf ../frontend/node_modules/ + +echo "Step 6/6: Removing frontend build files..." +rm -rf ../frontend/dist/ + +echo "Cleanup completed successfully!" diff --git a/tchap/conf/config.template.yaml b/tchap/conf/config.template.yaml new file mode 100644 index 000000000..ce02dd4a5 --- /dev/null +++ b/tchap/conf/config.template.yaml @@ -0,0 +1,255 @@ +http: + listeners: + - name: web + resources: + - name: discovery + - name: human + - name: oauth + - name: compat + - name: graphql + - name: assets + # for api admin calls + - name: adminapi + binds: + - address: '[::]:8080' + proxy_protocol: false + - name: internal + resources: + - name: health + # for api admin calls + - name: adminapi + binds: + - host: localhost + port: 8081 + proxy_protocol: false + trusted_proxies: + - 192.168.0.0/16 + - 172.16.0.0/12 + - 10.0.0.0/10 + - 127.0.0.1/8 + - fd00::/8 + - ::1/128 + # public_base: http://[::]:8080/ + # issuer: http://[::]:8080/ + public_base: https://auth.tchapgouv.com/ + issuer: https://auth.tchapgouv.com/ +database: + uri: postgresql://postgres:postgres@localhost:5439/postgres + max_connections: 10 + min_connections: 0 + connect_timeout: 30 + idle_timeout: 600 + max_lifetime: 1800 +email: + from: '"Authentication Service" ' + reply_to: '"Authentication Service" ' + transport: smtp + mode: plain + hostname: 127.0.0.1 + port: 1025 + +secrets: + encryption: eb38b8b9087842b3345269f3c6ca92b2a8d6aa63e3a773d23ed1c9cb45c5ef83 + keys: + - kid: dyrZtIyXSA + key: | + -----BEGIN RSA PRIVATE KEY----- + MIIEogIBAAKCAQEAukLyWSv9KOeBsmIG4ntL/BP+wj5L4GbOyAOEzRBBO0ZbBYVn + 1L/aYQ65cQpQKK0MzH5cn7TvIY0JBh/ZsSm3e7DdhGJzoqPrW6E0/6QqXEl4gN1q + DUCMj2CwAcT9OX1Wt6cNq70+gbqp6+yT+Nn++KPylHa/V9wRxkaV3fyM+XYVeddB + amDR+fBjHgVXQ3xk2ezUBS3AmyKBgETnHufKCkxJ5mXdU9HT0ewg8J2PrgiRBwDj + XP7C5Zif/+NfYPO2mM/b0Y91pl9aXuUHX+/zlqtpcwX/WprEsCaRUa9qWHuiftMf + uelsflCgZ0KumZjzr3wPDEfu8n7WbVEObNxF+QIDAQABAoIBAEl82mNGUMbPuEMq + G+9FmDAnr27x5zvtNA6EHORPUn1Rf94IyXOOEloS1iV8XS3/QLp57I9ycpq5K2NI + M7qLbAIYQP3XXipAJD7ttpxaKABrWGj3cr0xx4NWMXsxPnttMUaaWXF14/CJNjuI + BsW7NLbi8HWU+F9wy26AMOb5mqFdQfk15H3LaM3L3hMuV5DOcA467luwHmuGGTji + VjZg3yZZF2ROtTwwVSB7UCokmW3FZys/U4SyzXSrboUxF9T3PW3Hxm9e1JfFQuLh + rn85Q4IDpRtK2O5ECmKjY0cyvQOVItQytTeVXtSEzgMfK5VpnYMefdCFvhExcsuV + JHT1c/UCgYEAziAPCJYNTXsvo3yEW1iWnAjfi6R7g9aluNDjf4xmva5DTOdZSH1d + AJkzXZtPYXXlR42RT4FzE/ICdjo81aDMrMHwoIiH9p1n7IdDTt3GONYi3VPKGvZd + Ghgdq5jgdedDhF9MximZcWdZOZLCymKME61RuCg18p/MMIJ/FRNBE38CgYEA51R6 + CjNc93qO7hL7AGu+evs3/PVrHsg5iBf8GcGeyNNGkA5TJcYTO075VBDQuWsMZbvx + d0jBjROs8U2AJzgbh6+N/rZACflk8W4LyD3Pw+1coIcckH4hmgN37vkh63qiGX8K + YVe8CrbGliBB2OccXsdJVDe0f45kte0eJh6cAocCgYAH9d7+wuTCoEZHtxBZgsNW + RVV0zCZlAg4mZBLVIzP4kVlSCAE/tm+4DTKZo9zd87KmH8aD3oj2NTt5G2isC2i8 + J0VGvd8aXBveW57y1cfI/CQejhTZE7imwFWtAdtxUjweSZvqb0LYyVf9zDgvnryw + KdplFVB4DUnSece0pai2uwKBgDzV335dQZ6nsXz0quPScfZ/qJqyo+gledPLkvXn + EG35+f2ads1hSN95BmLQRUPt3gXHJlpbXONQAFQ5MHGf9MV7KpmIrlCxMJW5fgm8 + D66T9p8UyTNKqGWLcff7tqrpxkV0PnOZEg+zP4htlUOIi9J1EFjAiYxeEygw4pPd + yuNzAoGAI0lLE32iIm+j5byFCKRuS6cQmDBzUJxZiCuLsuZEINYdz2nxKZqd9VtA + rOXzo8vJuGLF1hjf1/C66F3EnPxcclPL10vLCE8RbfQYVwBxw7MG9BLJXIlMjb2V + 7CjVDE4Gaa96tChg1pepuJcOEuWez3o3Ard9oZ4Z9sm7VJzmuoY= + -----END RSA PRIVATE KEY----- + - kid: O1hkajPW2v + key: | + -----BEGIN EC PRIVATE KEY----- + MHcCAQEEINsgMBFDIrIzqOIWuR94TCi5MTH1FS8wfgatu9BsO2jVoAoGCCqGSM49 + AwEHoUQDQgAEldEtvZaXtblpUdHpKKQiH7z9ADC55H0yrCYyQsLXbt14lI2NuseX + MWsvSLBzkbEetDxkmKh0bhOfrdwv9x5SwA== + -----END EC PRIVATE KEY----- + - kid: 2nhe3z2925 + key: | + -----BEGIN EC PRIVATE KEY----- + MIGkAgEBBDDTVngHypOwUnPOGXeskQJhdSLLPBCM+mkSvzr2SZ7Kjm3hftvs2s7J + gZBOZwXyoaKgBwYFK4EEACKhZANiAAQ3WGOQs3EqO2x4X7PBWs6Lw3qdmRLHqblc + Zplh3wYPDOoUMvD99Snxz43t5sK6kphLBL262/srx/UPT1McLUxBMlBvBUbBEKHX + a8icrL13yIwflquj0EHrE7czFJw1txs= + -----END EC PRIVATE KEY----- + - kid: 50aRR3QVqx + key: | + -----BEGIN EC PRIVATE KEY----- + MHQCAQEEIN8bErXY1sWEJ1y9KoYcpcUImIjpS/ay3pEugYPfr3Y/oAcGBSuBBAAK + oUQDQgAEMHcshHVFbMSEyyt3ptIdAhnrg+XlQskZ33hZvdtzm6I0wW8H8zslMp+I + t0KYCeIQ7HTPtgJAOsKxEPBfmVXZmA== + -----END EC PRIVATE KEY----- +passwords: + enabled: true + schemes: + - version: 1 + algorithm: bcrypt + secret: "secret01" + - version: 2 + algorithm: argon2id + minimum_complexity: 3 +matrix: + homeserver: tchapgouv.com + #TODO copy from element-docker-demo/data/mas/config.yaml + secret: 'TO BE COPY' + endpoint: https://matrix.tchapgouv.com/ + +clients: + - client_id: 0000000000000000000SYNAPSE + client_auth_method: client_secret_basic + client_secret: '/DjWc4D3yyqgjYN8tum65g' + + # for api admin calls + - client_id: 01J44RKQYM4G3TNVANTMTDYTX6 + client_auth_method: client_secret_basic + client_secret: phoo8ahneir3ohY2eigh4xuu6Oodaewi + + +policy: + data: + admin_clients: + # for api admin calls + - 01J44RKQYM4G3TNVANTMTDYTX6 + client_registration: + allow_insecure_uris: true + allow_host_mismatch: true + +account: + # Whether users are allowed to change their email addresses. + # + # Defaults to `true`. + email_change_allowed: false + + # Whether users are allowed to change their display names + # + # Defaults to `true`. + # This should be in sync with the policy in the homeserver configuration. + displayname_change_allowed: false + + # Whether to enable self-service password registration + # + # Defaults to `false`. + # This has no effect if password login is disabled. + password_registration_enabled: true + + # Whether users are allowed to change their passwords + # + # Defaults to `true`. + # This has no effect if password login is disabled. + password_change_allowed: true + + # Whether email-based password recovery is enabled + # + # Defaults to `false`. + # This has no effect if password login is disabled. + password_recovery_enabled: true + + # Whether users can log in with their email address. + # + # Defaults to `false`. + # This has no effect if password login is disabled. + login_with_email_allowed: true + +templates: + # From where to load the templates + # This is relative to the current working directory, *not* the config file + path: "WILL BE REPLACED BY build_conf.sh" + + # Path to the frontend assets manifest file + # assets_manifest: "/to/manifest.json" + + # # From where to load the translation files + # # Default in Docker distribution: `/usr/local/share/mas-cli/translations/` + # # Default in pre-built binaries: `./share/translations/` + # # Default in locally-built binaries: `./translations/` + translations_path: "WILL BE REPLACED BY build_conf.sh" + +upstream_oauth2: + providers: + - id: "01JK5MR1SD21MAQY4PWMFG283W" + human_name: Proconnect (mock) + issuer: "https://sso.tchapgouv.com/realms/proconnect-mock" + token_endpoint_auth_method: client_secret_basic + client_id: "matrix-authentication-service" + client_secret: "HrJ1NZ0AbkHuWWjyRHh7X2lzn3S8eagt" + scope: "openid profile email" + claims_imports: + localpart: + action: force + #template: "{{ user.preferred_username }}" + on_conflict: add + #action: require + template: "{{ user.email | email_to_mxid_localpart }}" + displayname: + action: require + #template: "{{ user.name }}" + template: "{{ user.email | email_to_display_name }}" + email: + action: require + template: "{{ user.email }}" + set_email_verification: always + +telemetry: + tracing: + # # List of propagators to use for extracting and injecting trace contexts + # propagators: + # # Propagate according to the W3C Trace Context specification + # - tracecontext + # # Propagate according to the W3C Baggage specification + # - baggage + # # Propagate trace context with Jaeger compatible headers + # - jaeger + + # # The default: don't export traces + exporter: none + + # Export traces to an OTLP-compatible endpoint + #exporter: otlp + #endpoint: https://localhost:4318 + metrics: + # The default: don't export metrics + exporter: none + + # Export metrics to an OTLP-compatible endpoint + #exporter: otlp + #endpoint: https://localhost:4317 + + # Export metrics by exposing a Prometheus endpoint + # This requires mounting the `prometheus` resource to an HTTP listener + #exporter: prometheus + + # sentry: + # # DSN to use for sending errors and crashes to Sentry + # dsn: https://public@host:port/1 + +tchap: + identity_server_url: "http://localhost:8083" + email_lookup_fallback_rules: + # match : the new email pattern + # search : the old email pattern + # old email in mail.numerique.gouv.fr + - match_with : '@numerique.gouv.fr' + search: '@beta.gouv.fr' \ No newline at end of file diff --git a/tchap/deprecated.start-local-stack b/tchap/deprecated.start-local-stack new file mode 100755 index 000000000..d1132d330 --- /dev/null +++ b/tchap/deprecated.start-local-stack @@ -0,0 +1,35 @@ + +# deprecated - use https://github.com/tchapgouv/element-docker-demo instead +#!/bin/bash + +#set -e + +# Run mail service +#docker rm -f mailcatcher +#docker run -d --name mailcatcher -p 1080:1080 -p 1025:1025 sj26/mailcatcher +# Run postgres service +#createdb -U postgres postgres +# dropdb postgres +#docker rm -f postgres +#docker run -d --name postgres -p 5432:5432 -v ./tmp/postgres:/var/lib/postgresql/data:rw -e 'POSTGRES_USER=postgres' -e 'POSTGRES_PASSWORD=postgres' -e 'POSTGRES_DATABASE=postgres' -e 'PGDATA=/var/lib/postgresql/data' postgres +# Wait for postgres to be ready +#sleep 3 + +#docker exec -it postgres createdb -U postgres -O postgres keycloak + + +# Run Keycloak service with proconnect-mock realm import +# frontend url : https://sso.tchapgouv.com +#docker rm -f keycloak +#docker run -d --name keycloak -p 8082:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin -v $(pwd)/tchap/keycloak/proconnect-mock-realm.json:/opt/keycloak/data/import/proconnect-mock-realm.json quay.io/keycloak/keycloak:latest start-dev --import-realm --hostname=https://sso.tchapgouv.com + +mkdir tmp + +# Stops containers and removes containers, networks, volumes, and images +docker compose down + +# (re)creates, starts, and attaches to containers in the background and leaves them running. +docker compose up -d + +# View output from containers +docker compose logs -f diff --git a/tchap/docker-compose.yml b/tchap/docker-compose.yml new file mode 100644 index 000000000..7a3c0ef52 --- /dev/null +++ b/tchap/docker-compose.yml @@ -0,0 +1,69 @@ +# only use as a handler for the postgres service +# use https://github.com/tchapgouv/element-docker-demo for the complete stack + +services: + # mailcatcher: + # image: sj26/mailcatcher + # #network_mode: host + # ports: + # - "1025:1025" + # - "1080:1080" + # networks: + # - tchap-network + + # identity-mock: + # image: wiremock/wiremock:2.35.0 + # ports: + # - "8083:8080" + # volumes: + # - ./wiremock:/home/wiremock + # command: + # - --verbose + # - --global-response-templating + # networks: + # - tchap-network + # This service mocks the Matrix Identity Server API + + postgres: + image: postgres + ports: + - "5439:5432" + environment: + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=postgres + - POSTGRES_DB=postgres + - PGDATA=/var/lib/postgresql/data + volumes: + - ./tmp/postgres:/var/lib/postgresql/data:rw + - ./tools/keycloak/init-keycloak-db.sql:/docker-entrypoint-initdb.d/init-keycloak-db.sql + networks: + - tchap-network + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 5s + timeout: 5s + retries: 50 + + # keycloak: + # image: quay.io/keycloak/keycloak:26.2 + # ports: + # - "8082:8080" + # environment: + # - KEYCLOAK_ADMIN=admin + # - KEYCLOAK_ADMIN_PASSWORD=admin + # - KC_DB=postgres + # - KC_DB_URL=jdbc:postgresql://postgres:5432/keycloak + # - KC_DB_USERNAME=postgres + # - KC_DB_PASSWORD=postgres + # volumes: + # - ./tools/keycloak/proconnect-mock-realm.json:/opt/keycloak/data/import/proconnect-mock-realm.json + # command: "start-dev --import-realm --hostname=${KEYCLOAK_HOSTNAME}" + # networks: + # - tchap-network + # depends_on: + # postgres: + # condition: service_healthy + +networks: + tchap-network: + driver: bridge diff --git a/tchap/resources/templates/base.html b/tchap/resources/templates/base.html new file mode 100644 index 000000000..bbfc5bb5f --- /dev/null +++ b/tchap/resources/templates/base.html @@ -0,0 +1,50 @@ +{# +Copyright 2024 New Vector Ltd. +Copyright 2021-2024 The Matrix.org Foundation C.I.C. + +SPDX-License-Identifier: AGPL-3.0-only +Please see LICENSE in the repository root for full details. +-#} + +{% set _ = translator(lang) %} + +{% import "components/button.html" as button %} +{% import "components/field.html" as field %} +{% import "components/back_to_client.html" as back_to_client %} +{% import "components/logout.html" as logout %} +{% import "components/errors.html" as errors %} +{% import "components/icon.html" as icon %} +{% import "components/scope.html" as scope %} +{% import "components/captcha.html" as captcha %} + + + + + + + {% block title %}{{ _("app.name") }}{% endblock title %} + + {{ include_asset('src/shared.css') | indent(4) | safe }} + {{ include_asset('src/templates.css') | indent(4) | safe }} + + + {{ include_asset('tchap/css/tchap.css') | indent(4) | safe }} + {{ include_asset('node_modules/@gouvfr-lasuite/integration/dist/css/homepage-full.css') | indent(4) | safe }} + + + {{ captcha.head() }} + + + + {% include "tchap/header.html" %} + + + + + {% include "tchap/footer.html" %} + + diff --git a/tchap/resources/templates/pages/login.html b/tchap/resources/templates/pages/login.html new file mode 100644 index 000000000..cacc3c578 --- /dev/null +++ b/tchap/resources/templates/pages/login.html @@ -0,0 +1,136 @@ +{# +Copyright 2024 New Vector Ltd. +Copyright 2021-2024 The Matrix.org Foundation C.I.C. + +SPDX-License-Identifier: AGPL-3.0-only +Please see LICENSE in the repository root for full details. +-#} + +{% extends "base.html" %} + +{% from "components/idp_brand.html" import logo %} + +{% block content %} +
+
+ + {% if next and next.kind == "link_upstream" %} +
+

{{ _("mas.login.link.headline") }}

+ {% set name = provider.human_name or (provider.issuer | simplify_url(keep_path=True)) or provider.id %} +

{{ _("mas.login.link.description", provider=name) }}

+
+ {% else %} +
+

{{ _("mas.login.headline") }}

+ +
+ {% endif %} +
+ + + {% set params = next["params"] | default({}) | to_params(prefix="?") %} + {% for provider in providers %} + {% set name = provider.human_name or (provider.issuer | simplify_url(keep_path=True)) or provider.id %} + + +
+ +

+ + Qu’est-ce que ProConnect ? + +

+
+ {% endfor %} + + + + + {{ field.separator() }} + + +
+ {% if form.errors is not empty %} + {% for error in form.errors %} +
+ {{ errors.form_error_message(error=error) }} +
+ {% endfor %} + {% endif %} + + + + {% if features.login_with_email_allowed %} + {% call(f) field.field(label=_("mas.login.username_or_email"), name="username", form_state=form) %} + + {% endcall %} + {% else %} + {% call(f) field.field(label=_("common.username"), name="username", form_state=form) %} + + {% endcall %} + {% endif %} + + {% if features.password_login %} + {% call(f) field.field(label=_("common.password"), name="password", form_state=form) %} + + {% endcall %} + + {% if features.account_recovery %} + {{ button.link_text(text=_("mas.login.forgot_password"), href="/recover", class="self-center") }} + {% endif %} + {% endif %} +
+ +
+ {% if features.password_login %} + {{ button.button(text=_("action.continue")) }} + {% endif %} + + + + +
+ + {% if (not next or next.kind != "link_upstream") and features.password_registration %} +
+

+ {{ _("mas.login.call_to_register") }} +

+ + + {% set params = next["params"] | default({}) | to_params(prefix="?") %} + {{ button.link_text(text=_("action.create_account"), href="/register" ~ params) }} +
+ {% endif %} + + {% if not providers and not features.password_login %} +
+ {{ _("mas.login.no_login_methods") }} +
+ {% endif %} +
+{% endblock content %} diff --git a/tchap/resources/templates/tchap/footer.html b/tchap/resources/templates/tchap/footer.html new file mode 100644 index 000000000..c9448023f --- /dev/null +++ b/tchap/resources/templates/tchap/footer.html @@ -0,0 +1,111 @@ + + diff --git a/tchap/resources/templates/tchap/header.html b/tchap/resources/templates/tchap/header.html new file mode 100644 index 000000000..bfb514add --- /dev/null +++ b/tchap/resources/templates/tchap/header.html @@ -0,0 +1,52 @@ + + diff --git a/tchap/resources/translations/en.json b/tchap/resources/translations/en.json new file mode 100644 index 000000000..96a10ae8f --- /dev/null +++ b/tchap/resources/translations/en.json @@ -0,0 +1,817 @@ +{ + "action": { + "back": "Back", + "@back": { + "context": "pages/recovery/disabled.html:22:32-48" + }, + "cancel": "Cancel", + "@cancel": { + "context": "pages/consent.html:69:11-29, pages/device_consent.html:127:13-31, pages/policy_violation.html:44:13-31" + }, + "continue": "Continue", + "@continue": { + "context": "form_post.html:25:28-48, pages/consent.html:57:28-48, pages/device_consent.html:124:13-33, pages/device_link.html:40:26-46, pages/login.html:110:30-50, pages/reauth.html:32:28-48, pages/recovery/start.html:38:26-46, pages/register/password.html:74:26-46, pages/register/steps/display_name.html:43:28-48, pages/register/steps/registration_token.html:41:28-48, pages/register/steps/verify_email.html:51:26-46, pages/sso.html:37:28-48" + }, + "create_account": "Create Account", + "@create_account": { + "context": "pages/login.html:126:33-59, pages/upstream_oauth2/do_register.html:191:26-52" + }, + "sign_in": "Sign in", + "@sign_in": { + "context": "pages/account/deactivated.html:23:28-47, pages/account/locked.html:23:28-47, pages/index.html:30:26-45" + }, + "sign_out": "Sign out", + "@sign_out": { + "context": "pages/account/logged_out.html:22:28-48, pages/consent.html:65:28-48, pages/device_consent.html:136:30-50, pages/index.html:28:28-48, pages/policy_violation.html:38:28-48, pages/sso.html:45:28-48, pages/upstream_oauth2/link_mismatch.html:24:24-44, pages/upstream_oauth2/suggest_link.html:32:26-46" + }, + "skip": "Skip", + "@skip": { + "context": "pages/register/steps/display_name.html:49:28-44" + }, + "start_over": "Start over", + "@start_over": { + "context": "pages/recovery/consumed.html:22:32-54, pages/recovery/expired.html:30:32-54, pages/register/steps/email_in_use.html:28:32-54" + } + }, + "app": { + "human_name": "Matrix Authentication Service", + "@human_name": { + "context": "pages/index.html:15:29-48", + "description": "Human readable name of the application" + }, + "name": "matrix-authentication-service", + "@name": { + "context": "app.html:17:14-27, base.html:25:31-44", + "description": "Name of the application" + }, + "technical_description": "OpenID Connect discovery document: %(discovery_url)s", + "@technical_description": { + "context": "pages/index.html:17:13-72", + "description": "Introduction text displayed on the home page" + } + }, + "branding": { + "privacy_policy": { + "alt": "Link to the service privacy policy", + "@alt": { + "context": "components/footer.html:13:83-115" + }, + "link": "Privacy Policy", + "@link": { + "context": "components/footer.html:14:14-47" + } + }, + "terms_and_conditions": { + "alt": "Link to the service terms and conditions", + "@alt": { + "context": "components/footer.html:23:80-118" + }, + "link": "Terms & Conditions", + "@link": { + "context": "components/footer.html:24:14-53" + } + } + }, + "common": { + "display_name": "Display Name", + "@display_name": { + "context": "pages/register/steps/display_name.html:34:35-59, pages/upstream_oauth2/do_register.html:146:37-61" + }, + "email_address": "Email address", + "@email_address": { + "context": "pages/recovery/start.html:34:33-58, pages/register/password.html:38:33-58, pages/upstream_oauth2/do_register.html:114:37-62" + }, + "hide": "", + "@hide": {}, + "loading": "Loading…", + "@loading": { + "context": "form_post.html:14:27-46" + }, + "mxid": "Matrix ID", + "@mxid": { + "context": "pages/upstream_oauth2/do_register.html:93:35-51" + }, + "password": "Password", + "@password": { + "context": "pages/login.html:98:37-57, pages/reauth.html:28:35-55, pages/register/password.html:42:33-53" + }, + "password_confirm": "Confirm password", + "@password_confirm": { + "context": "pages/register/password.html:46:33-61" + }, + "show": "", + "@show": {}, + "username": "Username", + "@username": { + "context": "pages/login.html:92:37-57, pages/register/index.html:30:35-55, pages/register/password.html:34:33-53, pages/upstream_oauth2/do_register.html:101:35-55, pages/upstream_oauth2/do_register.html:106:39-59" + } + }, + "error": { + "unexpected": "Unexpected error", + "@unexpected": { + "context": "pages/error.html:22:29-50", + "description": "Error message displayed when an unexpected error occurs" + } + }, + "mas": { + "account": { + "deactivated": { + "description": "This account (%(mxid)s) has been deleted. If this is not expected, contact your server administrator.", + "@description": { + "context": "pages/account/deactivated.html:20:27-78" + }, + "heading": "Account deleted", + "@heading": { + "context": "pages/account/deactivated.html:18:29-65" + } + }, + "locked": { + "description": "This account (%(mxid)s) has been locked. If this is not expected, contact your server administrator.", + "@description": { + "context": "pages/account/locked.html:20:27-73" + }, + "heading": "Account locked", + "@heading": { + "context": "pages/account/locked.html:18:29-60" + } + }, + "logged_out": { + "description": "This session has been terminated. Sign out to be able to log back in", + "@description": { + "context": "pages/account/logged_out.html:19:27-66" + }, + "heading": "Session terminated", + "@heading": { + "context": "pages/account/logged_out.html:18:29-64" + } + } + }, + "back_to_homepage": "Go back to the homepage", + "@back_to_homepage": { + "context": "pages/404.html:16:29-54" + }, + "captcha": { + "noscript": "This form is protected by a CAPTCHA and requires JavaScript to be enabled to submit it. Please enable JavaScript in your browser and reload this page.", + "@noscript": { + "context": "components/captcha.html:13:11-36" + } + }, + "change_password": { + "change": "Change password", + "@change": { + "description": "Button to change the user's password" + }, + "confirm": "Confirm password", + "@confirm": { + "description": "Confirmation field for the new password" + }, + "current": "Current password", + "@current": { + "description": "Field for the user's current password" + }, + "description": "This will change the password on your account.", + "@description": {}, + "heading": "Change my password", + "@heading": { + "description": "Heading on the change password page" + }, + "new": "New password", + "@new": { + "description": "Field for the user's new password" + } + }, + "choose_display_name": { + "description": "This is the name other people will see. You can change this at any time.", + "@description": { + "context": "pages/register/steps/display_name.html:17:25-65", + "description": "During the registration flow, the user is asked to choose a display name. This is the description of that form." + }, + "headline": "Choose your display name", + "@headline": { + "context": "pages/register/steps/display_name.html:16:27-64", + "description": "During the registration flow, the user is asked to choose a display name. This is the headline of that form." + } + }, + "consent": { + "client_wants_access": "%(client_name)s at %(redirect_uri)s wants to access your account.", + "@client_wants_access": { + "context": "pages/consent.html:27:11-122" + }, + "heading": "Allow access to your account?", + "@heading": { + "context": "pages/consent.html:25:27-51, pages/device_consent.html:28:29-53" + }, + "make_sure_you_trust": "Make sure that you trust %(client_name)s.", + "@make_sure_you_trust": { + "context": "pages/consent.html:38:81-142, pages/device_consent.html:104:83-144" + }, + "this_will_allow": "This will allow %(client_name)s to:", + "@this_will_allow": { + "context": "pages/consent.html:28:11-68, pages/device_consent.html:94:13-70" + }, + "you_may_be_sharing": "You may be sharing sensitive information with this site or app.", + "@you_may_be_sharing": { + "context": "pages/consent.html:39:7-42, pages/device_consent.html:105:9-44" + } + }, + "device_card": { + "access_requested": "Access requested", + "@access_requested": { + "context": "pages/device_consent.html:82:34-71" + }, + "device_code": "Code", + "@device_code": { + "context": "pages/device_consent.html:86:34-66" + }, + "generic_device": "Device", + "@generic_device": { + "context": "pages/device_consent.html:70:22-57" + }, + "ip_address": "IP address", + "@ip_address": { + "context": "pages/device_consent.html:77:36-67" + } + }, + "device_code_link": { + "description": "Link a device", + "@description": { + "context": "pages/device_link.html:19:25-62" + }, + "headline": "Enter the code displayed on your device", + "@headline": { + "context": "pages/device_link.html:18:27-61" + } + }, + "device_consent": { + "another_device_access": "Another device wants to access your account.", + "@another_device_access": { + "context": "pages/device_consent.html:93:13-58" + }, + "denied": { + "description": "You denied access to %(client_name)s. You can close this window.", + "@description": { + "context": "pages/device_consent.html:147:27-94" + }, + "heading": "Access denied", + "@heading": { + "context": "pages/device_consent.html:146:29-67" + } + }, + "granted": { + "description": "You granted access to %(client_name)s. You can close this window.", + "@description": { + "context": "pages/device_consent.html:158:27-95" + }, + "heading": "Access granted", + "@heading": { + "context": "pages/device_consent.html:157:29-68" + } + } + }, + "device_display_name": { + "client_on_device": "%(client_name)s on %(device_name)s", + "@client_on_device": { + "context": "device_name.txt:28:4-99", + "description": "The automatic device name generated for a client, e.g. 'Element on iPhone'" + }, + "name_for_platform": "%(name)s for %(platform)s", + "@name_for_platform": { + "context": "device_name.txt:19:10-102", + "description": "Part of the automatic device name for the platfom, e.g. 'Safari for macOS'" + }, + "unknown_device": "Unknown device", + "@unknown_device": { + "context": "device_name.txt:24:8-51" + } + }, + "email_in_use": { + "description": "If you have forgotten your account credentials, you can recover your account. You can also start over and use a different email address.", + "@description": { + "context": "pages/register/steps/email_in_use.html:22:13-46" + }, + "title": "The email address %(email)s is already in use", + "@title": { + "context": "pages/register/steps/email_in_use.html:19:13-53" + } + }, + "emails": { + "greeting": "Hello %(username)s,", + "@greeting": { + "context": "emails/verification.html:17:3-64, emails/verification.txt:17:3-64", + "description": "Greeting at the top of emails sent to the user" + }, + "recovery": { + "click_button": "Click on the button below to create a new password:", + "@click_button": { + "context": "emails/recovery.html:28:7-44" + }, + "copy_link": "Copy the following link and paste it into a browser to create a new password:", + "@copy_link": { + "context": "emails/recovery.html:45:49-83, emails/recovery.txt:12:3-37" + }, + "create_new_password": "Create new password", + "@create_new_password": { + "context": "emails/recovery.html:43:9-53" + }, + "fallback": "The button doesn't work for you?", + "@fallback": { + "context": "emails/recovery.html:45:9-42" + }, + "headline": "You requested a password reset for your %(server_name)s account.", + "@headline": { + "context": "emails/recovery.html:26:7-74, emails/recovery.txt:10:3-70" + }, + "subject": "Reset your account password (%(mxid)s)", + "@subject": { + "context": "emails/recovery.subject:14:3-46" + }, + "you_can_ignore": "If you didn't ask for a new password, you can ignore this email. Your current password will continue to work.", + "@you_can_ignore": { + "context": "emails/recovery.html:50:7-46, emails/recovery.txt:16:3-42" + } + }, + "verify": { + "body_html": "Your verification code to confirm this email address is: %(code)s", + "@body_html": { + "context": "emails/verification.html:19:3-66", + "description": "The body of the email sent to verify an email address (HTML)" + }, + "body_text": "Your verification code to confirm this email address is: %(code)s", + "@body_text": { + "context": "emails/verification.txt:19:3-66", + "description": "The body of the email sent to verify an email address (text)" + }, + "subject": "Your email verification code is: %(code)s", + "@subject": { + "context": "emails/verification.subject:11:3-64", + "description": "The subject line of the email sent to verify an email address" + } + } + }, + "errors": { + "captcha": "CAPTCHA verification failed, please try again", + "@captcha": { + "context": "components/errors.html:19:7-30" + }, + "denied_policy": "Denied by policy: %(policy)s", + "@denied_policy": { + "context": "components/errors.html:17:7-58, components/field.html:85:19-70" + }, + "email_banned": "Email is banned by the server policy", + "@email_banned": { + "context": "components/field.html:83:19-47" + }, + "email_domain_banned": "Email domain is banned by the server policy", + "@email_domain_banned": { + "context": "components/field.html:79:19-54" + }, + "email_domain_not_allowed": "Email domain is not allowed by the server policy", + "@email_domain_not_allowed": { + "context": "components/field.html:77:19-59" + }, + "email_not_allowed": "Email is not allowed by the server policy", + "@email_not_allowed": { + "context": "components/field.html:81:19-52" + }, + "field_required": "This field is required", + "@field_required": { + "context": "components/field.html:60:17-47" + }, + "invalid_credentials": "Invalid credentials", + "@invalid_credentials": { + "context": "components/errors.html:11:7-42" + }, + "password_mismatch": "Password fields don't match", + "@password_mismatch": { + "context": "components/errors.html:13:7-40, components/field.html:88:17-50" + }, + "rate_limit_exceeded": "You've made too many requests in a short period. Please wait a few minutes and try again.", + "@rate_limit_exceeded": { + "context": "components/errors.html:15:7-42, pages/recovery/progress.html:26:11-46" + }, + "username_all_numeric": "Username cannot consist solely of numbers", + "@username_all_numeric": { + "context": "components/field.html:71:19-55" + }, + "username_banned": "Username is banned by the server policy", + "@username_banned": { + "context": "components/field.html:73:19-50", + "description": "Error message shown on registration, when the username matches a pattern that is banned by the server policy." + }, + "username_invalid_chars": "Username contains invalid characters. Use lowercase letters, numbers, dashes and underscores only.", + "@username_invalid_chars": { + "context": "components/field.html:69:19-57" + }, + "username_not_allowed": "Username is not allowed by the server policy", + "@username_not_allowed": { + "context": "components/field.html:75:19-55", + "description": "Error message shown on registration, when the username *does not match* any of the patterns that are allowed by the server policy." + }, + "username_taken": "This username is already taken", + "@username_taken": { + "context": "components/field.html:62:17-47" + }, + "username_too_long": "Username is too long", + "@username_too_long": { + "context": "components/field.html:67:19-52" + }, + "username_too_short": "Username is too short", + "@username_too_short": { + "context": "components/field.html:65:19-53" + } + }, + "login": { + "call_to_register": "Don't have an account yet?", + "@call_to_register": { + "context": "pages/login.html:121:13-44" + }, + "continue_with_provider": "Continue with %(provider)s", + "@continue_with_provider": { + "context": "pages/login.html:66:11-63, pages/register/index.html:53:15-67", + "description": "Button to log in with an upstream provider" + }, + "description": "Please sign in to continue:", + "@description": { + "context": "pages/login.html:30:34-60" + }, + "forgot_password": "Forgot password?", + "@forgot_password": { + "context": "pages/login.html:103:35-65", + "description": "On the login page, link to the account recovery process" + }, + "headline": "Sign in", + "@headline": { + "context": "pages/login.html:29:31-54" + }, + "link": { + "description": "Linking your %(provider)s account", + "@description": { + "context": "pages/login.html:25:29-75" + }, + "headline": "Sign in to link", + "@headline": { + "context": "pages/login.html:23:31-59" + } + }, + "no_login_methods": "No login methods available.", + "@no_login_methods": { + "context": "pages/login.html:132:11-42" + }, + "username_or_email": "Username or Email", + "@username_or_email": { + "context": "pages/login.html:88:37-69" + } + }, + "navbar": { + "my_account": "My account", + "@my_account": { + "context": "pages/index.html:27:26-52" + }, + "register": "Create an account", + "@register": { + "context": "pages/index.html:33:36-60" + }, + "signed_in_as": "Signed in as %(username)s.", + "@signed_in_as": { + "context": "pages/index.html:24:11-79", + "description": "Displayed in the navbar when the user is signed in" + } + }, + "not_found": { + "description": "The page you were looking for doesn't exist or has been moved", + "@description": { + "context": "pages/404.html:14:8-38" + }, + "heading": "Page not found", + "@heading": { + "context": "pages/404.html:13:39-65" + } + }, + "not_you": "Not %(username)s?", + "@not_you": { + "context": "pages/consent.html:62:11-67, pages/device_consent.html:133:13-69, pages/sso.html:42:11-67", + "description": "Suggestions for the user to log in as a different user" + }, + "or_separator": "Or", + "@or_separator": { + "context": "components/field.html:107:10-31", + "description": "Separator between the login methods" + }, + "policy_violation": { + "description": "This might be because of the client which authored the request, the currently logged in user, or the request itself.", + "@description": { + "context": "pages/policy_violation.html:19:25-62", + "description": "Displayed when an authorization request is denied by the policy" + }, + "heading": "The authorization request was denied the policy enforced by this service", + "@heading": { + "context": "pages/policy_violation.html:18:27-60", + "description": "Displayed when an authorization request is denied by the policy" + }, + "logged_as": "Logged as %(username)s", + "@logged_as": { + "context": "pages/policy_violation.html:35:11-86" + } + }, + "recovery": { + "consumed": { + "description": "To create a new password, start over and select “Forgot password”.", + "@description": { + "context": "pages/recovery/consumed.html:19:25-63", + "description": "Description on the error page shown when a user tries to use a recovery link that has already been used" + }, + "heading": "The link to reset your password has already been used", + "@heading": { + "context": "pages/recovery/consumed.html:18:27-61", + "description": "Title on the error page shown when a user tries to use a recovery link that has already been used" + } + }, + "disabled": { + "description": "If you have lost your credentials, please contact the administrator to recover your account.", + "@description": { + "context": "pages/recovery/disabled.html:19:25-63" + }, + "heading": "Account recovery is disabled", + "@heading": { + "context": "pages/recovery/disabled.html:18:27-61" + } + }, + "expired": { + "description": "Request a new email that will be sent to: %(email)s.", + "@description": { + "context": "pages/recovery/expired.html:19:46-104", + "description": "Description on the page shown when a user tries to use an expired recovery link" + }, + "heading": "The link to reset your password has expired", + "@heading": { + "context": "pages/recovery/expired.html:18:27-60", + "description": "Title on the page shown when a user tries to use an expired recovery link" + }, + "resend_email": "Resend email", + "@resend_email": { + "context": "pages/recovery/expired.html:27:28-66" + } + }, + "finish": { + "confirm": "Enter new password again", + "@confirm": { + "context": "pages/recovery/finish.html:41:33-65", + "description": "Label for the password confirmation field" + }, + "description": "Choose a new password for your account.", + "@description": { + "context": "pages/recovery/finish.html:19:25-61", + "description": "Description for the final password recovery page" + }, + "heading": "Reset your password", + "@heading": { + "context": "pages/recovery/finish.html:18:27-59", + "description": "Heading for the final password recovery page" + }, + "new": "New password", + "@new": { + "context": "pages/recovery/finish.html:37:33-61", + "description": "Label for the new password field" + }, + "save_and_continue": "Save and continue", + "@save_and_continue": { + "context": "pages/recovery/finish.html:45:26-68", + "description": "Button to save the new password and continue" + } + }, + "progress": { + "change_email": "Try a different email", + "@change_email": { + "context": "pages/recovery/progress.html:35:33-72", + "description": "Button to change the email address for the password recovery link" + }, + "description": "We sent an email with a link to reset your password if there's an account using %(email)s.", + "@description": { + "context": "pages/recovery/progress.html:19:46-105", + "description": "The description of the password recovery page, informing the user that an email has been sent to reset their password" + }, + "heading": "Check your email", + "@heading": { + "context": "pages/recovery/progress.html:18:27-61", + "description": "The title of the password recovery page, informing the user that an email has been sent to reset their password" + }, + "resend_email": "Resend email", + "@resend_email": { + "context": "pages/recovery/progress.html:32:36-75", + "description": "Button to resend the email with the password recovery link" + } + }, + "start": { + "description": "An email will be sent with a link to reset your password.", + "@description": { + "context": "pages/recovery/start.html:19:25-60", + "description": "The description of the page to initiate an account recovery" + }, + "heading": "Enter your email to continue", + "@heading": { + "context": "pages/recovery/start.html:18:27-58", + "description": "The title of the page to initiate an account recovery" + } + } + }, + "register": { + "call_to_login": "Already have an account?", + "@call_to_login": { + "context": "pages/register/index.html:59:35-66, pages/register/password.html:77:33-64", + "description": "Displayed on the registration page to suggest to log in instead" + }, + "continue_with_email": "Continue with email address", + "@continue_with_email": { + "context": "pages/register/index.html:44:30-67" + }, + "create_account": { + "description": "Choose a username to continue.", + "@description": { + "context": "pages/register/index.html:24:29-73" + }, + "heading": "Create an account", + "@heading": { + "context": "pages/register/index.html:21:29-69, pages/register/password.html:18:27-67" + } + }, + "terms_of_service": "I agree to the Terms and Conditions", + "@terms_of_service": { + "context": "pages/register/password.html:51:35-95, pages/upstream_oauth2/do_register.html:179:35-95" + } + }, + "registration_token": { + "description": "", + "@description": { + "context": "pages/register/steps/registration_token.html:17:25-64" + }, + "field": "", + "@field": { + "context": "pages/register/steps/registration_token.html:33:35-68" + }, + "headline": "", + "@headline": { + "context": "pages/register/steps/registration_token.html:16:27-63" + } + }, + "scope": { + "edit_profile": "Edit your profile and contact details", + "@edit_profile": { + "context": "components/scope.html:15:35-62", + "description": "Displayed when the 'urn:mas:graphql:*' scope is requested" + }, + "manage_sessions": "Manage your devices and sessions", + "@manage_sessions": { + "context": "components/scope.html:16:39-69", + "description": "Displayed when the 'urn:mas:graphql:*' scope is requested" + }, + "mas_admin": "Administer any user on the matrix-authentication-service", + "@mas_admin": { + "context": "components/scope.html:23:42-66", + "description": "Displayed when the 'urn:mas:admin' scope is requested" + }, + "send_messages": "Send new messages on your behalf", + "@send_messages": { + "context": "components/scope.html:19:35-63" + }, + "synapse_admin": "Administer the Synapse homeserver", + "@synapse_admin": { + "context": "components/scope.html:21:42-70", + "description": "Displayed when the 'urn:synapse:admin:*' scope is requested" + }, + "view_messages": "View your existing messages and data", + "@view_messages": { + "context": "components/scope.html:18:35-63", + "description": "Displayed when the 'urn:matrix:client:api:*' scope is requested" + }, + "view_profile": "See your profile info and contact details", + "@view_profile": { + "context": "components/scope.html:13:43-70", + "description": "Displayed when the 'openid' scope is requested" + } + }, + "upstream_oauth2": { + "link_mismatch": { + "heading": "This upstream account is already linked to another account.", + "@heading": { + "context": "pages/upstream_oauth2/link_mismatch.html:19:11-57", + "description": "Page shown when the user tries to link an upstream account that is already linked to another account" + } + }, + "login_link": { + "action": "Link", + "@action": { + "context": "pages/upstream_oauth2/login_link.html:27:28-70" + }, + "description": "An account exists for the username : %(username)s", + "@description": { + "context": "pages/upstream_oauth2/login_link.html:21:7-85" + }, + "heading": "Link to your existing account", + "@heading": { + "context": "pages/upstream_oauth2/login_link.html:17:27-70" + } + }, + "register": { + "choose_username": { + "description": "This cannot be changed later.", + "@description": { + "context": "pages/upstream_oauth2/do_register.html:52:13-74" + }, + "heading": "Choose your username", + "@heading": { + "context": "pages/upstream_oauth2/do_register.html:49:13-70", + "description": "Displayed when creating a new account from an SSO login, and the username is not forced" + } + }, + "create_account": "Create a new account", + "@create_account": { + "description": "Displayed when creating a new account from an SSO login, and the username is pre-filled and forced" + }, + "enforced_by_policy": "Enforced by server policy", + "@enforced_by_policy": { + "context": "pages/upstream_oauth2/do_register.html:97:14-66" + }, + "forced_display_name": "Will use the following display name", + "@forced_display_name": { + "description": "Tells the user what display name will be imported" + }, + "forced_email": "Will use the following email address", + "@forced_email": { + "description": "Tells the user which email address will be imported" + }, + "forced_localpart": "Will use the following username", + "@forced_localpart": { + "description": "Tells the user which username will be used" + }, + "import_data": { + "description": "Confirm the information that will be linked to your new %(server_name)s account.", + "@description": { + "context": "pages/upstream_oauth2/do_register.html:25:13-104" + }, + "heading": "Import your data", + "@heading": { + "context": "pages/upstream_oauth2/do_register.html:22:13-66" + } + }, + "imported_from_upstream": "Imported from your upstream account", + "@imported_from_upstream": { + "context": "pages/upstream_oauth2/do_register.html:121:18-74, pages/upstream_oauth2/do_register.html:153:18-74" + }, + "imported_from_upstream_with_name": "Imported from your %(human_name)s account", + "@imported_from_upstream_with_name": { + "context": "pages/upstream_oauth2/do_register.html:119:18-131, pages/upstream_oauth2/do_register.html:151:18-131" + }, + "link_existing": "Link to an existing account", + "@link_existing": { + "description": "Button to link an existing account after an SSO login" + }, + "provider_name": "%(human_name)s account", + "@provider_name": { + "context": "pages/upstream_oauth2/do_register.html:68:14-108" + }, + "signup_with_upstream": { + "heading": "Continue signing up with your %(human_name)s account", + "@heading": { + "context": "pages/upstream_oauth2/do_register.html:37:13-122" + } + }, + "suggested_display_name": "Import display name", + "@suggested_display_name": { + "description": "Option to let the user import their display name after an SSO login" + }, + "suggested_email": "Import email address", + "@suggested_email": { + "description": "Option to let the user import their email address after an SSO login" + }, + "use": "Use", + "@use": { + "context": "pages/upstream_oauth2/do_register.html:137:18-55, pages/upstream_oauth2/do_register.html:170:20-57" + } + }, + "suggest_link": { + "action": "Link", + "@action": { + "context": "pages/upstream_oauth2/suggest_link.html:27:28-72" + }, + "heading": "Link to your existing account", + "@heading": { + "context": "pages/upstream_oauth2/suggest_link.html:18:27-72" + } + } + }, + "verify_email": { + "6_digit_code": "6-digit code", + "@6_digit_code": { + "context": "pages/register/steps/verify_email.html:33:33-67" + }, + "description": "Enter the 6-digit code sent to: %(email)s", + "@description": { + "context": "pages/register/steps/verify_email.html:18:25-86" + }, + "headline": "Verify your email", + "@headline": { + "context": "pages/register/steps/verify_email.html:17:27-57" + } + } + } +} \ No newline at end of file diff --git a/tchap/resources/translations/fr.json b/tchap/resources/translations/fr.json new file mode 100644 index 000000000..7f02a2eaf --- /dev/null +++ b/tchap/resources/translations/fr.json @@ -0,0 +1,275 @@ +{ + "action": { + "back": "Retour", + "cancel": "Annuler", + "continue": "Continuer", + "create_account": "Créer un compte", + "sign_in": "Se connecter", + "sign_out": "Se déconnecter", + "skip": "Passer", + "start_over": "Recommencer", + "submit": "Soumettre" + }, + "app": { + "human_name": "Matrix Authentication Service", + "name": "matrix-authentication-service", + "technical_description": "Document de découverte OpenID Connect : %(discovery_url)s" + }, + "branding": { + "privacy_policy": { + "alt": "Lien vers la politique de confidentialité", + "link": "Politique de confidentialité" + }, + "terms_and_conditions": { + "alt": "Lien vers les conditions d'utilisation", + "link": "Conditions d'utilisation" + } + }, + "common": { + "display_name": "Pseudonyme", + "email_address": "Adresse mail", + "loading": "Chargement…", + "mxid": "Matrix ID", + "password": "Mot de passe", + "password_confirm": "Confirmer le mot de passe", + "username": "Nom d’utilisateur" + }, + "error": { + "unexpected": "Erreur inattendue" + }, + "mas": { + "account": { + "deactivated": { + "description": "Ce compte (%(mxid)s) a été supprimé. Si vous ne vous attendez pas à cela, contactez l'administrateur de votre serveur.", + "heading": "Compte supprimé" + }, + "locked": { + "description": "Ce compte (%(mxid)s) a été verrouillé. Si vous ne vous attendez pas à cela, contactez l'administrateur de votre serveur.", + "heading": "Compte bloqué" + }, + "logged_out": { + "description": "Cette session est terminée. Déconnectez-vous pour pouvoir vous reconnecter", + "heading": "Session terminée" + } + }, + "add_email": { + "description": "Entrez une adresse mail qui sera utilisée pour récupérer votre compte au cas où vous perdriez l'accès à celui-ci.", + "heading": "Ajouter une adresse mail" + }, + "back_to_homepage": "Retourner sur la page d'accueil", + "captcha": { + "noscript": "Ce formulaire est protégé par un CAPTCHA et nécessite l'activation de JavaScript pour le soumettre. Veuillez activer JavaScript dans votre navigateur et recharger cette page." + }, + "change_password": { + "change": "Changer de mot de passe", + "confirm": "Confirmer le mot de passe", + "current": "Mot de passe actuel", + "description": "Cela modifiera le mot de passe de votre compte.", + "heading": "Modifier mon mot de passe", + "new": "Nouveau mot de passe" + }, + "choose_display_name": { + "description": "C'est le nom que les autres personnes verront. Vous pouvez le modifier à tout moment.", + "headline": "Choisissez votre pseudonyme" + }, + "consent": { + "client_wants_access": "%(client_name)s à l'adresse %(redirect_uri)s souhaite accéder à votre compte.", + "heading": "Autoriser l'accès à votre compte ?", + "make_sure_you_trust": "Assurez-vous de faire confiance %(client_name)s.", + "this_will_allow": "Cela va permettre à %(client_name)s de :", + "you_may_be_sharing": "Vous partagez peut-être des informations sensibles avec ce site ou cette application." + }, + "device_card": { + "access_requested": "Accès demandé", + "device_code": "Code", + "generic_device": "Appareil", + "ip_address": "Adresse IP" + }, + "device_code_link": { + "description": "Associer un appareil", + "headline": "Entrez le code affiché sur votre appareil" + }, + "device_consent": { + "another_device_access": "Un autre appareil souhaite accéder à votre compte.", + "denied": { + "description": "Vous avez refusé l'accès à %(client_name)s. Vous pouvez fermer cette fenêtre.", + "heading": "Accès refusé" + }, + "granted": { + "description": "Vous avez accordé l'accès à %(client_name)s. Vous pouvez fermer cette fenêtre.", + "heading": "Accès accordé" + } + }, + "device_display_name": { + "client_on_device": "%(client_name)s sur %(device_name)s", + "name_for_platform": "%(name)s pour %(platform)s", + "unknown_device": "Appareil inconnu" + }, + "email_in_use": { + "description": "Si vous avez oublié vos identifiants de compte, vous pouvez récupérer votre compte. Vous pouvez également recommencer l'enregistrement et utiliser une adresse mail différente.", + "title": "L’adresse mail %(email)s est déjà utilisée" + }, + "emails": { + "greeting": "Bonjour %(username)s,", + "recovery": { + "click_button": "Cliquez sur le bouton ci-dessous pour créer un nouveau mot de passe :", + "copy_link": "Copiez le lien suivant et collez-le dans un navigateur pour créer un nouveau mot de passe :", + "create_new_password": "Créer un nouveau mot de passe", + "fallback": "Le bouton ne fonctionne pas pour vous ?", + "headline": "Vous avez demandé la réinitialisation du mot de passe de votre compte %(server_name)s.", + "subject": "Réinitialisez le mot de passe de votre compte (%(mxid)s)", + "you_can_ignore": "Si vous n’avez pas demandé à réinitialisation votre mot de passe, vous pouvez ignorer cet e-mail. Votre mot de passe actuel continuera de fonctionner." + }, + "verify": { + "body_html": "Votre code de vérification pour confirmer cette adresse mail est : %(code)s", + "body_text": "Votre code de vérification pour confirmer cette adresse mail est : %(code)s", + "subject": "Votre code de vérification est : %(code)s" + } + }, + "errors": { + "captcha": "La vérification du CAPTCHA a échoué, veuillez réessayer", + "denied_policy": "Refusé par la politique du serveur : %(policy)s", + "email_banned": "Cette adresse mail est interdite par la politique du serveur", + "email_domain_banned": "Le domaine de l'adresse mail est interdit par la politique du serveur.", + "email_domain_not_allowed": "Le domaine de l'adresse mail n'est pas autorisée par la politique du serveur.", + "email_not_allowed": "Cette adresse mail n'est pas autorisée par la politique du serveur", + "field_required": "Ce champ est requis", + "invalid_credentials": "Identifiants invalides", + "password_mismatch": "Les champs du mot de passe ne correspondent pas.", + "rate_limit_exceeded": "Vous avez effectué trop de requêtes sur une courte période. Veuillez patienter quelques minutes et réessayer.", + "username_all_numeric": "Le nom d'utilisateur ne peut pas être composé uniquement de chiffres", + "username_banned": "Ce nom d'utilisateur est interdit par la politique du serveur", + "username_invalid_chars": "Le nom d'utilisateur contient des caractères non valides. Utilisez uniquement des lettres minuscules, des chiffres, des tirets et des traits de soulignement.", + "username_not_allowed": "Ce nom d'utilisateur n'est pas autorisé par la politique du serveur", + "username_taken": "Ce nom d'utilisateur est déjà utilisé", + "username_too_long": "Le nom d'utilisateur est trop long", + "username_too_short": "Le nom d'utilisateur est trop court" + }, + "login": { + "call_to_register": "Vous n’avez pas encore de compte ?", + "continue_with_provider": "Poursuivre avec %(provider)s", + "description": "Veuillez vous connecter pour continuer :", + "forgot_password": "Mot de passe oublié ?", + "headline": "Se connecter", + "link": { + "description": "Associer votre compte %(provider)s", + "headline": "Se connecter pour associer" + }, + "no_login_methods": "Aucune méthode de connexion n'est disponible.", + "separator": "Ou", + "username_or_email": "Adresse mail professionnelle" + }, + "navbar": { + "my_account": "Mon compte", + "register": "Créer un compte", + "signed_in_as": "Connecté en tant que %(username)s" + }, + "not_found": { + "description": "La page que vous recherchez n’existe pas ou a été déplacée", + "heading": "Page introuvable" + }, + "not_you": "Vous n'êtes pas %(username)s ?", + "or_separator": "Ou", + "policy_violation": { + "description": "Cela peut être dû à l'application auteur de la demande, à l'utilisateur actuellement connecté ou à la demande elle-même.", + "heading": "La demande d'autorisation a été refusée, conformément à la politique appliquée par ce service.", + "logged_as": "Connecté en tant que %(username)s" + }, + "recovery": { + "consumed": { + "description": "Pour créer un nouveau mot de passe, recommencez et sélectionnez « Mot de passe oublié ».", + "heading": "Le lien pour réinitialiser votre mot de passe a déjà été utilisé" + }, + "disabled": { + "description": "Si vous avez perdu vos identifiants, veuillez contacter l'administrateur pour récupérer votre compte.", + "heading": "La récupération de compte est désactivée" + }, + "expired": { + "description": "Demander un nouvel e-mail qui sera envoyé à : %(email)s.", + "heading": "Le lien pour réinitialiser votre mot de passe a expiré", + "resend_email": "Renvoyer l’e-mail" + }, + "finish": { + "confirm": "Entrez de nouveau votre nouveau mot de passe", + "description": "Choisissez un nouveau mot de passe pour votre compte.", + "heading": "Réinitialiser votre mot de passe", + "new": "Nouveau mot de passe", + "save_and_continue": "Sauvegarder et continuer" + }, + "progress": { + "change_email": "Essayez une autre adresse mail", + "description": "Nous avons envoyé un e-mail contenant un lien pour réinitialiser votre mot de passe si un compte utilise %(email)s.", + "heading": "Vérifiez vos e-mails", + "resend_email": "Renvoyer l’e-mail" + }, + "start": { + "description": "", + "heading": "Entrez votre adresse mail pour réinitialiser votre mot de passe" + } + }, + "register": { + "call_to_login": "Vous avez déjà un compte ?", + "continue_with_email": "Continuer avec une adresse mail", + "create_account": { + "description": "Choisissez un nom d'utilisateur pour continuer.", + "heading": "Créer un compte" + }, + "sign_in_instead": "Se connecter", + "terms_of_service": "J'accepte les Conditions d'utilisation" + }, + "scope": { + "edit_profile": "Modifier votre profil et vos coordonnées", + "manage_sessions": "Gérer vos appareils et vos sessions", + "mas_admin": "Administrer n'importe quel utilisateur dans matrix-authentication-service", + "send_messages": "Envoyez de nouveaux messages en votre nom", + "synapse_admin": "Administrer le serveur d’accueil Synapse", + "view_messages": "Afficher vos messages et données existants", + "view_profile": "Voir les informations de votre profil et vos coordonnées" + }, + "upstream_oauth2": { + "link_mismatch": { + "heading": "Ce compte est déjà associé à un autre compte." + }, + "register": { + "choose_username": { + "description": "Cela ne peut pas être modifié ultérieurement.", + "heading": "Choisissez votre nom d'utilisateur" + }, + "create_account": "Créer un nouveau compte", + "enforced_by_policy": "Importé selon la politique du serveur", + "forced_display_name": "Utilisera le pseudonyme suivant", + "forced_email": "Utilisera l’adresse mail suivante", + "forced_localpart": "Utilisera le nom d'utilisateur suivant", + "import_data": { + "description": "Confirmez les informations qui seront associées à votre nouveau compte %(server_name)s.", + "heading": "Importez vos données" + }, + "imported_from_upstream": "Importé de votre compte en amont", + "imported_from_upstream_with_name": "Importé depuis votre compte %(human_name)s", + "link_existing": "Associer un compte existant", + "provider_name": "Compte %(human_name)s", + "signup_with_upstream": { + "heading": "Continuez à vous inscrire avec votre compte %(human_name)s" + }, + "suggested_display_name": "Importer le pseudonyme", + "suggested_email": "Importer l’adresse mail", + "use": "Importer" + }, + "suggest_link": { + "action": "Associer", + "heading": "Associer votre compte existant" + }, + "login_link": { + "action": "Continuer", + "heading": "Associer votre compte existant", + "description": "Un compte existe pour ce nom d'utilisateur: %(username)s, il sera associé à votre compte Proconnect" + } + }, + "verify_email": { + "6_digit_code": "Code à 6 chiffres", + "code": "Code", + "description": "Veuillez saisir le code à 6 chiffres envoyé à : %(email)s", + "headline": "Vérifiez votre adresse mail" + } + } +} \ No newline at end of file diff --git a/tchap/start-with-hot-reload.sh b/tchap/start-with-hot-reload.sh new file mode 100644 index 000000000..6e6ebd082 --- /dev/null +++ b/tchap/start-with-hot-reload.sh @@ -0,0 +1,66 @@ +#!/bin/bash + +# start the MAS with a hot reload when a template is modified + +set -e + +# Check if fswatch is installed +if ! command -v fswatch &> /dev/null; then + echo "fswatch is not installed. Please install it first:" + echo "brew install fswatch" + exit 1 +fi + +# Get the directory where this script is located +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Set MAS_HOME to the parent directory (project root) +export MAS_HOME="$(dirname "$SCRIPT_DIR")" +export MAS_TCHAP_HOME=$SCRIPT_DIR +export TEMPLATE_WATCH="$MAS_HOME/tchap/resources/templates" +echo "Template watching on : " + $TEMPLATE_WATCH + +# Function to send SIGHUP to the server process +send_sighup() { + # Find the server process + SERVER_PID=$(pgrep -f "mas-cli server -c") + if [ -n "$SERVER_PID" ]; then + echo "Sending SIGHUP to server process $SERVER_PID" + kill -HUP $SERVER_PID + else + echo "Server process not found" + fi +} + +# Function to watch for template changes +watch_templates() { + echo "Watching for changes in $TEMPLATE_WATCH..." + + # Use fswatch to monitor the templates directory + fswatch -o "$TEMPLATE_WATCH" | while read; do + echo "Template change detected..." + $MAS_TCHAP_HOME/build_conf.sh + send_sighup + done +} + +# Start watching for changes in the background +watch_templates & + +# Store the watcher's PID +WATCHER_PID=$! + +# Function to clean up on exit +cleanup() { + echo "Stopping template watcher..." + kill $WATCHER_PID 2>/dev/null + exit 0 +} + +# Set up trap for cleanup +trap cleanup EXIT INT TERM + +#Start the server +cd "$MAS_TCHAP_HOME" + +./start.sh diff --git a/tchap/start.sh b/tchap/start.sh new file mode 100755 index 000000000..8c8bf2ce5 --- /dev/null +++ b/tchap/start.sh @@ -0,0 +1,62 @@ +#!/bin/bash + +# start the MAS + +set -e + +echo "Starting MAS initialization process..." + +# Get the directory where this script is located +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +export MAS_HOME="$(dirname "$SCRIPT_DIR")" +export MAS_TCHAP_HOME=$SCRIPT_DIR +export RUST_LOG=info + +# start the postgres service if not running already +echo "Step 1/7: Checking PostgreSQL service status..." + +# Check if postgres container is running +if ! docker compose ps postgres | grep -q "Up"; then + echo "PostgreSQL is not running. Starting docker-compose services..." + docker compose up -d postgres + + # Wait for PostgreSQL to be ready + echo "Waiting for PostgreSQL to be ready..." + docker compose exec postgres pg_isready -U postgres + while [ $? -ne 0 ]; do + sleep 10 + docker compose exec postgres pg_isready -U postgres + done + echo "PostgreSQL is ready!" +else + echo "PostgreSQL is already running." +fi + +echo "Step 2/7: Checking policy.wasm..." +#if policy.wasm does not exists create it +if [ -f $MAS_HOME/policies/policy.wasm ]; then + echo "policy.wasm found" +else + cd $MAS_HOME/policies + echo "Creating policy.wasm..." + make DOCKER=1 +fi + +cd "$MAS_HOME/frontend" +echo "Step 3/7: Installing npm dependencies..." +npm install +echo "Step 4/7: Building frontend and static resources..." +npm run build-tchap + +echo "Step 5/7: Building configuration..." +$MAS_TCHAP_HOME/build_conf.sh + +cd "$MAS_HOME" + +echo "Step 6/7: Checking templates..." +cargo run -- templates check -c $MAS_TCHAP_HOME/tmp/config.local.dev.yaml + +echo "Step 7/7: Starting server..." +cargo run -- server -c $MAS_TCHAP_HOME/tmp/config.local.dev.yaml + +echo "MAS initialization completed successfully!"