From c67cb5287f086d739075cc5dd880afbd4655f0ef Mon Sep 17 00:00:00 2001 From: Daniel Mader Date: Tue, 9 Jul 2024 09:30:34 +0200 Subject: [PATCH 01/37] refactor: undo unnecessary changes --- agent_application/docker/Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/agent_application/docker/Dockerfile b/agent_application/docker/Dockerfile index c1859a92..3dc72860 100644 --- a/agent_application/docker/Dockerfile +++ b/agent_application/docker/Dockerfile @@ -12,12 +12,11 @@ FROM chef AS builder COPY --from=planner /app/recipe.json recipe.json RUN cargo chef cook --release --recipe-path recipe.json COPY . . - RUN cargo build --release --bin agent_application FROM debian:bookworm-slim AS runtime -COPY --from=builder /app/target/release/agent_application /usr/local/bin/ WORKDIR /app +COPY --from=builder /app/target/release/agent_application /usr/local/bin/ COPY ../../agent_application/config.yml agent_application/config.yml COPY ../../agent_secret_manager/tests/res/selv.stronghold res/stronghold From 125ef5589c5838511f0129e601e66431085c9428 Mon Sep 17 00:00:00 2001 From: Daniel Mader Date: Wed, 10 Jul 2024 00:29:30 +0200 Subject: [PATCH 02/37] docs: list all config values --- README.md | 4 ++++ agent_application/CONFIGURATION.md | 37 ++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 agent_application/CONFIGURATION.md diff --git a/README.md b/README.md index f819a692..92d9857e 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,10 @@ Build and run the **SSI Agent** in a local Docker environment following [these steps](./agent_application/docker/README.md). +## Configuration + +All configuration options are documented [here](./agent_application/CONFIGURATION.md). + ## Breaking changes From time to time breaking changes can occur. Please make sure you read the [CHANGELOG](./CHANGELOG.md) before updating. diff --git a/agent_application/CONFIGURATION.md b/agent_application/CONFIGURATION.md new file mode 100644 index 00000000..dd1af37e --- /dev/null +++ b/agent_application/CONFIGURATION.md @@ -0,0 +1,37 @@ +# Configuration + +A configuration file is used to configure UniCore. An example can be found in [example-config.yaml](example-config.yaml). Values can also be set through the environment, preferably used to inject sensitive values or environment-specific values. + +> [!NOTE] +> Environment variables override values specified in the configuration file. + +## Common + +| Name | Description | Default value | Accepted values | +| ------------------------------------- | --------------------------------------------------- | ------------- | ------------------------------------------------------------------ | +| `LOG_FORMAT` | The format of the log output. | `json` | `json`, `text` | +| `EVENT_STORE` | The type of event store to use. | - | `in-memory`, `postgres` | +| `EVENT_STORE_DB_CONNECTION_STRING` | The connection string for the event store database. | - | `postgresql://:@` (only required for `postgres`) | +| `URL` | The URL of the service itself. | - | `https://my-domain.example.org` | +| `ENABLE_CORS` | Enable CORS (permissive, allow all). | `false` | boolean | +| `DID_METHOD_WEB_ENABLED` | Create and host a `did:web` document. | `false` | boolean | +| `DOMAIN_LINKAGE_ENABLED` | Enable domain linkage (only works with `did:web`). | `false` | boolean | +| `EXTERNAL_SERVER_RESPONSE_TIMEOUT_MS` | The timeout for external server responses. | `1000` | integer | +| `DEFAULT_DID_METHOD` | The default DID method to use. | `jwk` | `jwk`, `key`, `web` | + +## Stronghold + +| Name | Description | Default value | Accepted values | +| --------------------- | -------------------------------------- | ------------- | ----------------------------- | +| `STRONGHOLD_PATH` | The path to the stronghold file. | - | `/var/lib/unicore/stronghold` | +| `STRONGHOLD_PASSWORD` | The password to unlock the stronghold. | - | - | +| `ISSUER_DID` | The DID of the issuer. | - | - | +| `ISSUER_FRAGMENT` | The fragment to be used. | - | - | +| `KEY_ID` | The key ID to be used. | - | - | + +## Look and Feel + +| Name | Description | Default value | Accepted values | +| --------------------- | --------------------------------- | ------------- | --------------- | +| `CREDENTIAL_NAME` | The name of the credential. | - | string | +| `CREDENTIAL_LOGO_URL` | The URL of the credential's logo. | - | URL | From aac66d0d01b38385155447cb429d633a7ad1cdeb Mon Sep 17 00:00:00 2001 From: Daniel Mader Date: Wed, 10 Jul 2024 00:29:55 +0200 Subject: [PATCH 03/37] refactor: rename config file --- agent_application/{config.yml => example-config.yaml} | 1 - 1 file changed, 1 deletion(-) rename agent_application/{config.yml => example-config.yaml} (99%) diff --git a/agent_application/config.yml b/agent_application/example-config.yaml similarity index 99% rename from agent_application/config.yml rename to agent_application/example-config.yaml index 3f2cca09..61fc7c5b 100644 --- a/agent_application/config.yml +++ b/agent_application/example-config.yaml @@ -26,4 +26,3 @@ display: # This value will also be used in the SIOPv2/OID4VP `client_metadata`'s `logo_uri` property. uri: https://impierce.com/images/logo-blue.png alt_text: UniCore Logo - \ No newline at end of file From 66700a8ea10f3bedbf2d5c0a123f940a1534d0cb Mon Sep 17 00:00:00 2001 From: Daniel Mader Date: Wed, 10 Jul 2024 00:30:37 +0200 Subject: [PATCH 04/37] refactor: remove example files from docker image --- agent_application/docker/Dockerfile | 2 -- 1 file changed, 2 deletions(-) diff --git a/agent_application/docker/Dockerfile b/agent_application/docker/Dockerfile index 3dc72860..4db94ef8 100644 --- a/agent_application/docker/Dockerfile +++ b/agent_application/docker/Dockerfile @@ -18,8 +18,6 @@ FROM debian:bookworm-slim AS runtime WORKDIR /app COPY --from=builder /app/target/release/agent_application /usr/local/bin/ -COPY ../../agent_application/config.yml agent_application/config.yml -COPY ../../agent_secret_manager/tests/res/selv.stronghold res/stronghold COPY ../../agent_event_publisher_http/config.yml agent_event_publisher_http/config.yml COPY ../../agent_verification/presentation_definitions agent_verification/presentation_definitions COPY ../../agent_issuance/issuance-config.yml agent_issuance/issuance-config.yml From ccacd7458efad669a085b6b5ca59ef9eb683a959 Mon Sep 17 00:00:00 2001 From: Daniel Mader Date: Wed, 10 Jul 2024 11:48:33 +0200 Subject: [PATCH 05/37] refactor: merge config files --- .env.example | 22 +++++------ agent_api_rest/README.md | 2 +- agent_application/CONFIGURATION.md | 22 +++++------ agent_application/docker/Dockerfile | 2 - agent_application/example-config.yaml | 53 ++++++++++++++++++++++++++- agent_application/src/main.rs | 2 +- agent_event_publisher_http/Cargo.toml | 1 + agent_event_publisher_http/config.yml | 12 ------ agent_event_publisher_http/src/lib.rs | 29 ++++++++++++++- agent_issuance/issuance-config.yml | 13 ------- agent_shared/src/config.rs | 11 +++--- agent_shared/src/metadata.rs | 1 + 12 files changed, 110 insertions(+), 60 deletions(-) delete mode 100644 agent_event_publisher_http/config.yml delete mode 100644 agent_issuance/issuance-config.yml diff --git a/.env.example b/.env.example index 79384bbe..0e054caf 100644 --- a/.env.example +++ b/.env.example @@ -1,13 +1,9 @@ -AGENT_CONFIG_LOG_FORMAT=json -AGENT_CONFIG_EVENT_STORE=postgres -AGENT_CONFIG_URL=https://my-domain.example.org -AGENT_APPLICATION_ENABLE_CORS=false -AGENT_SECRET_MANAGER_STRONGHOLD_PATH="test.stronghold" -AGENT_SECRET_MANAGER_STRONGHOLD_PASSWORD="secure_password" -AGENT_SECRET_MANAGER_ISSUER_DID="did:key:z6Mkv5KkqNHuR6bPVT8fud3m9JaHBSEjEmiLp7HuGAwtbkk6" -AGENT_SECRET_MANAGER_ISSUER_FRAGMENT="key-0" -AGENT_SECRET_MANAGER_ISSUER_KEY_ID="9O66nzWqYYy1LmmiOudOlh2SMIaUWoTS" -AGENT_CONFIG_DID_METHOD_WEB_ENABLED=false -AGENT_STORE_DB_CONNECTION_STRING=postgresql://demo_user:demo_pass@localhost:5432/demo -AGENT_API_REST_EXTERNAL_SERVER_RESPONSE_TIMEOUT_MS=500 -AGENT_CONFIG_DOMAIN_LINKAGE_ENABLED=false +AGENT_LOG_FORMAT=text +AGENT_EVENT_STORE=in-memory +AGENT_URL="http://192.168.1.234:3033" + +AGENT_STRONGHOLD_PATH="agent_secret_manager/tests/res/test.stronghold" +AGENT_STRONGHOLD_PASSWORD="secure_password" +AGENT_ISSUER_KEY_ID="9O66nzWqYYy1LmmiOudOlh2SMIaUWoTS" +AGENT_ISSUER_DID="did:iota:rms:0x42ad588322e58b3c07aa39e4948d021ee17ecb5747915e9e1f35f028d7ecaf90" +AGENT_ISSUER_FRAGMENT="bQKQRzaop7CgEvqVq8UlgLGsdF-R-hnLFkKFZqW2VN0" diff --git a/agent_api_rest/README.md b/agent_api_rest/README.md index 3667e0ac..c5b14b19 100644 --- a/agent_api_rest/README.md +++ b/agent_api_rest/README.md @@ -18,7 +18,7 @@ Browse to http://localhost:9090 ### CORS -If you want to access UniCore's API from a browser, you can set the `AGENT_APPLICATION_ENABLE_CORS` environment variable to `true`. This will enable a permissive CORS policy (allow all). +If you want to access UniCore's API from a browser, you can set the `AGENT_APPLICATION_CORS_ENABLED` environment variable to `true`. This will enable a permissive CORS policy (allow all). ## Usage Below we describe a typical usage of the REST API for UniCore. diff --git a/agent_application/CONFIGURATION.md b/agent_application/CONFIGURATION.md index dd1af37e..1af45ee9 100644 --- a/agent_application/CONFIGURATION.md +++ b/agent_application/CONFIGURATION.md @@ -7,17 +7,17 @@ A configuration file is used to configure UniCore. An example can be found in [e ## Common -| Name | Description | Default value | Accepted values | -| ------------------------------------- | --------------------------------------------------- | ------------- | ------------------------------------------------------------------ | -| `LOG_FORMAT` | The format of the log output. | `json` | `json`, `text` | -| `EVENT_STORE` | The type of event store to use. | - | `in-memory`, `postgres` | -| `EVENT_STORE_DB_CONNECTION_STRING` | The connection string for the event store database. | - | `postgresql://:@` (only required for `postgres`) | -| `URL` | The URL of the service itself. | - | `https://my-domain.example.org` | -| `ENABLE_CORS` | Enable CORS (permissive, allow all). | `false` | boolean | -| `DID_METHOD_WEB_ENABLED` | Create and host a `did:web` document. | `false` | boolean | -| `DOMAIN_LINKAGE_ENABLED` | Enable domain linkage (only works with `did:web`). | `false` | boolean | -| `EXTERNAL_SERVER_RESPONSE_TIMEOUT_MS` | The timeout for external server responses. | `1000` | integer | -| `DEFAULT_DID_METHOD` | The default DID method to use. | `jwk` | `jwk`, `key`, `web` | +| Name | Description | Default value | Accepted values | +| ------------------------------------- | ------------------------------------------------------------------------- | ------------- | ------------------------------------------------------------------ | +| `LOG_FORMAT` | The format of the log output. | `json` | `json`, `text` | +| `EVENT_STORE` | The type of event store to use. | - | `in-memory`, `postgres` | +| `EVENT_STORE_DB_CONNECTION_STRING` | The connection string for the event store database. | - | `postgresql://:@` (only required for `postgres`) | +| `URL` | The URL of the service itself. | - | `https://my-domain.example.org` | +| `CORS_ENABLED` | Enable CORS (permissive, allow all). Only required for web-based wallets. | `false` | boolean | +| `DID_METHOD_WEB_ENABLED` | Create and host a `did:web` document. | `false` | boolean | +| `DOMAIN_LINKAGE_ENABLED` | Enable domain linkage (only works with `did:web`). | `false` | boolean | +| `EXTERNAL_SERVER_RESPONSE_TIMEOUT_MS` | The timeout for external server responses. | `1000` | integer | +| `PREFERRED_DID_METHOD` | The default DID method to use. | `jwk` | `jwk`, `key`, `web` | ## Stronghold diff --git a/agent_application/docker/Dockerfile b/agent_application/docker/Dockerfile index 4db94ef8..1e365815 100644 --- a/agent_application/docker/Dockerfile +++ b/agent_application/docker/Dockerfile @@ -18,8 +18,6 @@ FROM debian:bookworm-slim AS runtime WORKDIR /app COPY --from=builder /app/target/release/agent_application /usr/local/bin/ -COPY ../../agent_event_publisher_http/config.yml agent_event_publisher_http/config.yml COPY ../../agent_verification/presentation_definitions agent_verification/presentation_definitions -COPY ../../agent_issuance/issuance-config.yml agent_issuance/issuance-config.yml ENTRYPOINT ["/usr/local/bin/agent_application"] diff --git a/agent_application/example-config.yaml b/agent_application/example-config.yaml index 61fc7c5b..4f74085c 100644 --- a/agent_application/example-config.yaml +++ b/agent_application/example-config.yaml @@ -1,3 +1,11 @@ +# Application +log_format: "json" +event_store: "postgres" +db_connection_string: "postgresql://demo_user:demo_pass@localhost:5432/demo" +url: "https://ssi-agent.example.org" +cors_enabled: false +external_server_response_timeout_ms: 500 + # List of Subject Syntax Types (DID Methods) supported by UniCore. The first item in the list is the default Syntax Type, # meaning UniCore will use its identifier belonging to this type by default. subject_syntax_types_supported: @@ -5,6 +13,13 @@ subject_syntax_types_supported: - did:key - did:iota:rms +did_method_web_enabled: false # TODO: should be implicitly enabled when it's listed in "subject_syntax_types_supported" + +# TODO: unused, but let's define this explicitly +preferred_did_method: did:key + +domain_linkage_enabled: false + # List of supported signing algorithms for ID Tokens, Request Objects, and Verifiable Credentials. As of now, UniCore # only supports EdDSA. signing_algorithms_supported: &signing_algorithms_supported @@ -17,7 +32,22 @@ vp_formats: jwt_vp_json: alg: *signing_algorithms_supported -# These display parameters can be used by identity Wallets to display interactions with UniCore. +# agent_event_publisher_http +# Configure here to which events will be dispatched to a specific `target_url`. In the example below, the +# `EventPublisherHttp` will listen for the `SIOPv2AuthorizationResponseVerified` event which is part of the `connection` +# aggregate. The events will be dispatched to the `https://my-domain.example.org/event-subscriber` endpoint. +event_publisher: + http: + enabled: false + target_url: &target_url "https://my-domain.example.org/event-subscriber" + publishers: + connection: + { + target_url: *target_url, + target_events: [SIOPv2AuthorizationResponseVerified], + } + +# These display parameters are interpreted by identity wallets. display: # This value will also be used in the SIOPv2/OID4VP `client_metadata`'s `client_name` property. - name: UniCore @@ -26,3 +56,24 @@ display: # This value will also be used in the SIOPv2/OID4VP `client_metadata`'s `logo_uri` property. uri: https://impierce.com/images/logo-blue.png alt_text: UniCore Logo + +# agent_issuance/issuance-config.yml +server_config: + credential_configurations: + - credential_configuration_id: w3c_vc_credential + format: jwt_vc_json + credential_definition: + type: + - VerifiableCredential + display: + - name: Verifiable Credential + locale: en + logo: + url: https://impierce.com/images/logo-blue.png + alt_text: UniCore Logo +# Key configuration (temporary) +# stronghold_path: "/tmp/local.stronghold" +# stronghold_password: "secure_password" +# issuer_key_id: "ed25519-0" +# issuer_did: "did:iota:rms:0x0000000000000000000000000000000000000000000000000000000000000000" +# issuer_fragment: "key-0" diff --git a/agent_application/src/main.rs b/agent_application/src/main.rs index d9cfceab..15c95f43 100644 --- a/agent_application/src/main.rs +++ b/agent_application/src/main.rs @@ -70,7 +70,7 @@ async fn main() -> io::Result<()> { let mut app = app((issuance_state, verification_state)); // CORS - if config!("enable_cors", bool).unwrap_or(false) { + if config!("cors_enabled", bool).unwrap_or(false) { info!("CORS (permissive) enabled for all routes"); app = app.layer(CorsLayer::permissive()); } diff --git a/agent_event_publisher_http/Cargo.toml b/agent_event_publisher_http/Cargo.toml index 115b6097..a7e4f590 100644 --- a/agent_event_publisher_http/Cargo.toml +++ b/agent_event_publisher_http/Cargo.toml @@ -6,6 +6,7 @@ rust-version.workspace = true [dependencies] agent_issuance = { path = "../agent_issuance" } +agent_shared = { path = "../agent_shared" } agent_store = { path = "../agent_store" } agent_verification = { path = "../agent_verification" } diff --git a/agent_event_publisher_http/config.yml b/agent_event_publisher_http/config.yml deleted file mode 100644 index 0c03356d..00000000 --- a/agent_event_publisher_http/config.yml +++ /dev/null @@ -1,12 +0,0 @@ -# Configure here to which events will be dispatched to a specific `target_url`. In the example below, the -# `EventPublisherHttp` will listen for the `SIOPv2AuthorizationResponseVerified` event which is part of the `connection` -# aggregate. The events will be dispatched to the `https://my-domain.example.org/event-subscriber` endpoint. - -# target_url: &target_url "https://my-domain.example.org/event-subscriber" - -# connection: { -# target_url: *target_url, -# target_events: [ -# SIOPv2AuthorizationResponseVerified -# ] -# } diff --git a/agent_event_publisher_http/src/lib.rs b/agent_event_publisher_http/src/lib.rs index 7628090f..c11be6b9 100644 --- a/agent_event_publisher_http/src/lib.rs +++ b/agent_event_publisher_http/src/lib.rs @@ -46,8 +46,35 @@ impl EventPublisherHttp { Err(_) => serde_yaml::Value::Null, } }; + let config = agent_shared::config::config("AGENT_EVENT_PUBLISHER_HTTP"); - config.apply_merge()?; + let event_publishers = config.get_table("event_publisher").unwrap_or_default(); + let event_publisher_http = event_publishers + .get("http") + .unwrap() + .clone() + .into_table() + .unwrap_or_default(); + + info!("event_publisher_http: {:?}", event_publisher_http); + + let config = if event_publisher_http + .get("enabled") + .unwrap() + .clone() + .into_bool() + .unwrap_or_default() + { + let publishers: serde_yaml::Value = event_publisher_http + .get("publishers") + .unwrap() + .clone() + .try_deserialize() + .unwrap(); + publishers + } else { + serde_yaml::Value::Null + }; serde_yaml::from_value(config) .map_err(Into::into) diff --git a/agent_issuance/issuance-config.yml b/agent_issuance/issuance-config.yml deleted file mode 100644 index d3f91aaa..00000000 --- a/agent_issuance/issuance-config.yml +++ /dev/null @@ -1,13 +0,0 @@ -server_config: - credential_configurations: - - credential_configuration_id: w3c_vc_credential - format: jwt_vc_json - credential_definition: - type: - - VerifiableCredential - display: - - name: Verifiable Credential - locale: en - logo: - url: https://impierce.com/images/logo-blue.png - alt_text: UniCore Logo diff --git a/agent_shared/src/config.rs b/agent_shared/src/config.rs index 26d39e60..78216a10 100644 --- a/agent_shared/src/config.rs +++ b/agent_shared/src/config.rs @@ -11,15 +11,16 @@ pub fn config(package_name: &str) -> config::Config { dotenvy::dotenv().ok(); config::Config::builder() - .add_source(config::Environment::with_prefix(package_name)) - .add_source(config::Environment::with_prefix("AGENT_CONFIG")) - .add_source(config::File::with_name("agent_application/config.yml")) - .add_source(config::File::with_name("agent_issuance/issuance-config.yml")) + // .add_source(config::Environment::with_prefix(package_name)) + // TODO: read config.{run_mode}.yml from env "RUN_MODE" + .add_source(config::File::with_name("agent_application/example-config.yaml")) + .add_source(config::Environment::with_prefix("AGENT")) .build() .unwrap() }; - info!("{:?}", config); + // TODO: this should ideally only printed once on startup or when the config changed + // info!("{:#?}", config.clone().try_deserialize::().unwrap()); config } diff --git a/agent_shared/src/metadata.rs b/agent_shared/src/metadata.rs index a88f6a50..99bde19d 100644 --- a/agent_shared/src/metadata.rs +++ b/agent_shared/src/metadata.rs @@ -55,6 +55,7 @@ pub fn load_metadata() -> Metadata { Err(_) => serde_yaml::Value::Null, } }; + let mut config: serde_yaml::Value = crate::config::config("AGENT_APPLICATION").try_deserialize().unwrap(); let supported_algorithms: serde_yaml::Value = vec!["EdDSA".to_string()].into(); if config["signing_algorithms_supported"] != supported_algorithms { From 7094ee8a138570ea43249514c5e861488a863d8c Mon Sep 17 00:00:00 2001 From: Daniel Mader Date: Wed, 10 Jul 2024 12:55:13 +0200 Subject: [PATCH 06/37] refactor: update .dockerignore to include all Dockerfiles and .env files --- .dockerignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.dockerignore b/.dockerignore index bc7a410f..4ffd5c8a 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,4 @@ -Dockerfile +**/Dockerfile **/target **/.git +**/*.env From 4352cd626d665db8c5304e9959430412211b3472 Mon Sep 17 00:00:00 2001 From: Daniel Mader Date: Thu, 11 Jul 2024 00:44:01 +0200 Subject: [PATCH 07/37] WIP: migrate from config macro to config function --- .../issuance/credential_issuer/credential.rs | 19 ++++---- agent_api_rest/src/lib.rs | 39 ++++++++------- agent_application/example-config.yaml | 26 ++++++---- agent_application/src/main.rs | 12 ++++- agent_issuance/src/server_config/aggregate.rs | 11 ++++- agent_secret_manager/src/services.rs | 13 +++-- agent_shared/src/config.rs | 48 +++++++++++++++++++ agent_shared/src/metadata.rs | 6 +++ agent_verification/src/services.rs | 12 +++-- 9 files changed, 139 insertions(+), 47 deletions(-) diff --git a/agent_api_rest/src/issuance/credential_issuer/credential.rs b/agent_api_rest/src/issuance/credential_issuer/credential.rs index 3d684f3e..fe5953b0 100644 --- a/agent_api_rest/src/issuance/credential_issuer/credential.rs +++ b/agent_api_rest/src/issuance/credential_issuer/credential.rs @@ -11,6 +11,7 @@ use agent_issuance::{ }; use agent_shared::{ config, + config::config_2, handlers::{command_handler, query_handler}, }; use axum::{ @@ -24,7 +25,7 @@ use serde_json::json; use tokio::time::sleep; use tracing::{error, info}; -const DEFAULT_EXTERNAL_SERVER_RESPONSE_TIMEOUT_MS: u128 = 1000; +const DEFAULT_EXTERNAL_SERVER_RESPONSE_TIMEOUT_MS: u64 = 1000; const POLLING_INTERVAL_MS: u64 = 100; #[axum_macros::debug_handler] @@ -64,9 +65,11 @@ pub(crate) async fn credential( StatusCode::INTERNAL_SERVER_ERROR.into_response(); }; - let timeout = config!("external_server_response_timeout_ms", String) - .ok() - .and_then(|external_server_response_timeout_ms| external_server_response_timeout_ms.parse().ok()) + let timeout = config_2() + .external_server_response_timeout_ms + // let timeout = config!("external_server_response_timeout_ms", String) + // .ok() + // .and_then(|external_server_response_timeout_ms| external_server_response_timeout_ms.parse().ok()) .unwrap_or(DEFAULT_EXTERNAL_SERVER_RESPONSE_TIMEOUT_MS); let start_time = Instant::now(); @@ -76,7 +79,7 @@ pub(crate) async fn credential( match query_handler(&offer_id, &state.query.offer).await { // When the Offer does not include the credential id's yet, wait for the external server to provide them. Ok(Some(OfferView { credential_ids, .. })) if credential_ids.is_empty() => { - if start_time.elapsed().as_millis() <= timeout { + if start_time.elapsed().as_millis() <= timeout.into() { sleep(Duration::from_millis(POLLING_INTERVAL_MS)).await; } else { error!("timeout failure"); @@ -181,7 +184,7 @@ mod tests { &self, app: Arc>>, is_self_signed: bool, - delay: u128, + delay: u64, ); } @@ -193,7 +196,7 @@ mod tests { &self, app: Arc>>, is_self_signed: bool, - delay: u128, + delay: u64, ) { Mock::given(method("POST")) .and(path("/ssi-events-subscriber")) @@ -275,7 +278,7 @@ mod tests { async fn test_credential_endpoint( #[case] with_external_server: bool, #[case] is_self_signed: bool, - #[case] delay: u128, + #[case] delay: u64, ) { set_metadata_configuration("did:key"); diff --git a/agent_api_rest/src/lib.rs b/agent_api_rest/src/lib.rs index 9f0dcdc6..d21d311a 100644 --- a/agent_api_rest/src/lib.rs +++ b/agent_api_rest/src/lib.rs @@ -2,7 +2,7 @@ mod issuance; mod verification; use agent_issuance::state::IssuanceState; -use agent_shared::{config, ConfigError}; +use agent_shared::{config, config::config_2, ConfigError}; use agent_verification::state::VerificationState; use axum::{ body::Bytes, @@ -99,23 +99,26 @@ pub fn app(state: ApplicationState) -> Router { } fn get_base_path() -> Result { - config!("base_path", String).map(|mut base_path| { - if base_path.starts_with('/') { - base_path.remove(0); - } - - if base_path.ends_with('/') { - base_path.pop(); - } - - if base_path.is_empty() { - panic!("AGENT_APPLICATION_BASE_PATH can't be empty, remove or set path"); - } - - tracing::info!("Base path: {:?}", base_path); - - base_path - }) + config_2() + .base_path + .ok_or_else(|| ConfigError::NotFound("No configuration for `base_path` found".to_string())) + .map(|mut base_path| { + if base_path.starts_with('/') { + base_path.remove(0); + } + + if base_path.ends_with('/') { + base_path.pop(); + } + + if base_path.is_empty() { + panic!("AGENT_APPLICATION_BASE_PATH can't be empty, remove or set path"); + } + + tracing::info!("Base path: {:?}", base_path); + + base_path + }) } #[cfg(test)] diff --git a/agent_application/example-config.yaml b/agent_application/example-config.yaml index 4f74085c..be927b83 100644 --- a/agent_application/example-config.yaml +++ b/agent_application/example-config.yaml @@ -3,20 +3,28 @@ log_format: "json" event_store: "postgres" db_connection_string: "postgresql://demo_user:demo_pass@localhost:5432/demo" url: "https://ssi-agent.example.org" +# base_path: "unicore" cors_enabled: false external_server_response_timeout_ms: 500 +did_methods: + did_jwk: + enabled: true + # preferred: true + did_key: + enabled: true + did_iota_rms: + enabled: true + did_web: + enabled: true + preferred: true + # List of Subject Syntax Types (DID Methods) supported by UniCore. The first item in the list is the default Syntax Type, # meaning UniCore will use its identifier belonging to this type by default. -subject_syntax_types_supported: - - did:jwk - - did:key - - did:iota:rms - -did_method_web_enabled: false # TODO: should be implicitly enabled when it's listed in "subject_syntax_types_supported" - -# TODO: unused, but let's define this explicitly -preferred_did_method: did:key +# subject_syntax_types_supported: +# - did:jwk +# - did:key +# - did:iota:rms domain_linkage_enabled: false diff --git a/agent_application/src/main.rs b/agent_application/src/main.rs index 15c95f43..6d1b5ccf 100644 --- a/agent_application/src/main.rs +++ b/agent_application/src/main.rs @@ -4,6 +4,7 @@ use agent_issuance::{startup_commands::startup_commands, state::initialize}; use agent_secret_manager::{secret_manager, subject::Subject}; use agent_shared::{ config, + config::{config_2, DidMethodOptions}, domain_linkage::create_did_configuration_resource, metadata::{load_metadata, Metadata}, }; @@ -57,7 +58,9 @@ async fn main() -> io::Result<()> { ), }; - let url = config!("url", String).expect("AGENT_CONFIG_URL is not set"); + // let url = config!("url", String).expect("AGENT_CONFIG_URL is not set"); + let url = config_2().url; + info!("{:?}", config_2()); // TODO: Temporary solution. In the future we need to read these kinds of values from a config file. std::env::set_var("AGENT_VERIFICATION_URL", &url); @@ -76,7 +79,12 @@ async fn main() -> io::Result<()> { } // did:web - let enable_did_web = config!("did_method_web_enabled", bool).unwrap_or(false); + // let enable_did_web = config!("did_method_web_enabled", bool).unwrap_or(false); + let enable_did_web = config_2() + .did_methods + .get("did_web") + .unwrap_or(&DidMethodOptions::default()) + .enabled; let did_document = if enable_did_web { let subject = Subject { diff --git a/agent_issuance/src/server_config/aggregate.rs b/agent_issuance/src/server_config/aggregate.rs index 5aa69de7..dca15dd3 100644 --- a/agent_issuance/src/server_config/aggregate.rs +++ b/agent_issuance/src/server_config/aggregate.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use agent_shared::config; +use agent_shared::config::config_2; use async_trait::async_trait; use cqrs_es::Aggregate; use jsonwebtoken::Algorithm; @@ -58,8 +59,14 @@ impl Aggregate for ServerConfig { AddCredentialConfiguration { credential_configuration, } => { - let cryptographic_binding_methods_supported = - config!("subject_syntax_types_supported", Vec).unwrap_or_default(); + let cryptographic_binding_methods_supported = config_2() + .did_methods + .iter() + .filter(|(_, v)| v.enabled) + .map(|(k, _)| k.clone()) + // TODO: find less hacky solution, possibly: enum + serde rename + .map(|did_method| did_method.replace("_", ":")) + .collect(); let credential_signing_alg_values_supported = config!("signing_algorithms_supported", Vec).unwrap_or_default(); diff --git a/agent_secret_manager/src/services.rs b/agent_secret_manager/src/services.rs index c400886f..e9a46a24 100644 --- a/agent_secret_manager/src/services.rs +++ b/agent_secret_manager/src/services.rs @@ -1,5 +1,6 @@ use crate::subject::Subject; use agent_shared::config; +use agent_shared::config::config_2; use anyhow::Result; use did_manager::SecretManager; @@ -10,9 +11,15 @@ pub struct SecretManagerServices { impl SecretManagerServices { pub fn new(subject: Option) -> Self { - let default_did_method = config!("subject_syntax_types_supported", Vec) - .ok() - .and_then(|subject_syntax_types_supported| subject_syntax_types_supported.first().cloned()) + let default_did_method: String = config_2() + .did_methods + .iter() + .filter(|(_, v)| v.preferred.unwrap_or(false)) + .map(|(k, _)| k.clone()) + .collect::>() + // TODO: should fail when there's more than one result + .first() + .cloned() .unwrap_or("did:key".to_string()); Self { subject, diff --git a/agent_shared/src/config.rs b/agent_shared/src/config.rs index 78216a10..5cbe48f9 100644 --- a/agent_shared/src/config.rs +++ b/agent_shared/src/config.rs @@ -1,5 +1,53 @@ +use config::ConfigError; +use serde::Deserialize; +use std::{collections::HashMap, sync::Mutex}; use tracing::info; +#[derive(Debug, Deserialize, Clone)] +pub struct ApplicationConfiguration { + pub log_format: String, + pub url: String, + pub base_path: Option, + pub did_methods: HashMap, + pub external_server_response_timeout_ms: Option, +} + +// pub enum DidMethod { +// #[serde(rename = "did:jwk")] +// Jwk, +// Key, +// Web, +// IotaRms, +// } + +#[derive(Debug, Deserialize, Default, Clone)] +pub struct DidMethodOptions { + pub enabled: bool, + pub preferred: Option, +} + +static CONFIG: Mutex> = Mutex::new(None); + +impl ApplicationConfiguration { + pub fn new() -> Result { + info!("Loading application configuration ..."); + let config = config::Config::builder() + .add_source(config::File::with_name("agent_application/example-config.yaml")) + .add_source(config::Environment::with_prefix("AGENT").separator("__")) + .build()?; + config.try_deserialize() + } +} + +/// Loads the configuration or returns it, if it has already been loaded. +pub fn config_2() -> ApplicationConfiguration { + CONFIG + .lock() + .unwrap() + .get_or_insert_with(|| ApplicationConfiguration::new().unwrap()) + .clone() +} + /// Read environment variables #[allow(unused)] pub fn config(package_name: &str) -> config::Config { diff --git a/agent_shared/src/metadata.rs b/agent_shared/src/metadata.rs index 99bde19d..fe973ed6 100644 --- a/agent_shared/src/metadata.rs +++ b/agent_shared/src/metadata.rs @@ -8,6 +8,8 @@ use serde_with::skip_serializing_none; use tracing::info; use url::Url; +use crate::config::config_2; + #[skip_serializing_none] #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Logo { @@ -57,6 +59,8 @@ pub fn load_metadata() -> Metadata { }; let mut config: serde_yaml::Value = crate::config::config("AGENT_APPLICATION").try_deserialize().unwrap(); + info!("config: {:?}", config); + let supported_algorithms: serde_yaml::Value = vec!["EdDSA".to_string()].into(); if config["signing_algorithms_supported"] != supported_algorithms { unimplemented!( @@ -67,6 +71,8 @@ pub fn load_metadata() -> Metadata { config.apply_merge().unwrap(); + info!("config: {:?}", config); + let metadata = serde_yaml::from_value::(config).expect("Invalid metadata in `agent_application/config.yml` file"); diff --git a/agent_verification/src/services.rs b/agent_verification/src/services.rs index 67b7f227..eb096fa5 100644 --- a/agent_verification/src/services.rs +++ b/agent_verification/src/services.rs @@ -5,6 +5,7 @@ use oid4vc_manager::RelyingPartyManager; use oid4vp::ClaimFormatProperty; use serde_json::json; use std::{collections::HashMap, sync::Arc}; +use tracing::info; /// Verification services. This struct is used to generate authorization requests and validate authorization responses. pub struct VerificationServices { @@ -16,11 +17,12 @@ pub struct VerificationServices { impl VerificationServices { pub fn new(verifier: Arc, metadata: &Metadata) -> Self { - let default_did_method = metadata - .subject_syntax_types_supported - .first() - .expect("`subject_syntax_types_supported` must contain at least one element.") - .to_string(); + // let default_did_method = metadata + // .subject_syntax_types_supported + // .first() + // .expect("`subject_syntax_types_supported` must contain at least one element.") + // .to_string(); + let default_did_method = "did:key".to_string(); let client_name = metadata.display.first().as_ref().map(|display| display.name.clone()); From f282fb1c4894c97610e2c84098af3d8ef7e275eb Mon Sep 17 00:00:00 2001 From: Daniel Mader Date: Thu, 11 Jul 2024 10:27:27 +0200 Subject: [PATCH 08/37] refactor: configs for `log_format` and `event_store` --- .env.example | 5 +-- Cargo.lock | 1 + agent_application/example-config.yaml | 5 +-- agent_application/src/main.rs | 33 +++++++++++++------- agent_shared/src/config.rs | 45 +++++++++++++++++++++++---- agent_store/Cargo.toml | 1 + agent_store/src/postgres.rs | 9 +++--- 7 files changed, 73 insertions(+), 26 deletions(-) diff --git a/.env.example b/.env.example index 0e054caf..81ee1e24 100644 --- a/.env.example +++ b/.env.example @@ -1,5 +1,6 @@ -AGENT_LOG_FORMAT=text -AGENT_EVENT_STORE=in-memory +AGENT__LOG_FORMAT=text +AGENT__EVENT_STORE__TYPE=postgres +AGENT__EVENT_STORE__CONNECTION_STRING="postgresql://demo_user:demo_pass@localhost:5432/demo" AGENT_URL="http://192.168.1.234:3033" AGENT_STRONGHOLD_PATH="agent_secret_manager/tests/res/test.stronghold" diff --git a/Cargo.lock b/Cargo.lock index 7bf47454..ea7d26df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -243,6 +243,7 @@ dependencies = [ "async-trait", "cqrs-es", "postgres-es", + "serde", "serde_json", "sqlx", "tokio", diff --git a/agent_application/example-config.yaml b/agent_application/example-config.yaml index be927b83..e243450e 100644 --- a/agent_application/example-config.yaml +++ b/agent_application/example-config.yaml @@ -1,7 +1,8 @@ # Application log_format: "json" -event_store: "postgres" -db_connection_string: "postgresql://demo_user:demo_pass@localhost:5432/demo" +event_store: + type: "postgres" + # connection_string: "" <== Should be injected through the env variable `AGENT__EVENT_STORE__CONNECTION_STRING` url: "https://ssi-agent.example.org" # base_path: "unicore" cors_enabled: false diff --git a/agent_application/src/main.rs b/agent_application/src/main.rs index 6d1b5ccf..24d87d8b 100644 --- a/agent_application/src/main.rs +++ b/agent_application/src/main.rs @@ -4,7 +4,7 @@ use agent_issuance::{startup_commands::startup_commands, state::initialize}; use agent_secret_manager::{secret_manager, subject::Subject}; use agent_shared::{ config, - config::{config_2, DidMethodOptions}, + config::{config_2, DidMethodOptions, LogFormat}, domain_linkage::create_did_configuration_resource, metadata::{load_metadata, Metadata}, }; @@ -26,11 +26,13 @@ async fn main() -> io::Result<()> { // Set the default logging level to `info`, equivalent to `RUST_LOG=info` .with(tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into())); - match config!("log_format", String) { - Ok(log_format) if log_format == "json" => { - tracing_subscriber.with(tracing_subscriber::fmt::layer().json()).init() - } - _ => tracing_subscriber.with(tracing_subscriber::fmt::layer()).init(), + match config_2().log_format { + LogFormat::Json => tracing_subscriber.with(tracing_subscriber::fmt::layer().json()).init(), + LogFormat::Text => tracing_subscriber.with(tracing_subscriber::fmt::layer()).init(), + // Ok(log_format) if log_format == "json" => { + // tracing_subscriber.with(tracing_subscriber::fmt::layer().json()).init() + // } + // _ => tracing_subscriber.with(tracing_subscriber::fmt::layer()).init(), } let verification_services = Arc::new(VerificationServices::new( @@ -47,12 +49,19 @@ async fn main() -> io::Result<()> { let verification_event_publishers: Vec> = vec![Box::new(EventPublisherHttp::load().unwrap())]; - let (issuance_state, verification_state) = match agent_shared::config!("event_store", String).unwrap().as_str() { - "postgres" => ( - postgres::issuance_state(issuance_event_publishers).await, - postgres::verification_state(verification_services, verification_event_publishers).await, - ), - _ => ( + // let (issuance_state, verification_state) = match agent_shared::config!("event_store", String).unwrap().as_str() { + let (issuance_state, verification_state) = match agent_shared::config::config_2().event_store.type_ { + agent_shared::config::EventStoreType::Postgres => { + let connection_string = config_2().event_store.connection_string.expect( + "Missing config parameter `event_store.connection_string` or `AGENT__EVENT_STORE__CONNECTION_STRING`", + ); + ( + postgres::issuance_state(issuance_event_publishers, &connection_string).await, + postgres::verification_state(verification_services, verification_event_publishers, &connection_string) + .await, + ) + } + agent_shared::config::EventStoreType::InMemory => ( in_memory::issuance_state(issuance_event_publishers).await, in_memory::verification_state(verification_services, verification_event_publishers).await, ), diff --git a/agent_shared/src/config.rs b/agent_shared/src/config.rs index 5cbe48f9..1f3ac547 100644 --- a/agent_shared/src/config.rs +++ b/agent_shared/src/config.rs @@ -5,13 +5,42 @@ use tracing::info; #[derive(Debug, Deserialize, Clone)] pub struct ApplicationConfiguration { - pub log_format: String, + pub log_format: LogFormat, + pub event_store: EventStoreConfig, pub url: String, pub base_path: Option, pub did_methods: HashMap, pub external_server_response_timeout_ms: Option, } +#[derive(Debug, Deserialize, Clone, Default)] +#[serde(rename_all = "lowercase")] +pub enum LogFormat { + #[default] + Json, + Text, +} + +#[derive(Debug, Deserialize, Clone)] +pub struct EventStoreConfig { + #[serde(rename = "type")] + pub type_: EventStoreType, + pub connection_string: Option, +} + +#[derive(Debug, Deserialize, Clone)] +#[serde(rename_all = "snake_case")] +pub enum EventStoreType { + InMemory, + // Postgres(EventStorePostgresConfig), // <== "config-rs" panicks with "unreachable code" + Postgres, +} + +#[derive(Debug, Deserialize, Clone)] +pub struct EventStorePostgresConfig { + pub connection_string: String, +} + // pub enum DidMethod { // #[serde(rename = "did:jwk")] // Jwk, @@ -41,11 +70,15 @@ impl ApplicationConfiguration { /// Loads the configuration or returns it, if it has already been loaded. pub fn config_2() -> ApplicationConfiguration { - CONFIG - .lock() - .unwrap() - .get_or_insert_with(|| ApplicationConfiguration::new().unwrap()) - .clone() + info!("config_2()"); + // CONFIG + // .lock() + // .unwrap() + // .get_or_insert_with(|| ApplicationConfiguration::new().unwrap()) + // .clone() + let config = ApplicationConfiguration::new().unwrap(); + info!("{:#?}", config); + config } /// Read environment variables diff --git a/agent_store/Cargo.toml b/agent_store/Cargo.toml index e4536e03..31edde3a 100644 --- a/agent_store/Cargo.toml +++ b/agent_store/Cargo.toml @@ -13,6 +13,7 @@ cqrs-es.workspace = true postgres-es = "0.4.10" async-trait = "0.1" +serde.workspace = true serde_json.workspace = true sqlx = { version = "0.7", features = [ "postgres", diff --git a/agent_store/src/postgres.rs b/agent_store/src/postgres.rs index b1d4f091..c07ce332 100644 --- a/agent_store/src/postgres.rs +++ b/agent_store/src/postgres.rs @@ -8,7 +8,7 @@ use agent_issuance::{ state::{CommandHandlers, IssuanceState, ViewRepositories}, SimpleLoggingQuery, }; -use agent_shared::{application_state::Command, config, generic_query::generic_query}; +use agent_shared::{application_state::Command, generic_query::generic_query}; use agent_verification::{services::VerificationServices, state::VerificationState}; use async_trait::async_trait; use cqrs_es::{Aggregate, Query}; @@ -67,8 +67,8 @@ where } } -pub async fn issuance_state(event_publishers: Vec>) -> IssuanceState { - let pool = default_postgress_pool(&config!("db_connection_string", String).unwrap()).await; +pub async fn issuance_state(event_publishers: Vec>, connection_string: &str) -> IssuanceState { + let pool = default_postgress_pool(connection_string).await; // Initialize the postgres repositories. let server_config = Arc::new(PostgresViewRepository::new("server_config", pool.clone())); @@ -127,8 +127,9 @@ pub async fn issuance_state(event_publishers: Vec>) -> I pub async fn verification_state( verification_services: Arc, event_publishers: Vec>, + connection_string: &str, ) -> VerificationState { - let pool = default_postgress_pool(&config!("db_connection_string", String).unwrap()).await; + let pool = default_postgress_pool(connection_string).await; // Initialize the postgres repositories. let authorization_request = Arc::new(PostgresViewRepository::new("authorization_request", pool.clone())); From d47c590ad2ed7c26edd1eab951c1678b9b79a2c4 Mon Sep 17 00:00:00 2001 From: Daniel Mader Date: Thu, 11 Jul 2024 14:38:43 +0200 Subject: [PATCH 09/37] refactor: remove unused macros calls --- .env.example | 2 +- .../src/issuance/credential_issuer/credential.rs | 3 --- agent_application/example-config.yaml | 6 +++--- agent_application/src/main.rs | 12 ++++-------- agent_secret_manager/src/subject.rs | 6 ++---- agent_shared/src/config.rs | 2 ++ 6 files changed, 12 insertions(+), 19 deletions(-) diff --git a/.env.example b/.env.example index 81ee1e24..15a6c00c 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,7 @@ AGENT__LOG_FORMAT=text AGENT__EVENT_STORE__TYPE=postgres AGENT__EVENT_STORE__CONNECTION_STRING="postgresql://demo_user:demo_pass@localhost:5432/demo" -AGENT_URL="http://192.168.1.234:3033" +AGENT__URL="http://192.168.1.234:3033" AGENT_STRONGHOLD_PATH="agent_secret_manager/tests/res/test.stronghold" AGENT_STRONGHOLD_PASSWORD="secure_password" diff --git a/agent_api_rest/src/issuance/credential_issuer/credential.rs b/agent_api_rest/src/issuance/credential_issuer/credential.rs index fe5953b0..ad2ede35 100644 --- a/agent_api_rest/src/issuance/credential_issuer/credential.rs +++ b/agent_api_rest/src/issuance/credential_issuer/credential.rs @@ -67,9 +67,6 @@ pub(crate) async fn credential( let timeout = config_2() .external_server_response_timeout_ms - // let timeout = config!("external_server_response_timeout_ms", String) - // .ok() - // .and_then(|external_server_response_timeout_ms| external_server_response_timeout_ms.parse().ok()) .unwrap_or(DEFAULT_EXTERNAL_SERVER_RESPONSE_TIMEOUT_MS); let start_time = Instant::now(); diff --git a/agent_application/example-config.yaml b/agent_application/example-config.yaml index e243450e..8b9f2082 100644 --- a/agent_application/example-config.yaml +++ b/agent_application/example-config.yaml @@ -3,9 +3,9 @@ log_format: "json" event_store: type: "postgres" # connection_string: "" <== Should be injected through the env variable `AGENT__EVENT_STORE__CONNECTION_STRING` -url: "https://ssi-agent.example.org" -# base_path: "unicore" -cors_enabled: false +# url: "https://ssi-agent.example.org" +# base_path: "unicore" <== Runs all endpoints with a base path such as `https://ssi-agent.example.org/unicore` +# cors_enabled: false <== Only applicable for browser-based wallets that require CORS external_server_response_timeout_ms: 500 did_methods: diff --git a/agent_application/src/main.rs b/agent_application/src/main.rs index 24d87d8b..7a6dabe7 100644 --- a/agent_application/src/main.rs +++ b/agent_application/src/main.rs @@ -49,7 +49,6 @@ async fn main() -> io::Result<()> { let verification_event_publishers: Vec> = vec![Box::new(EventPublisherHttp::load().unwrap())]; - // let (issuance_state, verification_state) = match agent_shared::config!("event_store", String).unwrap().as_str() { let (issuance_state, verification_state) = match agent_shared::config::config_2().event_store.type_ { agent_shared::config::EventStoreType::Postgres => { let connection_string = config_2().event_store.connection_string.expect( @@ -67,11 +66,9 @@ async fn main() -> io::Result<()> { ), }; - // let url = config!("url", String).expect("AGENT_CONFIG_URL is not set"); - let url = config_2().url; info!("{:?}", config_2()); - // TODO: Temporary solution. In the future we need to read these kinds of values from a config file. - std::env::set_var("AGENT_VERIFICATION_URL", &url); + + let url = config_2().url; info!("Application url: {:?}", url); @@ -82,13 +79,12 @@ async fn main() -> io::Result<()> { let mut app = app((issuance_state, verification_state)); // CORS - if config!("cors_enabled", bool).unwrap_or(false) { + if config_2().cors_enabled.unwrap_or(false) { info!("CORS (permissive) enabled for all routes"); app = app.layer(CorsLayer::permissive()); } // did:web - // let enable_did_web = config!("did_method_web_enabled", bool).unwrap_or(false); let enable_did_web = config_2() .did_methods .get("did_web") @@ -113,7 +109,7 @@ async fn main() -> io::Result<()> { None }; // Domain Linkage - let did_configuration_resource = if config!("domain_linkage_enabled", bool).unwrap_or(false) { + let did_configuration_resource = if config_2().domain_linkage_enabled { Some( create_did_configuration_resource( url.clone(), diff --git a/agent_secret_manager/src/subject.rs b/agent_secret_manager/src/subject.rs index 4aa3a141..2ba74084 100644 --- a/agent_secret_manager/src/subject.rs +++ b/agent_secret_manager/src/subject.rs @@ -1,4 +1,4 @@ -use agent_shared::config; +use agent_shared::config::config_2; use async_trait::async_trait; use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine}; use did_manager::{DidMethod, Resolver, SecretManager}; @@ -109,7 +109,5 @@ impl oid4vc_core::Subject for Subject { } fn origin() -> url::Origin { - config!("url", url::Url) - .map(|url| url.origin()) - .expect("AGENT_CONFIG_URL is not set") + config_2().url.parse::().unwrap().origin() } diff --git a/agent_shared/src/config.rs b/agent_shared/src/config.rs index 1f3ac547..9b124f9c 100644 --- a/agent_shared/src/config.rs +++ b/agent_shared/src/config.rs @@ -9,8 +9,10 @@ pub struct ApplicationConfiguration { pub event_store: EventStoreConfig, pub url: String, pub base_path: Option, + pub cors_enabled: Option, pub did_methods: HashMap, pub external_server_response_timeout_ms: Option, + pub domain_linkage_enabled: bool, } #[derive(Debug, Deserialize, Clone, Default)] From 5ba695fcf87f0d500ed9403c7ed5b6cf19813650 Mon Sep 17 00:00:00 2001 From: Daniel Mader Date: Fri, 12 Jul 2024 09:30:12 +0200 Subject: [PATCH 10/37] refactor: migrate `url` config --- agent_application/example-config.yaml | 40 ++++++++++--------- agent_issuance/src/credential/aggregate.rs | 18 ++++----- .../src/authorization_request/aggregate.rs | 3 +- 3 files changed, 30 insertions(+), 31 deletions(-) diff --git a/agent_application/example-config.yaml b/agent_application/example-config.yaml index 8b9f2082..6187e257 100644 --- a/agent_application/example-config.yaml +++ b/agent_application/example-config.yaml @@ -3,7 +3,7 @@ log_format: "json" event_store: type: "postgres" # connection_string: "" <== Should be injected through the env variable `AGENT__EVENT_STORE__CONNECTION_STRING` -# url: "https://ssi-agent.example.org" +url: "https://ssi-agent.example.org" # base_path: "unicore" <== Runs all endpoints with a base path such as `https://ssi-agent.example.org/unicore` # cors_enabled: false <== Only applicable for browser-based wallets that require CORS external_server_response_timeout_ms: 500 @@ -67,22 +67,24 @@ display: alt_text: UniCore Logo # agent_issuance/issuance-config.yml -server_config: - credential_configurations: - - credential_configuration_id: w3c_vc_credential - format: jwt_vc_json - credential_definition: - type: - - VerifiableCredential - display: - - name: Verifiable Credential - locale: en - logo: - url: https://impierce.com/images/logo-blue.png - alt_text: UniCore Logo +credential_configurations: + - credential_configuration_id: w3c_vc_credential + format: jwt_vc_json + credential_definition: + type: + - VerifiableCredential + display: + - name: Verifiable Credential + locale: en + logo: + url: https://impierce.com/images/logo-blue.png + alt_text: UniCore Logo + # Key configuration (temporary) -# stronghold_path: "/tmp/local.stronghold" -# stronghold_password: "secure_password" -# issuer_key_id: "ed25519-0" -# issuer_did: "did:iota:rms:0x0000000000000000000000000000000000000000000000000000000000000000" -# issuer_fragment: "key-0" +secret_manager: + stronghold_path: "/tmp/local.stronghold" + # stronghold_password: "" <== Should be injected through the env variable `AGENT__SECRET_MANAGER__STRONGHOLD_PASSWORD` + # stronghold_password_file: "" + # issuer_key_id: "ed25519-0" + # issuer_did: "did:iota:rms:0x0000000000000000000000000000000000000000000000000000000000000000" + # issuer_fragment: "key-0" diff --git a/agent_issuance/src/credential/aggregate.rs b/agent_issuance/src/credential/aggregate.rs index 3ea61a48..5e77ec62 100644 --- a/agent_issuance/src/credential/aggregate.rs +++ b/agent_issuance/src/credential/aggregate.rs @@ -1,5 +1,6 @@ use agent_secret_manager::services::SecretManagerServices; use agent_shared::config; +use agent_shared::config::config_2; use agent_shared::metadata::Display; use async_trait::async_trait; use cqrs_es::Aggregate; @@ -89,17 +90,12 @@ impl Aggregate for Credential { None => unreachable!("The display.name parameter is not set"), }; - let issuer: Profile = match config!("url", String).ok().and_then(|url| { - ProfileBuilder::default() - .id(url) - .type_("Profile") - .name(name) - .try_into() - .ok() - }) { - Some(issuer) => issuer, - None => unreachable!("The `AGENT_CONFIG_URL` environment variable is not set."), - }; + let issuer: Profile = ProfileBuilder::default() + .id(config_2().url) + .type_("Profile") + .name(name) + .try_into() + .expect("Could not build issuer profile"); let mut credential_types: Vec = type_.clone(); diff --git a/agent_verification/src/authorization_request/aggregate.rs b/agent_verification/src/authorization_request/aggregate.rs index 67e43bf2..58126dd8 100644 --- a/agent_verification/src/authorization_request/aggregate.rs +++ b/agent_verification/src/authorization_request/aggregate.rs @@ -4,6 +4,7 @@ use crate::{ services::VerificationServices, }; use agent_shared::config; +use agent_shared::config::config_2; use async_trait::async_trait; use cqrs_es::Aggregate; use jsonwebtoken::Algorithm; @@ -51,7 +52,7 @@ impl Aggregate for AuthorizationRequest { .await .unwrap(); - let url = config!("url", String).unwrap(); + let url = config_2().url; let request_uri = format!("{url}/request/{state}").parse().unwrap(); let redirect_uri = format!("{url}/redirect").parse::().unwrap(); From 3f40a05db9cd6ee84f032a38bf7bfcf206ffa3a4 Mon Sep 17 00:00:00 2001 From: Daniel Mader Date: Fri, 12 Jul 2024 10:13:04 +0200 Subject: [PATCH 11/37] refactor: `secret_manager` config --- .env.example | 10 +++++----- agent_secret_manager/src/lib.rs | 20 ++++++++++---------- agent_secret_manager/src/services.rs | 20 +++++++++++--------- agent_shared/src/config.rs | 10 ++++++++++ 4 files changed, 36 insertions(+), 24 deletions(-) diff --git a/.env.example b/.env.example index 15a6c00c..f1b8f930 100644 --- a/.env.example +++ b/.env.example @@ -3,8 +3,8 @@ AGENT__EVENT_STORE__TYPE=postgres AGENT__EVENT_STORE__CONNECTION_STRING="postgresql://demo_user:demo_pass@localhost:5432/demo" AGENT__URL="http://192.168.1.234:3033" -AGENT_STRONGHOLD_PATH="agent_secret_manager/tests/res/test.stronghold" -AGENT_STRONGHOLD_PASSWORD="secure_password" -AGENT_ISSUER_KEY_ID="9O66nzWqYYy1LmmiOudOlh2SMIaUWoTS" -AGENT_ISSUER_DID="did:iota:rms:0x42ad588322e58b3c07aa39e4948d021ee17ecb5747915e9e1f35f028d7ecaf90" -AGENT_ISSUER_FRAGMENT="bQKQRzaop7CgEvqVq8UlgLGsdF-R-hnLFkKFZqW2VN0" +AGENT__SECRET_MANAGER__STRONGHOLD_PATH="agent_secret_manager/tests/res/test.stronghold" +AGENT__SECRET_MANAGER__STRONGHOLD_PASSWORD="secure_password" +AGENT__SECRET_MANAGER__ISSUER_KEY_ID="9O66nzWqYYy1LmmiOudOlh2SMIaUWoTS" +AGENT__SECRET_MANAGER__ISSUER_DID="did:iota:rms:0x42ad588322e58b3c07aa39e4948d021ee17ecb5747915e9e1f35f028d7ecaf90" +AGENT__SECRET_MANAGER__ISSUER_FRAGMENT="bQKQRzaop7CgEvqVq8UlgLGsdF-R-hnLFkKFZqW2VN0" diff --git a/agent_secret_manager/src/lib.rs b/agent_secret_manager/src/lib.rs index e18325aa..547d1cf1 100644 --- a/agent_secret_manager/src/lib.rs +++ b/agent_secret_manager/src/lib.rs @@ -1,4 +1,4 @@ -use agent_shared::config; +use agent_shared::config::config_2; use did_manager::SecretManager; pub mod aggregate; @@ -9,17 +9,17 @@ pub mod subject; // TODO: find better solution for this pub async fn secret_manager() -> SecretManager { - let snapshot_path = config!("stronghold_path", String); - let password = config!("stronghold_password", String); - let key_id = config!("issuer_key_id", String); - let issuer_did = config!("issuer_did", String); - let issuer_fragment = config!("issuer_fragment", String); + let snapshot_path = config_2().secret_manager.stronghold_path; + let password = config_2().secret_manager.stronghold_password; + let key_id = config_2().secret_manager.issuer_key_id; + let issuer_did = config_2().secret_manager.issuer_did; + let issuer_fragment = config_2().secret_manager.issuer_fragment; - match (snapshot_path, password, key_id, issuer_did.ok(), issuer_fragment.ok()) { - (Ok(snapshot_path), Ok(password), Ok(key_id), issuer_did, issuer_fragment) => { + match (snapshot_path, password, key_id, issuer_did, issuer_fragment) { + (snapshot_path, password, Some(key_id), issuer_did, issuer_fragment) => { SecretManager::load(snapshot_path, password, key_id, issuer_did, issuer_fragment).await.unwrap() } - (Ok(snapshot_path), Ok(password), _, _, _) => SecretManager::generate(snapshot_path, password).await.unwrap(), - _ => panic!("Unable to load or generate `SecretManager`. Please make sure to set both `AGENT_SECRET_MANAGER_STRONGHOLD_PATH` and `AGENT_SECRET_MANAGER_STRONGHOLD_PASSWORD` environment variables."), + (snapshot_path, password, None, _, _) => SecretManager::generate(snapshot_path, password).await.unwrap(), + _ => panic!("Unable to load or generate `SecretManager`. Please make sure to set both `AGENT__SECRET_MANAGER__STRONGHOLD_PATH` and `AGENT__SECRET_MANAGER__STRONGHOLD_PASSWORD` environment variables."), } } diff --git a/agent_secret_manager/src/services.rs b/agent_secret_manager/src/services.rs index e9a46a24..1eeb1e16 100644 --- a/agent_secret_manager/src/services.rs +++ b/agent_secret_manager/src/services.rs @@ -28,16 +28,18 @@ impl SecretManagerServices { } pub async fn init(&mut self) -> Result<(), std::io::Error> { - let snapshot_path = config!("stronghold_path", String).unwrap(); - let password = config!("stronghold_password", String).unwrap(); - let key_id = config!("issuer_key_id", String).unwrap(); - let issuer_did = config!("issuer_did", String); - let issuer_fragment = config!("issuer_fragment", String); + let snapshot_path = config_2().secret_manager.stronghold_path; + let password = config_2().secret_manager.stronghold_password; + let key_id = config_2() + .secret_manager + .issuer_key_id + .expect("Missing configuration: secret_manager.issuer_key_id"); + let issuer_did = config_2().secret_manager.issuer_did; + let issuer_fragment = config_2().secret_manager.issuer_fragment; - let secret_manager = - SecretManager::load(snapshot_path, password, key_id, issuer_did.ok(), issuer_fragment.ok()) - .await - .unwrap(); + let secret_manager = SecretManager::load(snapshot_path, password, key_id, issuer_did, issuer_fragment) + .await + .unwrap(); self.subject.replace(Subject { secret_manager }); diff --git a/agent_shared/src/config.rs b/agent_shared/src/config.rs index 9b124f9c..705a92c7 100644 --- a/agent_shared/src/config.rs +++ b/agent_shared/src/config.rs @@ -13,6 +13,7 @@ pub struct ApplicationConfiguration { pub did_methods: HashMap, pub external_server_response_timeout_ms: Option, pub domain_linkage_enabled: bool, + pub secret_manager: SecretManagerConfig, } #[derive(Debug, Deserialize, Clone, Default)] @@ -43,6 +44,15 @@ pub struct EventStorePostgresConfig { pub connection_string: String, } +#[derive(Debug, Deserialize, Clone)] +pub struct SecretManagerConfig { + pub stronghold_path: String, + pub stronghold_password: String, + pub issuer_key_id: Option, + pub issuer_did: Option, + pub issuer_fragment: Option, +} + // pub enum DidMethod { // #[serde(rename = "did:jwk")] // Jwk, From 3763a04728f60ce2d85ba7e8b502971e1652d8de Mon Sep 17 00:00:00 2001 From: Daniel Mader Date: Fri, 12 Jul 2024 10:22:00 +0200 Subject: [PATCH 12/37] refactor: `credential_configurations` config --- agent_issuance/src/startup_commands.rs | 13 ++++++------- agent_secret_manager/src/services.rs | 1 - agent_shared/src/config.rs | 3 +++ agent_shared/src/issuance.rs | 5 ----- 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/agent_issuance/src/startup_commands.rs b/agent_issuance/src/startup_commands.rs index e0563028..e2340ce8 100644 --- a/agent_issuance/src/startup_commands.rs +++ b/agent_issuance/src/startup_commands.rs @@ -1,9 +1,11 @@ -use crate::server_config::command::ServerConfigCommand; -use agent_shared::{config, issuance::ServerConfig, metadata::Metadata, url_utils::UrlAppendHelpers}; +use agent_shared::config::config_2; +use agent_shared::{metadata::Metadata, url_utils::UrlAppendHelpers}; use oid4vci::credential_issuer::{ authorization_server_metadata::AuthorizationServerMetadata, credential_issuer_metadata::CredentialIssuerMetadata, }; +use crate::server_config::command::ServerConfigCommand; + /// Returns the startup commands for the application. pub fn startup_commands(host: url::Url, metadata: &Metadata) -> Vec { vec![load_server_metadata(host, metadata), create_credentials_supported()] @@ -31,13 +33,10 @@ pub fn load_server_metadata(base_url: url::Url, metadata: &Metadata) -> ServerCo } pub fn create_credentials_supported() -> ServerConfigCommand { - let server_config = config!("server_config", ServerConfig) - .expect("Failed due to missing `server_config` in `issuance-config.yml` file"); - - let credential_configuration = server_config + let credential_configuration = config_2() .credential_configurations .first() - .expect("Failed due to empty `credential_configurations` array in `issuance-config.yml` file") + .expect("No credential_configurations found") .clone(); ServerConfigCommand::AddCredentialConfiguration { diff --git a/agent_secret_manager/src/services.rs b/agent_secret_manager/src/services.rs index 1eeb1e16..f3bee789 100644 --- a/agent_secret_manager/src/services.rs +++ b/agent_secret_manager/src/services.rs @@ -1,5 +1,4 @@ use crate::subject::Subject; -use agent_shared::config; use agent_shared::config::config_2; use anyhow::Result; use did_manager::SecretManager; diff --git a/agent_shared/src/config.rs b/agent_shared/src/config.rs index 705a92c7..8e0d9960 100644 --- a/agent_shared/src/config.rs +++ b/agent_shared/src/config.rs @@ -3,6 +3,8 @@ use serde::Deserialize; use std::{collections::HashMap, sync::Mutex}; use tracing::info; +use crate::issuance::CredentialConfiguration; + #[derive(Debug, Deserialize, Clone)] pub struct ApplicationConfiguration { pub log_format: LogFormat, @@ -14,6 +16,7 @@ pub struct ApplicationConfiguration { pub external_server_response_timeout_ms: Option, pub domain_linkage_enabled: bool, pub secret_manager: SecretManagerConfig, + pub credential_configurations: Vec, } #[derive(Debug, Deserialize, Clone, Default)] diff --git a/agent_shared/src/issuance.rs b/agent_shared/src/issuance.rs index bec6e87e..1519db05 100644 --- a/agent_shared/src/issuance.rs +++ b/agent_shared/src/issuance.rs @@ -10,11 +10,6 @@ pub struct CredentialConfiguration { pub display: Vec, } -#[derive(Deserialize, Serialize, Debug)] -pub struct ServerConfig { - pub credential_configurations: Vec, -} - #[cfg(feature = "test_utils")] use once_cell::sync::Lazy; From 29d2f187bd47e5bcdfd129848eea8c0c0590d3cb Mon Sep 17 00:00:00 2001 From: Daniel Mader Date: Fri, 12 Jul 2024 18:23:15 +0200 Subject: [PATCH 13/37] refactor: rename `DidMethodOptions`, add config for `signing_algorithms_supported` --- agent_application/example-config.yaml | 29 +++++++++++++------ agent_application/src/main.rs | 5 ++-- agent_issuance/src/server_config/aggregate.rs | 23 ++++++++++----- agent_shared/src/config.rs | 6 ++-- 4 files changed, 41 insertions(+), 22 deletions(-) diff --git a/agent_application/example-config.yaml b/agent_application/example-config.yaml index 6187e257..476de05c 100644 --- a/agent_application/example-config.yaml +++ b/agent_application/example-config.yaml @@ -29,17 +29,28 @@ did_methods: domain_linkage_enabled: false +signing_algorithms_supported: + eddsa: + enabled: true + +# TODO: required to be stated explicitly? +# vp_formats: +# jwt_vc_json: +# enabled: true +# jwt_vp_json: +# enabled: true + # List of supported signing algorithms for ID Tokens, Request Objects, and Verifiable Credentials. As of now, UniCore # only supports EdDSA. -signing_algorithms_supported: &signing_algorithms_supported - - EdDSA -id_token_signing_alg_values_supported: *signing_algorithms_supported -request_object_signing_alg_values_supported: *signing_algorithms_supported -vp_formats: - jwt_vc_json: - alg: *signing_algorithms_supported - jwt_vp_json: - alg: *signing_algorithms_supported +# signing_algorithms_supported: &signing_algorithms_supported +# - EdDSA +# id_token_signing_alg_values_supported: *signing_algorithms_supported +# request_object_signing_alg_values_supported: *signing_algorithms_supported +# vp_formats: +# jwt_vc_json: +# alg: *signing_algorithms_supported +# jwt_vp_json: +# alg: *signing_algorithms_supported # agent_event_publisher_http # Configure here to which events will be dispatched to a specific `target_url`. In the example below, the diff --git a/agent_application/src/main.rs b/agent_application/src/main.rs index 7a6dabe7..7423de78 100644 --- a/agent_application/src/main.rs +++ b/agent_application/src/main.rs @@ -3,8 +3,7 @@ use agent_event_publisher_http::EventPublisherHttp; use agent_issuance::{startup_commands::startup_commands, state::initialize}; use agent_secret_manager::{secret_manager, subject::Subject}; use agent_shared::{ - config, - config::{config_2, DidMethodOptions, LogFormat}, + config::{config_2, LogFormat, ToggleOptions}, domain_linkage::create_did_configuration_resource, metadata::{load_metadata, Metadata}, }; @@ -88,7 +87,7 @@ async fn main() -> io::Result<()> { let enable_did_web = config_2() .did_methods .get("did_web") - .unwrap_or(&DidMethodOptions::default()) + .unwrap_or(&ToggleOptions::default()) .enabled; let did_document = if enable_did_web { diff --git a/agent_issuance/src/server_config/aggregate.rs b/agent_issuance/src/server_config/aggregate.rs index dca15dd3..60e08b04 100644 --- a/agent_issuance/src/server_config/aggregate.rs +++ b/agent_issuance/src/server_config/aggregate.rs @@ -1,6 +1,3 @@ -use std::collections::HashMap; - -use agent_shared::config; use agent_shared::config::config_2; use async_trait::async_trait; use cqrs_es::Aggregate; @@ -12,6 +9,7 @@ use oid4vci::credential_issuer::{ use oid4vci::proof::KeyProofMetadata; use oid4vci::ProofType; use serde::{Deserialize, Serialize}; +use std::collections::HashMap; use tracing::info; use crate::server_config::command::ServerConfigCommand; @@ -68,21 +66,30 @@ impl Aggregate for ServerConfig { .map(|did_method| did_method.replace("_", ":")) .collect(); - let credential_signing_alg_values_supported = - config!("signing_algorithms_supported", Vec).unwrap_or_default(); + let signing_algorithms_supported: Vec = config_2() + .signing_algorithms_supported + .iter() + .filter(|(_, options)| options.enabled) + .map(|(alg, _)| alg.clone()) + .collect(); let proof_types_supported = HashMap::from_iter([( ProofType::Jwt, KeyProofMetadata { - proof_signing_alg_values_supported: config!("signing_algorithms_supported", Vec) - .unwrap_or_default(), + proof_signing_alg_values_supported: signing_algorithms_supported.clone(), }, )]); let credential_configuration_object = CredentialConfigurationsSupportedObject { credential_format: credential_configuration.credential_format_with_parameters, cryptographic_binding_methods_supported, - credential_signing_alg_values_supported, + credential_signing_alg_values_supported: signing_algorithms_supported + .into_iter() + .map(|algorithm| match algorithm { + jsonwebtoken::Algorithm::EdDSA => "EdDSA".to_string(), + _ => unimplemented!("Unsupported algorithm: {:?}", algorithm), + }) + .collect(), proof_types_supported, display: credential_configuration.display, ..Default::default() diff --git a/agent_shared/src/config.rs b/agent_shared/src/config.rs index 8e0d9960..d6ebd036 100644 --- a/agent_shared/src/config.rs +++ b/agent_shared/src/config.rs @@ -12,11 +12,12 @@ pub struct ApplicationConfiguration { pub url: String, pub base_path: Option, pub cors_enabled: Option, - pub did_methods: HashMap, + pub did_methods: HashMap, pub external_server_response_timeout_ms: Option, pub domain_linkage_enabled: bool, pub secret_manager: SecretManagerConfig, pub credential_configurations: Vec, + pub signing_algorithms_supported: HashMap, } #[derive(Debug, Deserialize, Clone, Default)] @@ -64,8 +65,9 @@ pub struct SecretManagerConfig { // IotaRms, // } +/// Generic options that add an "enabled" field and a "preferred" field (optional) to a configuration. #[derive(Debug, Deserialize, Default, Clone)] -pub struct DidMethodOptions { +pub struct ToggleOptions { pub enabled: bool, pub preferred: Option, } From 6053fc84174e2082d52d68c867b42730f2426652 Mon Sep 17 00:00:00 2001 From: Daniel Mader Date: Fri, 12 Jul 2024 18:32:18 +0200 Subject: [PATCH 14/37] refactor: determine default DID method --- agent_verification/src/services.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/agent_verification/src/services.rs b/agent_verification/src/services.rs index eb096fa5..9c632413 100644 --- a/agent_verification/src/services.rs +++ b/agent_verification/src/services.rs @@ -1,11 +1,10 @@ -use agent_shared::metadata::Metadata; +use agent_shared::{config::config_2, metadata::Metadata}; use jsonwebtoken::Algorithm; use oid4vc_core::{client_metadata::ClientMetadataResource, Subject}; use oid4vc_manager::RelyingPartyManager; use oid4vp::ClaimFormatProperty; use serde_json::json; use std::{collections::HashMap, sync::Arc}; -use tracing::info; /// Verification services. This struct is used to generate authorization requests and validate authorization responses. pub struct VerificationServices { @@ -17,12 +16,17 @@ pub struct VerificationServices { impl VerificationServices { pub fn new(verifier: Arc, metadata: &Metadata) -> Self { - // let default_did_method = metadata - // .subject_syntax_types_supported - // .first() - // .expect("`subject_syntax_types_supported` must contain at least one element.") - // .to_string(); - let default_did_method = "did:key".to_string(); + let default_did_method = config_2() + .did_methods + .iter() + .filter(|(_, v)| v.preferred.unwrap_or(false)) + .map(|(k, _)| k.clone()) + .collect::>() + // TODO: throw error if more than one preferred DID method is found + .first() + .unwrap() + .to_owned() + .replace("_", ":"); let client_name = metadata.display.first().as_ref().map(|display| display.name.clone()); From 18800f412302a1386539d9f173c220f35eebe014 Mon Sep 17 00:00:00 2001 From: Daniel Mader Date: Fri, 12 Jul 2024 18:56:00 +0200 Subject: [PATCH 15/37] refactor: remove comments, load env variables --- agent_application/example-config.yaml | 22 +--------------------- agent_shared/src/config.rs | 2 ++ 2 files changed, 3 insertions(+), 21 deletions(-) diff --git a/agent_application/example-config.yaml b/agent_application/example-config.yaml index 476de05c..a4c3806a 100644 --- a/agent_application/example-config.yaml +++ b/agent_application/example-config.yaml @@ -11,21 +11,13 @@ external_server_response_timeout_ms: 500 did_methods: did_jwk: enabled: true - # preferred: true + preferred: true did_key: enabled: true did_iota_rms: enabled: true did_web: enabled: true - preferred: true - -# List of Subject Syntax Types (DID Methods) supported by UniCore. The first item in the list is the default Syntax Type, -# meaning UniCore will use its identifier belonging to this type by default. -# subject_syntax_types_supported: -# - did:jwk -# - did:key -# - did:iota:rms domain_linkage_enabled: false @@ -40,18 +32,6 @@ signing_algorithms_supported: # jwt_vp_json: # enabled: true -# List of supported signing algorithms for ID Tokens, Request Objects, and Verifiable Credentials. As of now, UniCore -# only supports EdDSA. -# signing_algorithms_supported: &signing_algorithms_supported -# - EdDSA -# id_token_signing_alg_values_supported: *signing_algorithms_supported -# request_object_signing_alg_values_supported: *signing_algorithms_supported -# vp_formats: -# jwt_vc_json: -# alg: *signing_algorithms_supported -# jwt_vp_json: -# alg: *signing_algorithms_supported - # agent_event_publisher_http # Configure here to which events will be dispatched to a specific `target_url`. In the example below, the # `EventPublisherHttp` will listen for the `SIOPv2AuthorizationResponseVerified` event which is part of the `connection` diff --git a/agent_shared/src/config.rs b/agent_shared/src/config.rs index d6ebd036..14e35f44 100644 --- a/agent_shared/src/config.rs +++ b/agent_shared/src/config.rs @@ -76,6 +76,8 @@ static CONFIG: Mutex> = Mutex::new(None); impl ApplicationConfiguration { pub fn new() -> Result { + dotenvy::dotenv().ok(); + info!("Environment variables loaded."); info!("Loading application configuration ..."); let config = config::Config::builder() .add_source(config::File::with_name("agent_application/example-config.yaml")) From 094957377c9c433ca9134ad0a6873cb898390e00 Mon Sep 17 00:00:00 2001 From: Daniel Mader Date: Fri, 12 Jul 2024 23:36:43 +0200 Subject: [PATCH 16/37] WIP: refactor `event_publishers` config --- agent_application/example-config.yaml | 24 ++-- agent_application/src/main.rs | 4 +- agent_event_publisher_http/Cargo.toml | 1 + agent_event_publisher_http/src/lib.rs | 179 +++++++++++++++++++------- agent_shared/Cargo.toml | 4 +- agent_shared/src/config.rs | 67 +++++++--- agent_shared/src/metadata.rs | 88 ++++++++----- 7 files changed, 263 insertions(+), 104 deletions(-) diff --git a/agent_application/example-config.yaml b/agent_application/example-config.yaml index a4c3806a..10ae2b79 100644 --- a/agent_application/example-config.yaml +++ b/agent_application/example-config.yaml @@ -36,16 +36,24 @@ signing_algorithms_supported: # Configure here to which events will be dispatched to a specific `target_url`. In the example below, the # `EventPublisherHttp` will listen for the `SIOPv2AuthorizationResponseVerified` event which is part of the `connection` # aggregate. The events will be dispatched to the `https://my-domain.example.org/event-subscriber` endpoint. -event_publisher: +event_publishers: http: enabled: false - target_url: &target_url "https://my-domain.example.org/event-subscriber" - publishers: - connection: - { - target_url: *target_url, - target_events: [SIOPv2AuthorizationResponseVerified], - } + target_url: "https://my-domain.example.org/event-subscriber" + events: + [ + SIOPv2AuthorizationResponseVerified, + CredentialSigned, + CredentialOfferCreated, + ] + # publishers: + # - "connection" + # - "foobar" + # connection: + # { + # target_url: *target_url, + # target_events: [SIOPv2AuthorizationResponseVerified], + # } # These display parameters are interpreted by identity wallets. display: diff --git a/agent_application/src/main.rs b/agent_application/src/main.rs index 7423de78..934462a5 100644 --- a/agent_application/src/main.rs +++ b/agent_application/src/main.rs @@ -19,8 +19,6 @@ use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; #[tokio::main] async fn main() -> io::Result<()> { - let metadata: Metadata = load_metadata(); - let tracing_subscriber = tracing_subscriber::registry() // Set the default logging level to `info`, equivalent to `RUST_LOG=info` .with(tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into())); @@ -34,6 +32,8 @@ async fn main() -> io::Result<()> { // _ => tracing_subscriber.with(tracing_subscriber::fmt::layer()).init(), } + let metadata: Metadata = load_metadata(); + let verification_services = Arc::new(VerificationServices::new( Arc::new(Subject { secret_manager: secret_manager().await, diff --git a/agent_event_publisher_http/Cargo.toml b/agent_event_publisher_http/Cargo.toml index a7e4f590..b6c39a2e 100644 --- a/agent_event_publisher_http/Cargo.toml +++ b/agent_event_publisher_http/Cargo.toml @@ -20,6 +20,7 @@ rustls = { version = "0.23", default-features = false, features = [ ] } reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] } serde.workspace = true +serde_json.workspace = true serde_with.workspace = true serde_yaml.workspace = true tokio.workspace = true diff --git a/agent_event_publisher_http/src/lib.rs b/agent_event_publisher_http/src/lib.rs index c11be6b9..77cc8768 100644 --- a/agent_event_publisher_http/src/lib.rs +++ b/agent_event_publisher_http/src/lib.rs @@ -1,6 +1,11 @@ +use std::collections::HashMap; + use agent_issuance::{ - credential::aggregate::Credential, offer::aggregate::Offer, server_config::aggregate::ServerConfig, + credential::aggregate::Credential, + offer::{aggregate::Offer, event}, + server_config::aggregate::ServerConfig, }; +use agent_shared::config::{config_2, Event}; use agent_store::{ AuthorizationRequestEventPublisher, ConnectionEventPublisher, CredentialEventPublisher, EventPublisher, OfferEventPublisher, ServerConfigEventPublisher, @@ -29,58 +34,140 @@ pub struct EventPublisherHttp { pub authorization_request: Option>, } +// We need this to map events to aggregates +#[derive(PartialEq)] +enum DomainAggregate { + ServerConfig, + Credential, + Offer, + Connection, + AuthorizationRequest, +} + +// We need this to map events to aggregates +// #[derive(Eq, PartialEq, Hash)] +// enum Event { +// // credential +// UnsignedCredentialCreated, +// SignedCredentialCreated, +// CredentialSigned, +// // offer +// CredentialOfferCreated, +// CredentialAdded, +// FormUrlEncodedCredentialOfferCreated, +// TokenResponseCreated, +// CredentialRequestVerified, +// CredentialResponseCreated, +// } + impl EventPublisherHttp { pub fn load() -> anyhow::Result { - #[cfg(feature = "test_utils")] - let mut config = TEST_EVENT_PUBLISHER_HTTP_CONFIG - .lock() - .unwrap() - .as_ref() - .unwrap() - .clone(); - #[cfg(not(feature = "test_utils"))] - let mut config: serde_yaml::Value = { - match std::fs::File::open("agent_event_publisher_http/config.yml") { - Ok(config_file) => serde_yaml::from_reader(config_file)?, - // If the config file does not exist, return an empty config. - Err(_) => serde_yaml::Value::Null, - } - }; - let config = agent_shared::config::config("AGENT_EVENT_PUBLISHER_HTTP"); - - let event_publishers = config.get_table("event_publisher").unwrap_or_default(); - let event_publisher_http = event_publishers - .get("http") - .unwrap() - .clone() - .into_table() - .unwrap_or_default(); + // #[cfg(feature = "test_utils")] + // let mut config = TEST_EVENT_PUBLISHER_HTTP_CONFIG + // .lock() + // .unwrap() + // .as_ref() + // .unwrap() + // .clone(); + // #[cfg(not(feature = "test_utils"))] + // let mut config: serde_yaml::Value = { + // match std::fs::File::open("agent_event_publisher_http/config.yml") { + // Ok(config_file) => serde_yaml::from_reader(config_file)?, + // // If the config file does not exist, return an empty config. + // Err(_) => serde_yaml::Value::Null, + // } + // }; + // let config = agent_shared::config::config("AGENT_EVENT_PUBLISHER_HTTP"); + + let event_publishers = config_2().event_publishers.unwrap(); + let event_publisher_http = event_publishers.http.unwrap(); + + // let event_publishers = config.get_table("event_publishers").unwrap_or_default(); + // let event_publisher_http = event_publishers + // .get("http") + // .unwrap() + // .clone() + // .into_table() + // .unwrap_or_default(); info!("event_publisher_http: {:?}", event_publisher_http); - let config = if event_publisher_http - .get("enabled") - .unwrap() - .clone() - .into_bool() - .unwrap_or_default() - { - let publishers: serde_yaml::Value = event_publisher_http - .get("publishers") - .unwrap() - .clone() - .try_deserialize() - .unwrap(); - publishers - } else { - serde_yaml::Value::Null + if event_publisher_http.enabled { + // event_publisher_http.publishers + } + + // TODO: map events to aggregates + // let mapping = HashMap::::new(); + let mapping: HashMap = HashMap::from([ + // credential + (Event::UnsignedCredentialCreated, DomainAggregate::Credential), + (Event::SignedCredentialCreated, DomainAggregate::Credential), + (Event::CredentialSigned, DomainAggregate::Credential), + // offer + (Event::CredentialOfferCreated, DomainAggregate::Offer), + // connection + (Event::SIOPv2AuthorizationResponseVerified, DomainAggregate::Connection), + ]); + + let credential_events: Vec = event_publisher_http + .events + .iter() + .filter(|e| mapping.get(e).unwrap() == &DomainAggregate::Credential) + .map(|e| serde_json::to_string(e).unwrap()) + .map(|e| e.trim_matches('"').to_string()) // TODO: properly serialize to String + .collect(); + info!("credential_events: {:?}", credential_events); + + let offer_events: Vec = event_publisher_http + .events + .iter() + .filter(|e| mapping.get(e).unwrap() == &DomainAggregate::Offer) + .map(|e| serde_json::to_string(e).unwrap()) + .map(|e| e.trim_matches('"').to_string()) // TODO: properly serialize to String + .collect(); + info!("offer_events: {:?}", offer_events); + + let event_publisher: EventPublisherHttp = EventPublisherHttp { + server_config: None, + credential: Some(AggregateEventPublisherHttp::new( + event_publisher_http.target_url.clone(), + credential_events, + )), + offer: Some(AggregateEventPublisherHttp::new( + event_publisher_http.target_url.clone(), + offer_events, + )), + connection: None, + authorization_request: None, }; - serde_yaml::from_value(config) - .map_err(Into::into) - .inspect(|event_publisher| { - info!("Loaded HTTP event publisher: {:?}", event_publisher); - }) + info!("Loaded HTTP event publisher: {:?}", event_publisher); + + Ok(event_publisher) + + // let config = if event_publisher_http + // .get("enabled") + // .unwrap() + // .clone() + // .into_bool() + // .unwrap_or_default() + // { + // let publishers: serde_yaml::Value = event_publisher_http + // .get("publishers") + // .unwrap() + // .clone() + // .try_deserialize() + // .unwrap(); + // publishers + // } else { + // serde_yaml::Value::Null + // }; + + // serde_yaml::from_value(config) + // .map_err(Into::into) + // .inspect(|event_publisher| { + // info!("Loaded HTTP event publisher: {:?}", event_publisher); + // }) } } diff --git a/agent_shared/Cargo.toml b/agent_shared/Cargo.toml index 7cabb9e9..234a7146 100644 --- a/agent_shared/Cargo.toml +++ b/agent_shared/Cargo.toml @@ -23,7 +23,7 @@ jsonwebtoken.workspace = true oid4vc-core.workspace = true oid4vci.workspace = true oid4vp.workspace = true -once_cell = { version = "1.19", optional = true } +once_cell = { version = "1.19" } rand = "0.8" serde.workspace = true serde_json.workspace = true @@ -36,4 +36,4 @@ url.workspace = true [features] local_development = [] -test_utils = ["once_cell"] +test_utils = [] diff --git a/agent_shared/src/config.rs b/agent_shared/src/config.rs index 14e35f44..c1810c88 100644 --- a/agent_shared/src/config.rs +++ b/agent_shared/src/config.rs @@ -1,9 +1,10 @@ use config::ConfigError; -use serde::Deserialize; -use std::{collections::HashMap, sync::Mutex}; -use tracing::info; +use once_cell::sync::OnceCell; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use tracing::{debug, info}; -use crate::issuance::CredentialConfiguration; +use crate::{issuance::CredentialConfiguration, metadata::Display}; #[derive(Debug, Deserialize, Clone)] pub struct ApplicationConfiguration { @@ -18,6 +19,8 @@ pub struct ApplicationConfiguration { pub secret_manager: SecretManagerConfig, pub credential_configurations: Vec, pub signing_algorithms_supported: HashMap, + pub display: Vec, + pub event_publishers: Option, } #[derive(Debug, Deserialize, Clone, Default)] @@ -39,7 +42,7 @@ pub struct EventStoreConfig { #[serde(rename_all = "snake_case")] pub enum EventStoreType { InMemory, - // Postgres(EventStorePostgresConfig), // <== "config-rs" panicks with "unreachable code" + // Postgres(EventStorePostgresConfig), // <== TODO: "config-rs" panicks with "unreachable code", other solution? Postgres, } @@ -57,6 +60,44 @@ pub struct SecretManagerConfig { pub issuer_fragment: Option, } +#[derive(Debug, Deserialize, Clone)] +pub struct EventPublishers { + pub http: Option, +} + +#[derive(Debug, Deserialize, Clone)] +pub struct EventPublisherHttp { + pub enabled: bool, + pub target_url: String, + pub events: Vec, +} + +/// All events that can be published. +#[derive(Debug, Serialize, Deserialize, Clone, Hash, Eq, PartialEq)] +pub enum Event { + // credential + UnsignedCredentialCreated, + SignedCredentialCreated, + CredentialSigned, + // offer + CredentialOfferCreated, + CredentialAdded, + FormUrlEncodedCredentialOfferCreated, + TokenResponseCreated, + CredentialRequestVerified, + CredentialResponseCreated, + // server_config + ServerMetadataLoaded, + CredentialConfigurationAdded, + // authorization_request + AuthorizationRequestCreated, + FormUrlEncodedAuthorizationRequestCreated, + AuthorizationRequestObjectSigned, + // connection + SIOPv2AuthorizationResponseVerified, + OID4VPAuthorizationResponseVerified, +} + // pub enum DidMethod { // #[serde(rename = "did:jwk")] // Jwk, @@ -72,7 +113,7 @@ pub struct ToggleOptions { pub preferred: Option, } -static CONFIG: Mutex> = Mutex::new(None); +static CONFIG: OnceCell = OnceCell::new(); impl ApplicationConfiguration { pub fn new() -> Result { @@ -87,17 +128,11 @@ impl ApplicationConfiguration { } } -/// Loads the configuration or returns it, if it has already been loaded. +/// Returns the application configuration or loads it, if it hasn't been loaded already. pub fn config_2() -> ApplicationConfiguration { - info!("config_2()"); - // CONFIG - // .lock() - // .unwrap() - // .get_or_insert_with(|| ApplicationConfiguration::new().unwrap()) - // .clone() - let config = ApplicationConfiguration::new().unwrap(); - info!("{:#?}", config); - config + // info!("config_2()"); + CONFIG.get_or_init(|| ApplicationConfiguration::new().unwrap()).clone() + // TODO: or return -> &'static ApplicationConfiguration, so we don't need to clone on every call } /// Read environment variables diff --git a/agent_shared/src/metadata.rs b/agent_shared/src/metadata.rs index fe973ed6..6e854423 100644 --- a/agent_shared/src/metadata.rs +++ b/agent_shared/src/metadata.rs @@ -1,10 +1,9 @@ -use std::collections::HashMap; - use jsonwebtoken::Algorithm; use oid4vc_core::SubjectSyntaxType; use oid4vp::ClaimFormatDesignation; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; +use std::{collections::HashMap, str::FromStr}; use tracing::info; use url::Url; @@ -46,38 +45,67 @@ pub struct Metadata { #[cfg(feature = "test_utils")] pub static TEST_METADATA: std::sync::Mutex> = std::sync::Mutex::new(None); +// impl Metadata { +// pub fn new() -> Result { +// info!("Loading application metadata ..."); +// let config = config::Config::builder() +// .add_source(config::File::with_name("agent_application/example-config.yaml")) +// .add_source(config::Environment::with_prefix("AGENT").separator("__")) +// .build()?; +// config.try_deserialize() +// } +// } + pub fn load_metadata() -> Metadata { - #[cfg(feature = "test_utils")] - let mut config = TEST_METADATA.lock().unwrap().as_ref().unwrap().clone(); - #[cfg(not(feature = "test_utils"))] - let mut config: serde_yaml::Value = { - match std::fs::File::open("agent_application/config.yml") { - Ok(config_file) => serde_yaml::from_reader(config_file).unwrap(), - // If the config file does not exist, return an empty config. - Err(_) => serde_yaml::Value::Null, - } - }; - let mut config: serde_yaml::Value = crate::config::config("AGENT_APPLICATION").try_deserialize().unwrap(); - - info!("config: {:?}", config); - - let supported_algorithms: serde_yaml::Value = vec!["EdDSA".to_string()].into(); - if config["signing_algorithms_supported"] != supported_algorithms { - unimplemented!( - "\n{}\nOnly the `EdDSA` signing algorithm is supported", - serde_yaml::to_string(&config["signing_algorithms_supported"]).unwrap() - ) - } - - config.apply_merge().unwrap(); - - info!("config: {:?}", config); - - let metadata = - serde_yaml::from_value::(config).expect("Invalid metadata in `agent_application/config.yml` file"); + // #[cfg(feature = "test_utils")] + // let mut config = TEST_METADATA.lock().unwrap().as_ref().unwrap().clone(); + // #[cfg(not(feature = "test_utils"))] + // let mut config: serde_yaml::Value = { + // match std::fs::File::open("agent_application/config.yml") { + // Ok(config_file) => serde_yaml::from_reader(config_file).unwrap(), + // // If the config file does not exist, return an empty config. + // Err(_) => serde_yaml::Value::Null, + // } + // }; + // let mut config: serde_yaml::Value = crate::config::config("AGENT_APPLICATION").try_deserialize().unwrap(); + + // info!("config: {:?}", config); + let mut metadata = Metadata::default(); + // metadata.subject_syntax_types_supported = config_2().did_methods.keys().into_iter().map(|k| k.clone()).collect(); + + // let supported_algorithms: serde_yaml::Value = vec!["EdDSA".to_string()].into(); + // if config["signing_algorithms_supported"] != supported_algorithms { + // unimplemented!( + // "\n{}\nOnly the `EdDSA` signing algorithm is supported", + // serde_yaml::to_string(&config["signing_algorithms_supported"]).unwrap() + // ) + // } + + metadata.subject_syntax_types_supported = config_2() + .did_methods + .into_iter() + .filter(|(_, v)| v.enabled) + .map(|(k, _)| SubjectSyntaxType::from_str(&k.replace("_", ":")).unwrap()) + .collect(); + + metadata.signing_algorithms_supported = config_2() + .signing_algorithms_supported + .iter() + .filter(|(_, v)| v.enabled) + .map(|(k, _)| k.clone()) + .collect(); + + metadata.id_token_signing_alg_values_supported = metadata.signing_algorithms_supported.clone(); + metadata.request_object_signing_alg_values_supported = metadata.signing_algorithms_supported.clone(); + + // TODO: vp_formats <= can they be implied or do we need to make them configurable? + + metadata.display = config_2().display.clone(); info!("Loaded metadata: {:?}", metadata); + info!("{:?}", serde_json::to_string(&metadata).unwrap()); + metadata } From 2dac2f39f9309e25791026f048a29b0829a2bd2f Mon Sep 17 00:00:00 2001 From: Daniel Mader Date: Mon, 15 Jul 2024 22:43:04 +0200 Subject: [PATCH 17/37] refactor: remove `metadata` --- Cargo.lock | 39 ++++++-- .../issuance/credential_issuer/credential.rs | 4 +- .../src/issuance/credential_issuer/token.rs | 5 +- .../well_known/oauth_authorization_server.rs | 4 +- .../well_known/openid_credential_issuer.rs | 7 +- agent_api_rest/src/issuance/credentials.rs | 4 +- agent_api_rest/src/issuance/offers.rs | 4 +- agent_application/example-config.yaml | 25 ++---- agent_application/src/main.rs | 14 +-- agent_event_publisher_http/README.md | 13 ++- agent_event_publisher_http/src/lib.rs | 77 ++++------------ agent_issuance/src/credential/aggregate.rs | 15 ++-- agent_issuance/src/offer/event.rs | 2 +- agent_issuance/src/startup_commands.rs | 10 +-- agent_shared/Cargo.toml | 1 + agent_shared/src/config.rs | 68 +++++++++++--- agent_shared/src/metadata.rs | 11 --- agent_verification/src/services.rs | 89 +++++++++++-------- 18 files changed, 196 insertions(+), 196 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ea7d26df..78ddf256 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -227,6 +227,7 @@ dependencies = [ "serde_json", "serde_with 3.8.1", "serde_yaml", + "strum 0.26.3", "thiserror", "time", "tracing", @@ -3226,7 +3227,7 @@ dependencies = [ "multibase 0.9.1", "serde", "serde_json", - "strum", + "strum 0.25.0", "thiserror", "time", "url", @@ -3253,7 +3254,7 @@ dependencies = [ "serde-aux", "serde_json", "serde_repr", - "strum", + "strum 0.25.0", "thiserror", "url", ] @@ -3268,7 +3269,7 @@ dependencies = [ "form_urlencoded", "identity_core", "serde", - "strum", + "strum 0.25.0", "thiserror", ] @@ -3284,7 +3285,7 @@ dependencies = [ "identity_verification", "indexmap 2.2.6", "serde", - "strum", + "strum 0.25.0", "thiserror", ] @@ -3324,7 +3325,7 @@ dependencies = [ "prefix-hex", "ref-cast", "serde", - "strum", + "strum 0.25.0", "thiserror", ] @@ -3359,7 +3360,7 @@ dependencies = [ "identity_document", "identity_iota_core", "serde", - "strum", + "strum 0.25.0", "thiserror", ] @@ -3415,7 +3416,7 @@ dependencies = [ "identity_jose", "serde", "serde_json", - "strum", + "strum 0.25.0", "thiserror", ] @@ -7256,7 +7257,16 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" dependencies = [ - "strum_macros", + "strum_macros 0.25.3", +] + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros 0.26.4", ] [[package]] @@ -7272,6 +7282,19 @@ dependencies = [ "syn 2.0.67", ] +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.67", +] + [[package]] name = "subtle" version = "2.6.0" diff --git a/agent_api_rest/src/issuance/credential_issuer/credential.rs b/agent_api_rest/src/issuance/credential_issuer/credential.rs index ad2ede35..2549e3bd 100644 --- a/agent_api_rest/src/issuance/credential_issuer/credential.rs +++ b/agent_api_rest/src/issuance/credential_issuer/credential.rs @@ -142,7 +142,7 @@ pub(crate) async fn credential( #[cfg(test)] mod tests { - use agent_shared::metadata::{load_metadata, set_metadata_configuration}; + use agent_shared::metadata::set_metadata_configuration; use std::sync::Arc; use crate::{ @@ -312,7 +312,7 @@ mod tests { let issuance_state = in_memory::issuance_state(issuance_event_publishers).await; let verification_state = in_memory::verification_state(test_verification_services(), verification_event_publishers).await; - initialize(&issuance_state, startup_commands(BASE_URL.clone(), &load_metadata())).await; + initialize(&issuance_state, startup_commands(BASE_URL.clone())).await; let mut app = app((issuance_state, verification_state)); diff --git a/agent_api_rest/src/issuance/credential_issuer/token.rs b/agent_api_rest/src/issuance/credential_issuer/token.rs index 68c190f3..2f10d116 100644 --- a/agent_api_rest/src/issuance/credential_issuer/token.rs +++ b/agent_api_rest/src/issuance/credential_issuer/token.rs @@ -68,7 +68,7 @@ pub mod tests { use super::*; use agent_issuance::{startup_commands::startup_commands, state::initialize}; - use agent_shared::metadata::{load_metadata, set_metadata_configuration}; + use agent_shared::metadata::set_metadata_configuration; use agent_store::in_memory; use agent_verification::services::test_utils::test_verification_services; use axum::{ @@ -113,10 +113,9 @@ pub mod tests { async fn test_token_endpoint() { set_metadata_configuration("did:key"); - let metadata = load_metadata(); let issuance_state = in_memory::issuance_state(Default::default()).await; let verification_state = in_memory::verification_state(test_verification_services(), Default::default()).await; - initialize(&issuance_state, startup_commands(BASE_URL.clone(), &metadata)).await; + initialize(&issuance_state, startup_commands(BASE_URL.clone())).await; let mut app = app((issuance_state, verification_state)); diff --git a/agent_api_rest/src/issuance/credential_issuer/well_known/oauth_authorization_server.rs b/agent_api_rest/src/issuance/credential_issuer/well_known/oauth_authorization_server.rs index 02edd2c8..dde97ff9 100644 --- a/agent_api_rest/src/issuance/credential_issuer/well_known/oauth_authorization_server.rs +++ b/agent_api_rest/src/issuance/credential_issuer/well_known/oauth_authorization_server.rs @@ -27,7 +27,7 @@ mod tests { use super::*; use agent_issuance::{startup_commands::startup_commands, state::initialize}; - use agent_shared::metadata::{load_metadata, set_metadata_configuration}; + use agent_shared::metadata::set_metadata_configuration; use agent_store::in_memory; use agent_verification::services::test_utils::test_verification_services; use axum::{ @@ -75,7 +75,7 @@ mod tests { let issuance_state = in_memory::issuance_state(Default::default()).await; let verification_state = in_memory::verification_state(test_verification_services(), Default::default()).await; - initialize(&issuance_state, startup_commands(BASE_URL.clone(), &load_metadata())).await; + initialize(&issuance_state, startup_commands(BASE_URL.clone())).await; let mut app = app((issuance_state, verification_state)); diff --git a/agent_api_rest/src/issuance/credential_issuer/well_known/openid_credential_issuer.rs b/agent_api_rest/src/issuance/credential_issuer/well_known/openid_credential_issuer.rs index fe380073..2bbfcece 100644 --- a/agent_api_rest/src/issuance/credential_issuer/well_known/openid_credential_issuer.rs +++ b/agent_api_rest/src/issuance/credential_issuer/well_known/openid_credential_issuer.rs @@ -29,10 +29,7 @@ mod tests { use super::*; use agent_issuance::{startup_commands::startup_commands, state::initialize}; - use agent_shared::{ - metadata::{load_metadata, set_metadata_configuration}, - UrlAppendHelpers, - }; + use agent_shared::{metadata::set_metadata_configuration, UrlAppendHelpers}; use agent_store::in_memory; use agent_verification::services::test_utils::test_verification_services; use axum::{ @@ -137,7 +134,7 @@ mod tests { let issuance_state = in_memory::issuance_state(Default::default()).await; let verification_state = in_memory::verification_state(test_verification_services(), Default::default()).await; - initialize(&issuance_state, startup_commands(BASE_URL.clone(), &load_metadata())).await; + initialize(&issuance_state, startup_commands(BASE_URL.clone())).await; let mut app = app((issuance_state, verification_state)); diff --git a/agent_api_rest/src/issuance/credentials.rs b/agent_api_rest/src/issuance/credentials.rs index d2891e12..968c32a5 100644 --- a/agent_api_rest/src/issuance/credentials.rs +++ b/agent_api_rest/src/issuance/credentials.rs @@ -159,7 +159,7 @@ pub mod tests { tests::{BASE_URL, CREDENTIAL_CONFIGURATION_ID, OFFER_ID}, }; use agent_issuance::{startup_commands::startup_commands, state::initialize}; - use agent_shared::metadata::{load_metadata, set_metadata_configuration}; + use agent_shared::metadata::set_metadata_configuration; use agent_store::in_memory; use agent_verification::services::test_utils::test_verification_services; use axum::{ @@ -257,7 +257,7 @@ pub mod tests { let issuance_state = in_memory::issuance_state(Default::default()).await; let verification_state = in_memory::verification_state(test_verification_services(), Default::default()).await; - initialize(&issuance_state, startup_commands(BASE_URL.clone(), &load_metadata())).await; + initialize(&issuance_state, startup_commands(BASE_URL.clone())).await; let mut app = app((issuance_state, verification_state)); diff --git a/agent_api_rest/src/issuance/offers.rs b/agent_api_rest/src/issuance/offers.rs index 98f34055..bd17d331 100644 --- a/agent_api_rest/src/issuance/offers.rs +++ b/agent_api_rest/src/issuance/offers.rs @@ -92,7 +92,7 @@ pub mod tests { use super::*; use crate::API_VERSION; use agent_issuance::{startup_commands::startup_commands, state::initialize}; - use agent_shared::metadata::{load_metadata, set_metadata_configuration}; + use agent_shared::metadata::set_metadata_configuration; use agent_store::in_memory; use agent_verification::services::test_utils::test_verification_services; use axum::{ @@ -161,7 +161,7 @@ pub mod tests { let verification_state = in_memory::verification_state(test_verification_services(), Default::default()).await; - initialize(&issuance_state, startup_commands(BASE_URL.clone(), &load_metadata())).await; + initialize(&issuance_state, startup_commands(BASE_URL.clone())).await; let mut app = app((issuance_state, verification_state)); diff --git a/agent_application/example-config.yaml b/agent_application/example-config.yaml index 10ae2b79..32b4ff24 100644 --- a/agent_application/example-config.yaml +++ b/agent_application/example-config.yaml @@ -26,11 +26,11 @@ signing_algorithms_supported: enabled: true # TODO: required to be stated explicitly? -# vp_formats: -# jwt_vc_json: -# enabled: true -# jwt_vp_json: -# enabled: true +vp_formats: + jwt_vc_json: + enabled: true + jwt_vp_json: + enabled: true # agent_event_publisher_http # Configure here to which events will be dispatched to a specific `target_url`. In the example below, the @@ -41,19 +41,8 @@ event_publishers: enabled: false target_url: "https://my-domain.example.org/event-subscriber" events: - [ - SIOPv2AuthorizationResponseVerified, - CredentialSigned, - CredentialOfferCreated, - ] - # publishers: - # - "connection" - # - "foobar" - # connection: - # { - # target_url: *target_url, - # target_events: [SIOPv2AuthorizationResponseVerified], - # } + server_config: [] + credential: [UnsignedCredentialCreated, CredentialSigned] # These display parameters are interpreted by identity wallets. display: diff --git a/agent_application/src/main.rs b/agent_application/src/main.rs index 934462a5..c876e7b9 100644 --- a/agent_application/src/main.rs +++ b/agent_application/src/main.rs @@ -5,7 +5,6 @@ use agent_secret_manager::{secret_manager, subject::Subject}; use agent_shared::{ config::{config_2, LogFormat, ToggleOptions}, domain_linkage::create_did_configuration_resource, - metadata::{load_metadata, Metadata}, }; use agent_store::{in_memory, postgres, EventPublisher}; use agent_verification::services::VerificationServices; @@ -32,14 +31,9 @@ async fn main() -> io::Result<()> { // _ => tracing_subscriber.with(tracing_subscriber::fmt::layer()).init(), } - let metadata: Metadata = load_metadata(); - - let verification_services = Arc::new(VerificationServices::new( - Arc::new(Subject { - secret_manager: secret_manager().await, - }), - &metadata, - )); + let verification_services = Arc::new(VerificationServices::new(Arc::new(Subject { + secret_manager: secret_manager().await, + }))); // TODO: Currently `issuance_event_publishers` and `verification_event_publishers` are exactly the same, which is // weird. We need some sort of layer between `agent_application` and `agent_store` that will provide a cleaner way @@ -73,7 +67,7 @@ async fn main() -> io::Result<()> { let url = url::Url::parse(&url).unwrap(); - initialize(&issuance_state, startup_commands(url.clone(), &metadata)).await; + initialize(&issuance_state, startup_commands(url.clone())).await; let mut app = app((issuance_state, verification_state)); diff --git a/agent_event_publisher_http/README.md b/agent_event_publisher_http/README.md index 294a9d3e..a2f900b5 100644 --- a/agent_event_publisher_http/README.md +++ b/agent_event_publisher_http/README.md @@ -13,12 +13,11 @@ Example `config.yaml`: ```yaml target_url: &target_url "https://my-domain.example.org/ssi-event-subscriber" -connection: { - target_url: *target_url, - target_events: [ - SIOPv2AuthorizationResponseVerified - ] -} +connection: + { + target_url: *target_url, + target_events: [SIOPv2AuthorizationResponseVerified], + } ``` ### Available events @@ -35,7 +34,7 @@ CredentialSigned ``` CredentialOfferCreated -CredentialAdded +CredentialsAdded FormUrlEncodedCredentialOfferCreated TokenResponseCreated CredentialRequestVerified diff --git a/agent_event_publisher_http/src/lib.rs b/agent_event_publisher_http/src/lib.rs index 77cc8768..f3e77c36 100644 --- a/agent_event_publisher_http/src/lib.rs +++ b/agent_event_publisher_http/src/lib.rs @@ -1,11 +1,7 @@ -use std::collections::HashMap; - use agent_issuance::{ - credential::aggregate::Credential, - offer::{aggregate::Offer, event}, - server_config::aggregate::ServerConfig, + credential::aggregate::Credential, offer::aggregate::Offer, server_config::aggregate::ServerConfig, }; -use agent_shared::config::{config_2, Event}; +use agent_shared::config::config_2; use agent_store::{ AuthorizationRequestEventPublisher, ConnectionEventPublisher, CredentialEventPublisher, EventPublisher, OfferEventPublisher, ServerConfigEventPublisher, @@ -34,32 +30,6 @@ pub struct EventPublisherHttp { pub authorization_request: Option>, } -// We need this to map events to aggregates -#[derive(PartialEq)] -enum DomainAggregate { - ServerConfig, - Credential, - Offer, - Connection, - AuthorizationRequest, -} - -// We need this to map events to aggregates -// #[derive(Eq, PartialEq, Hash)] -// enum Event { -// // credential -// UnsignedCredentialCreated, -// SignedCredentialCreated, -// CredentialSigned, -// // offer -// CredentialOfferCreated, -// CredentialAdded, -// FormUrlEncodedCredentialOfferCreated, -// TokenResponseCreated, -// CredentialRequestVerified, -// CredentialResponseCreated, -// } - impl EventPublisherHttp { pub fn load() -> anyhow::Result { // #[cfg(feature = "test_utils")] @@ -79,8 +49,7 @@ impl EventPublisherHttp { // }; // let config = agent_shared::config::config("AGENT_EVENT_PUBLISHER_HTTP"); - let event_publishers = config_2().event_publishers.unwrap(); - let event_publisher_http = event_publishers.http.unwrap(); + let event_publisher_http = config_2().event_publishers.unwrap().http.unwrap(); // let event_publishers = config.get_table("event_publishers").unwrap_or_default(); // let event_publisher_http = event_publishers @@ -98,45 +67,33 @@ impl EventPublisherHttp { // TODO: map events to aggregates // let mapping = HashMap::::new(); - let mapping: HashMap = HashMap::from([ - // credential - (Event::UnsignedCredentialCreated, DomainAggregate::Credential), - (Event::SignedCredentialCreated, DomainAggregate::Credential), - (Event::CredentialSigned, DomainAggregate::Credential), - // offer - (Event::CredentialOfferCreated, DomainAggregate::Offer), - // connection - (Event::SIOPv2AuthorizationResponseVerified, DomainAggregate::Connection), - ]); + // let mapping: HashMap = HashMap::from([ + // // credential + // (Event::UnsignedCredentialCreated, DomainAggregate::Credential), + // (Event::SignedCredentialCreated, DomainAggregate::Credential), + // (Event::CredentialSigned, DomainAggregate::Credential), + // // offer + // (Event::CredentialOfferCreated, DomainAggregate::Offer), + // // connection + // (Event::SIOPv2AuthorizationResponseVerified, DomainAggregate::Connection), + // ]); let credential_events: Vec = event_publisher_http .events + .credential + .unwrap_or(vec![]) .iter() - .filter(|e| mapping.get(e).unwrap() == &DomainAggregate::Credential) - .map(|e| serde_json::to_string(e).unwrap()) - .map(|e| e.trim_matches('"').to_string()) // TODO: properly serialize to String + .map(|e| e.to_string()) .collect(); info!("credential_events: {:?}", credential_events); - let offer_events: Vec = event_publisher_http - .events - .iter() - .filter(|e| mapping.get(e).unwrap() == &DomainAggregate::Offer) - .map(|e| serde_json::to_string(e).unwrap()) - .map(|e| e.trim_matches('"').to_string()) // TODO: properly serialize to String - .collect(); - info!("offer_events: {:?}", offer_events); - let event_publisher: EventPublisherHttp = EventPublisherHttp { server_config: None, credential: Some(AggregateEventPublisherHttp::new( event_publisher_http.target_url.clone(), credential_events, )), - offer: Some(AggregateEventPublisherHttp::new( - event_publisher_http.target_url.clone(), - offer_events, - )), + offer: None, connection: None, authorization_request: None, }; diff --git a/agent_issuance/src/credential/aggregate.rs b/agent_issuance/src/credential/aggregate.rs index 5e77ec62..b09d0740 100644 --- a/agent_issuance/src/credential/aggregate.rs +++ b/agent_issuance/src/credential/aggregate.rs @@ -80,15 +80,12 @@ impl Aggregate for Credential { #[cfg(not(feature = "test_utils"))] let issuance_date = chrono::Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true); - let name = match config!("display", Vec) - .ok() - .as_ref() - .and_then(|displays| displays.first()) - .map(|display| display.name.clone()) - { - Some(name) => name, - None => unreachable!("The display.name parameter is not set"), - }; + let name = config_2() + .display + .first() + .expect("Configuration `display.name` missing") + .name + .clone(); let issuer: Profile = ProfileBuilder::default() .id(config_2().url) diff --git a/agent_issuance/src/offer/event.rs b/agent_issuance/src/offer/event.rs index 93d33b27..9fd2d03c 100644 --- a/agent_issuance/src/offer/event.rs +++ b/agent_issuance/src/offer/event.rs @@ -37,7 +37,7 @@ impl DomainEvent for OfferEvent { let event_type: &str = match self { CredentialOfferCreated { .. } => "CredentialOfferCreated", - CredentialsAdded { .. } => "CredentialAdded", + CredentialsAdded { .. } => "CredentialsAdded", FormUrlEncodedCredentialOfferCreated { .. } => "FormUrlEncodedCredentialOfferCreated", TokenResponseCreated { .. } => "TokenResponseCreated", CredentialRequestVerified { .. } => "CredentialRequestVerified", diff --git a/agent_issuance/src/startup_commands.rs b/agent_issuance/src/startup_commands.rs index e2340ce8..d74c74ce 100644 --- a/agent_issuance/src/startup_commands.rs +++ b/agent_issuance/src/startup_commands.rs @@ -1,5 +1,5 @@ use agent_shared::config::config_2; -use agent_shared::{metadata::Metadata, url_utils::UrlAppendHelpers}; +use agent_shared::url_utils::UrlAppendHelpers; use oid4vci::credential_issuer::{ authorization_server_metadata::AuthorizationServerMetadata, credential_issuer_metadata::CredentialIssuerMetadata, }; @@ -7,12 +7,12 @@ use oid4vci::credential_issuer::{ use crate::server_config::command::ServerConfigCommand; /// Returns the startup commands for the application. -pub fn startup_commands(host: url::Url, metadata: &Metadata) -> Vec { - vec![load_server_metadata(host, metadata), create_credentials_supported()] +pub fn startup_commands(host: url::Url) -> Vec { + vec![load_server_metadata(host), create_credentials_supported()] } -pub fn load_server_metadata(base_url: url::Url, metadata: &Metadata) -> ServerConfigCommand { - let display = metadata.display.first().map(|display| { +pub fn load_server_metadata(base_url: url::Url) -> ServerConfigCommand { + let display = config_2().display.first().map(|display| { let display = serde_json::to_value(display).unwrap(); vec![display] }); diff --git a/agent_shared/Cargo.toml b/agent_shared/Cargo.toml index 234a7146..1de0eb8b 100644 --- a/agent_shared/Cargo.toml +++ b/agent_shared/Cargo.toml @@ -29,6 +29,7 @@ serde.workspace = true serde_json.workspace = true serde_with = "3.0" serde_yaml.workspace = true +strum = { version = "0.26", features = ["derive"] } thiserror.workspace = true time = { version = "0.3" } tracing.workspace = true diff --git a/agent_shared/src/config.rs b/agent_shared/src/config.rs index c1810c88..27ed330c 100644 --- a/agent_shared/src/config.rs +++ b/agent_shared/src/config.rs @@ -1,4 +1,5 @@ use config::ConfigError; +use oid4vp::ClaimFormatDesignation; use once_cell::sync::OnceCell; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -21,6 +22,7 @@ pub struct ApplicationConfiguration { pub signing_algorithms_supported: HashMap, pub display: Vec, pub event_publishers: Option, + pub vp_formats: HashMap, } #[derive(Debug, Deserialize, Clone, Default)] @@ -69,33 +71,52 @@ pub struct EventPublishers { pub struct EventPublisherHttp { pub enabled: bool, pub target_url: String, - pub events: Vec, + pub events: Events, } -/// All events that can be published. -#[derive(Debug, Serialize, Deserialize, Clone, Hash, Eq, PartialEq)] -pub enum Event { - // credential +#[derive(Debug, Deserialize, Clone)] +pub struct Events { + pub server_config: Option>, + pub credential: Option>, + pub offer: Option>, + pub connection: Option>, + pub authorization_request: Option>, +} + +#[derive(Debug, Serialize, Deserialize, Clone, strum::Display)] +pub enum ServerConfigEvent { + ServerMetadataInitialized, + CredentialConfigurationAdded, +} + +#[derive(Debug, Serialize, Deserialize, Clone, strum::Display)] +pub enum CredentialEvent { UnsignedCredentialCreated, SignedCredentialCreated, CredentialSigned, - // offer +} + +#[derive(Debug, Serialize, Deserialize, Clone, strum::Display)] +pub enum OfferEvent { CredentialOfferCreated, - CredentialAdded, + CredentialsAdded, FormUrlEncodedCredentialOfferCreated, TokenResponseCreated, CredentialRequestVerified, CredentialResponseCreated, - // server_config - ServerMetadataLoaded, - CredentialConfigurationAdded, - // authorization_request +} + +#[derive(Debug, Serialize, Deserialize, Clone, strum::Display)] +pub enum ConnectionEvent { + SIOPv2AuthorizationResponseVerified, + OID4VPAuthorizationResponseVerified, +} + +#[derive(Debug, Serialize, Deserialize, Clone, strum::Display)] +pub enum AuthorizationRequestEvent { AuthorizationRequestCreated, FormUrlEncodedAuthorizationRequestCreated, AuthorizationRequestObjectSigned, - // connection - SIOPv2AuthorizationResponseVerified, - OID4VPAuthorizationResponseVerified, } // pub enum DidMethod { @@ -135,6 +156,25 @@ pub fn config_2() -> ApplicationConfiguration { // TODO: or return -> &'static ApplicationConfiguration, so we don't need to clone on every call } +pub fn did_methods_enabled() -> Vec { + config_2() + .did_methods + .iter() + .filter(|(_, v)| v.enabled) + .map(|(k, _)| k.clone().replace("_", ":")) + .collect() +} + +pub fn did_method_preferred() -> String { + config_2() + .did_methods + .iter() + .filter(|(_, v)| v.enabled) + .filter(|(_, v)| v.preferred.unwrap_or(false)) + .map(|(k, _)| k.clone().replace("_", ":")) + .collect() +} + /// Read environment variables #[allow(unused)] pub fn config(package_name: &str) -> config::Config { diff --git a/agent_shared/src/metadata.rs b/agent_shared/src/metadata.rs index 6e854423..453e6cb4 100644 --- a/agent_shared/src/metadata.rs +++ b/agent_shared/src/metadata.rs @@ -45,17 +45,6 @@ pub struct Metadata { #[cfg(feature = "test_utils")] pub static TEST_METADATA: std::sync::Mutex> = std::sync::Mutex::new(None); -// impl Metadata { -// pub fn new() -> Result { -// info!("Loading application metadata ..."); -// let config = config::Config::builder() -// .add_source(config::File::with_name("agent_application/example-config.yaml")) -// .add_source(config::Environment::with_prefix("AGENT").separator("__")) -// .build()?; -// config.try_deserialize() -// } -// } - pub fn load_metadata() -> Metadata { // #[cfg(feature = "test_utils")] // let mut config = TEST_METADATA.lock().unwrap().as_ref().unwrap().clone(); diff --git a/agent_verification/src/services.rs b/agent_verification/src/services.rs index 9c632413..6bc453c1 100644 --- a/agent_verification/src/services.rs +++ b/agent_verification/src/services.rs @@ -1,10 +1,10 @@ -use agent_shared::{config::config_2, metadata::Metadata}; +use agent_shared::config::{config_2, did_methods_enabled}; use jsonwebtoken::Algorithm; use oid4vc_core::{client_metadata::ClientMetadataResource, Subject}; use oid4vc_manager::RelyingPartyManager; use oid4vp::ClaimFormatProperty; use serde_json::json; -use std::{collections::HashMap, sync::Arc}; +use std::{collections::HashMap, str::FromStr, sync::Arc}; /// Verification services. This struct is used to generate authorization requests and validate authorization responses. pub struct VerificationServices { @@ -15,7 +15,7 @@ pub struct VerificationServices { } impl VerificationServices { - pub fn new(verifier: Arc, metadata: &Metadata) -> Self { + pub fn new(verifier: Arc) -> Self { let default_did_method = config_2() .did_methods .iter() @@ -28,25 +28,35 @@ impl VerificationServices { .to_owned() .replace("_", ":"); - let client_name = metadata.display.first().as_ref().map(|display| display.name.clone()); + let client_name = config_2().display.first().as_ref().map(|display| display.name.clone()); - let logo_uri = metadata + let logo_uri = config_2() .display .first() .and_then(|display| display.logo.as_ref().and_then(|logo| logo.url.clone())); - let id_token_signed_response_alg = metadata.signing_algorithms_supported.first().cloned(); + let signing_algorithms_supported: Vec = config_2() + .signing_algorithms_supported + .iter() + .filter(|(_, opts)| opts.enabled) + .map(|(alg, _)| *alg) + .collect(); + + // let id_token_signed_response_alg = signing_algorithms_supported.first().cloned(); let siopv2_client_metadata = ClientMetadataResource::ClientMetadata { client_name: client_name.clone(), logo_uri: logo_uri.clone(), extension: siopv2::authorization_request::ClientMetadataParameters { - subject_syntax_types_supported: metadata.subject_syntax_types_supported.clone(), - id_token_signed_response_alg, + subject_syntax_types_supported: did_methods_enabled() + .iter() + .map(|method| oid4vc_core::SubjectSyntaxType::from_str(method).unwrap()) + .collect(), + id_token_signed_response_alg: signing_algorithms_supported.first().cloned(), }, other: HashMap::from_iter([( "id_token_signing_alg_values_supported".to_string(), - json!(metadata.id_token_signing_alg_values_supported), + json!(signing_algorithms_supported), )]), }; @@ -54,42 +64,49 @@ impl VerificationServices { client_name, logo_uri, extension: oid4vp::authorization_request::ClientMetadataParameters { - vp_formats: metadata + vp_formats: config_2() .vp_formats .iter() - .map(|(k, v)| { + .filter(|(_, opts)| opts.enabled) + .map(|(c, _)| { ( - k.clone(), - ClaimFormatProperty::Alg( - v.get("alg") - .map(|value| { - value - .as_sequence() - .unwrap() - .iter() - .map(|value| value.as_str().unwrap().parse().unwrap()) - .collect::>() - }) - .unwrap(), - ), + c.clone(), + ClaimFormatProperty::Alg(signing_algorithms_supported.clone()), ) }) .collect(), + // vp_formats: metadata + // .vp_formats + // .iter() + // .map(|(k, v)| { + // ( + // k.clone(), + // ClaimFormatProperty::Alg( + // v.get("alg") + // .map(|value| { + // value + // .as_sequence() + // .unwrap() + // .iter() + // .map(|value| value.as_str().unwrap().parse().unwrap()) + // .collect::>() + // }) + // .unwrap(), + // ), + // ) + // }) + // .collect(), }, other: HashMap::from_iter([( "subject_syntax_types_supported".to_string(), - json!(metadata.subject_syntax_types_supported), + json!(did_methods_enabled()), )]), }; Self { verifier: verifier.clone(), - relying_party: RelyingPartyManager::new( - verifier, - default_did_method, - metadata.signing_algorithms_supported.clone(), - ) - .unwrap(), + relying_party: RelyingPartyManager::new(verifier, default_did_method, signing_algorithms_supported) + .unwrap(), siopv2_client_metadata, oid4vp_client_metadata, } @@ -100,18 +117,16 @@ impl VerificationServices { pub mod test_utils { use agent_secret_manager::secret_manager; use agent_secret_manager::subject::Subject; - use agent_shared::metadata::load_metadata; use super::*; pub fn test_verification_services() -> Arc { - Arc::new(VerificationServices::new( - Arc::new(futures::executor::block_on(async { + Arc::new(VerificationServices::new(Arc::new(futures::executor::block_on( + async { Subject { secret_manager: secret_manager().await, } - })), - &load_metadata(), - )) + }, + )))) } } From 6be548366d17e2caa89df2a45e6955b93ce53fd2 Mon Sep 17 00:00:00 2001 From: Daniel Mader Date: Mon, 15 Jul 2024 23:20:21 +0200 Subject: [PATCH 18/37] refactor: remove `config!` macro --- agent_shared/src/config.rs | 54 +----------------------------------- agent_shared/src/lib.rs | 8 ------ agent_shared/tests/.env.test | 12 -------- agent_shared/tests/config.rs | 9 ------ 4 files changed, 1 insertion(+), 82 deletions(-) delete mode 100644 agent_shared/tests/.env.test delete mode 100644 agent_shared/tests/config.rs diff --git a/agent_shared/src/config.rs b/agent_shared/src/config.rs index 27ed330c..032f1ee7 100644 --- a/agent_shared/src/config.rs +++ b/agent_shared/src/config.rs @@ -3,7 +3,7 @@ use oid4vp::ClaimFormatDesignation; use once_cell::sync::OnceCell; use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use tracing::{debug, info}; +use tracing::info; use crate::{issuance::CredentialConfiguration, metadata::Display}; @@ -174,55 +174,3 @@ pub fn did_method_preferred() -> String { .map(|(k, _)| k.clone().replace("_", ":")) .collect() } - -/// Read environment variables -#[allow(unused)] -pub fn config(package_name: &str) -> config::Config { - #[cfg(feature = "test_utils")] - let config = test_config(); - - #[cfg(not(feature = "test_utils"))] - let config = { - dotenvy::dotenv().ok(); - - config::Config::builder() - // .add_source(config::Environment::with_prefix(package_name)) - // TODO: read config.{run_mode}.yml from env "RUN_MODE" - .add_source(config::File::with_name("agent_application/example-config.yaml")) - .add_source(config::Environment::with_prefix("AGENT")) - .build() - .unwrap() - }; - - // TODO: this should ideally only printed once on startup or when the config changed - // info!("{:#?}", config.clone().try_deserialize::().unwrap()); - - config -} - -/// Read environment variables for tests that can be used across packages -#[cfg(feature = "test_utils")] -fn test_config() -> config::Config { - use crate::{issuance::TEST_ISSUER_CONFIG, metadata::TEST_METADATA}; - use std::env; - - dotenvy::from_filename("agent_shared/tests/.env.test").ok(); - - env::remove_var("AGENT_APPLICATION_BASE_PATH"); - - let mut config_builder = config::Config::builder().add_source(config::Environment::with_prefix("TEST")); - - // If some test metadata configuration is set then add it to the global configuration. - let metadata = TEST_METADATA.lock().unwrap(); - if let Some(metadata) = metadata.as_ref() { - let metadata_string = serde_yaml::to_string(metadata).unwrap(); - config_builder = config_builder.add_source(config::File::from_str(&metadata_string, config::FileFormat::Yaml)); - } - - config_builder = config_builder.add_source(config::File::from_str( - &serde_yaml::to_string(&*TEST_ISSUER_CONFIG).unwrap(), - config::FileFormat::Yaml, - )); - - config_builder.build().unwrap() -} diff --git a/agent_shared/src/lib.rs b/agent_shared/src/lib.rs index 054ab71e..cb2a37ae 100644 --- a/agent_shared/src/lib.rs +++ b/agent_shared/src/lib.rs @@ -12,14 +12,6 @@ pub use ::config::ConfigError; use rand::Rng; pub use url_utils::UrlAppendHelpers; -/// Macro to read configuration using the package name as prefix. -#[macro_export] -macro_rules! config { - ($string:expr, $type:ty) => { - agent_shared::config::config(std::env!("CARGO_PKG_NAME")).get::<$type>($string) - }; -} - pub fn generate_random_string() -> String { let mut rng = rand::thread_rng(); diff --git a/agent_shared/tests/.env.test b/agent_shared/tests/.env.test deleted file mode 100644 index 770358d4..00000000 --- a/agent_shared/tests/.env.test +++ /dev/null @@ -1,12 +0,0 @@ -TEST_VARIABLE=env_value -AGENT_OTHER_OTHER_VARIABLE=other_env_value - -# AGENT_SECRET_MANAGER -TEST_STRONGHOLD_PATH="../agent_secret_manager/tests/res/selv.stronghold" -TEST_STRONGHOLD_PASSWORD="VNvRtH4tKyWwvJDpL6Vuc2aoLiKAecGQ" -TEST_ISSUER_KEY_ID="UVDxWhG2rB39FkaR7I27mHeUNrGtUgcr" -TEST_ISSUER_DID="did:iota:rms:0x42ad588322e58b3c07aa39e4948d021ee17ecb5747915e9e1f35f028d7ecaf90" -TEST_ISSUER_FRAGMENT="bQKQRzaop7CgEvqVq8UlgLGsdF-R-hnLFkKFZqW2VN0" - -# AGENT_VERIFICATION -TEST_URL=https://my-domain.example.org diff --git a/agent_shared/tests/config.rs b/agent_shared/tests/config.rs deleted file mode 100644 index 93ceda1c..00000000 --- a/agent_shared/tests/config.rs +++ /dev/null @@ -1,9 +0,0 @@ -use agent_shared::config; - -#[cfg(feature = "test_utils")] -#[test] -fn test_config() { - assert_eq!(config!("variable", String).unwrap(), "env_value"); - // Reading from an environment variable that belongs to another package should fail. - assert!(config!("other_variable", String).is_err()); -} From b9106d7bfe808e5fdae015b6542424ee6caa2e29 Mon Sep 17 00:00:00 2001 From: Daniel Mader Date: Mon, 15 Jul 2024 23:24:17 +0200 Subject: [PATCH 19/37] refactor: rename `config_2` to `config` --- .../issuance/credential_issuer/credential.rs | 4 ++-- agent_api_rest/src/lib.rs | 4 ++-- agent_application/src/main.rs | 18 +++++++++--------- agent_event_publisher_http/src/lib.rs | 4 ++-- agent_issuance/src/credential/aggregate.rs | 6 +++--- agent_issuance/src/server_config/aggregate.rs | 6 +++--- agent_issuance/src/startup_commands.rs | 6 +++--- agent_secret_manager/src/lib.rs | 12 ++++++------ agent_secret_manager/src/services.rs | 14 +++++++------- agent_secret_manager/src/subject.rs | 4 ++-- agent_shared/src/config.rs | 7 +++---- agent_shared/src/metadata.rs | 8 ++++---- .../src/authorization_request/aggregate.rs | 4 ++-- agent_verification/src/services.rs | 12 ++++++------ 14 files changed, 54 insertions(+), 55 deletions(-) diff --git a/agent_api_rest/src/issuance/credential_issuer/credential.rs b/agent_api_rest/src/issuance/credential_issuer/credential.rs index 2549e3bd..96f955e8 100644 --- a/agent_api_rest/src/issuance/credential_issuer/credential.rs +++ b/agent_api_rest/src/issuance/credential_issuer/credential.rs @@ -11,7 +11,7 @@ use agent_issuance::{ }; use agent_shared::{ config, - config::config_2, + config::config, handlers::{command_handler, query_handler}, }; use axum::{ @@ -65,7 +65,7 @@ pub(crate) async fn credential( StatusCode::INTERNAL_SERVER_ERROR.into_response(); }; - let timeout = config_2() + let timeout = config() .external_server_response_timeout_ms .unwrap_or(DEFAULT_EXTERNAL_SERVER_RESPONSE_TIMEOUT_MS); let start_time = Instant::now(); diff --git a/agent_api_rest/src/lib.rs b/agent_api_rest/src/lib.rs index d21d311a..ff9b5bdb 100644 --- a/agent_api_rest/src/lib.rs +++ b/agent_api_rest/src/lib.rs @@ -2,7 +2,7 @@ mod issuance; mod verification; use agent_issuance::state::IssuanceState; -use agent_shared::{config, config::config_2, ConfigError}; +use agent_shared::{config, config::config, ConfigError}; use agent_verification::state::VerificationState; use axum::{ body::Bytes, @@ -99,7 +99,7 @@ pub fn app(state: ApplicationState) -> Router { } fn get_base_path() -> Result { - config_2() + config() .base_path .ok_or_else(|| ConfigError::NotFound("No configuration for `base_path` found".to_string())) .map(|mut base_path| { diff --git a/agent_application/src/main.rs b/agent_application/src/main.rs index c876e7b9..3387b8d5 100644 --- a/agent_application/src/main.rs +++ b/agent_application/src/main.rs @@ -3,7 +3,7 @@ use agent_event_publisher_http::EventPublisherHttp; use agent_issuance::{startup_commands::startup_commands, state::initialize}; use agent_secret_manager::{secret_manager, subject::Subject}; use agent_shared::{ - config::{config_2, LogFormat, ToggleOptions}, + config::{config, LogFormat, ToggleOptions}, domain_linkage::create_did_configuration_resource, }; use agent_store::{in_memory, postgres, EventPublisher}; @@ -22,7 +22,7 @@ async fn main() -> io::Result<()> { // Set the default logging level to `info`, equivalent to `RUST_LOG=info` .with(tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| "info".into())); - match config_2().log_format { + match config().log_format { LogFormat::Json => tracing_subscriber.with(tracing_subscriber::fmt::layer().json()).init(), LogFormat::Text => tracing_subscriber.with(tracing_subscriber::fmt::layer()).init(), // Ok(log_format) if log_format == "json" => { @@ -42,9 +42,9 @@ async fn main() -> io::Result<()> { let verification_event_publishers: Vec> = vec![Box::new(EventPublisherHttp::load().unwrap())]; - let (issuance_state, verification_state) = match agent_shared::config::config_2().event_store.type_ { + let (issuance_state, verification_state) = match agent_shared::config::config().event_store.type_ { agent_shared::config::EventStoreType::Postgres => { - let connection_string = config_2().event_store.connection_string.expect( + let connection_string = config().event_store.connection_string.expect( "Missing config parameter `event_store.connection_string` or `AGENT__EVENT_STORE__CONNECTION_STRING`", ); ( @@ -59,9 +59,9 @@ async fn main() -> io::Result<()> { ), }; - info!("{:?}", config_2()); + info!("{:?}", config()); - let url = config_2().url; + let url = config().url; info!("Application url: {:?}", url); @@ -72,13 +72,13 @@ async fn main() -> io::Result<()> { let mut app = app((issuance_state, verification_state)); // CORS - if config_2().cors_enabled.unwrap_or(false) { + if config().cors_enabled.unwrap_or(false) { info!("CORS (permissive) enabled for all routes"); app = app.layer(CorsLayer::permissive()); } // did:web - let enable_did_web = config_2() + let enable_did_web = config() .did_methods .get("did_web") .unwrap_or(&ToggleOptions::default()) @@ -102,7 +102,7 @@ async fn main() -> io::Result<()> { None }; // Domain Linkage - let did_configuration_resource = if config_2().domain_linkage_enabled { + let did_configuration_resource = if config().domain_linkage_enabled { Some( create_did_configuration_resource( url.clone(), diff --git a/agent_event_publisher_http/src/lib.rs b/agent_event_publisher_http/src/lib.rs index f3e77c36..fc9921b9 100644 --- a/agent_event_publisher_http/src/lib.rs +++ b/agent_event_publisher_http/src/lib.rs @@ -1,7 +1,7 @@ use agent_issuance::{ credential::aggregate::Credential, offer::aggregate::Offer, server_config::aggregate::ServerConfig, }; -use agent_shared::config::config_2; +use agent_shared::config::config; use agent_store::{ AuthorizationRequestEventPublisher, ConnectionEventPublisher, CredentialEventPublisher, EventPublisher, OfferEventPublisher, ServerConfigEventPublisher, @@ -49,7 +49,7 @@ impl EventPublisherHttp { // }; // let config = agent_shared::config::config("AGENT_EVENT_PUBLISHER_HTTP"); - let event_publisher_http = config_2().event_publishers.unwrap().http.unwrap(); + let event_publisher_http = config().event_publishers.unwrap().http.unwrap(); // let event_publishers = config.get_table("event_publishers").unwrap_or_default(); // let event_publisher_http = event_publishers diff --git a/agent_issuance/src/credential/aggregate.rs b/agent_issuance/src/credential/aggregate.rs index b09d0740..60fc860f 100644 --- a/agent_issuance/src/credential/aggregate.rs +++ b/agent_issuance/src/credential/aggregate.rs @@ -1,6 +1,6 @@ use agent_secret_manager::services::SecretManagerServices; use agent_shared::config; -use agent_shared::config::config_2; +use agent_shared::config::config; use agent_shared::metadata::Display; use async_trait::async_trait; use cqrs_es::Aggregate; @@ -80,7 +80,7 @@ impl Aggregate for Credential { #[cfg(not(feature = "test_utils"))] let issuance_date = chrono::Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true); - let name = config_2() + let name = config() .display .first() .expect("Configuration `display.name` missing") @@ -88,7 +88,7 @@ impl Aggregate for Credential { .clone(); let issuer: Profile = ProfileBuilder::default() - .id(config_2().url) + .id(config().url) .type_("Profile") .name(name) .try_into() diff --git a/agent_issuance/src/server_config/aggregate.rs b/agent_issuance/src/server_config/aggregate.rs index 60e08b04..c5fa0821 100644 --- a/agent_issuance/src/server_config/aggregate.rs +++ b/agent_issuance/src/server_config/aggregate.rs @@ -1,4 +1,4 @@ -use agent_shared::config::config_2; +use agent_shared::config::config; use async_trait::async_trait; use cqrs_es::Aggregate; use jsonwebtoken::Algorithm; @@ -57,7 +57,7 @@ impl Aggregate for ServerConfig { AddCredentialConfiguration { credential_configuration, } => { - let cryptographic_binding_methods_supported = config_2() + let cryptographic_binding_methods_supported = config() .did_methods .iter() .filter(|(_, v)| v.enabled) @@ -66,7 +66,7 @@ impl Aggregate for ServerConfig { .map(|did_method| did_method.replace("_", ":")) .collect(); - let signing_algorithms_supported: Vec = config_2() + let signing_algorithms_supported: Vec = config() .signing_algorithms_supported .iter() .filter(|(_, options)| options.enabled) diff --git a/agent_issuance/src/startup_commands.rs b/agent_issuance/src/startup_commands.rs index d74c74ce..2423cd50 100644 --- a/agent_issuance/src/startup_commands.rs +++ b/agent_issuance/src/startup_commands.rs @@ -1,4 +1,4 @@ -use agent_shared::config::config_2; +use agent_shared::config::config; use agent_shared::url_utils::UrlAppendHelpers; use oid4vci::credential_issuer::{ authorization_server_metadata::AuthorizationServerMetadata, credential_issuer_metadata::CredentialIssuerMetadata, @@ -12,7 +12,7 @@ pub fn startup_commands(host: url::Url) -> Vec { } pub fn load_server_metadata(base_url: url::Url) -> ServerConfigCommand { - let display = config_2().display.first().map(|display| { + let display = config().display.first().map(|display| { let display = serde_json::to_value(display).unwrap(); vec![display] }); @@ -33,7 +33,7 @@ pub fn load_server_metadata(base_url: url::Url) -> ServerConfigCommand { } pub fn create_credentials_supported() -> ServerConfigCommand { - let credential_configuration = config_2() + let credential_configuration = config() .credential_configurations .first() .expect("No credential_configurations found") diff --git a/agent_secret_manager/src/lib.rs b/agent_secret_manager/src/lib.rs index 547d1cf1..501e3892 100644 --- a/agent_secret_manager/src/lib.rs +++ b/agent_secret_manager/src/lib.rs @@ -1,4 +1,4 @@ -use agent_shared::config::config_2; +use agent_shared::config::config; use did_manager::SecretManager; pub mod aggregate; @@ -9,11 +9,11 @@ pub mod subject; // TODO: find better solution for this pub async fn secret_manager() -> SecretManager { - let snapshot_path = config_2().secret_manager.stronghold_path; - let password = config_2().secret_manager.stronghold_password; - let key_id = config_2().secret_manager.issuer_key_id; - let issuer_did = config_2().secret_manager.issuer_did; - let issuer_fragment = config_2().secret_manager.issuer_fragment; + let snapshot_path = config().secret_manager.stronghold_path; + let password = config().secret_manager.stronghold_password; + let key_id = config().secret_manager.issuer_key_id; + let issuer_did = config().secret_manager.issuer_did; + let issuer_fragment = config().secret_manager.issuer_fragment; match (snapshot_path, password, key_id, issuer_did, issuer_fragment) { (snapshot_path, password, Some(key_id), issuer_did, issuer_fragment) => { diff --git a/agent_secret_manager/src/services.rs b/agent_secret_manager/src/services.rs index f3bee789..ab8e4865 100644 --- a/agent_secret_manager/src/services.rs +++ b/agent_secret_manager/src/services.rs @@ -1,5 +1,5 @@ use crate::subject::Subject; -use agent_shared::config::config_2; +use agent_shared::config::config; use anyhow::Result; use did_manager::SecretManager; @@ -10,7 +10,7 @@ pub struct SecretManagerServices { impl SecretManagerServices { pub fn new(subject: Option) -> Self { - let default_did_method: String = config_2() + let default_did_method: String = config() .did_methods .iter() .filter(|(_, v)| v.preferred.unwrap_or(false)) @@ -27,14 +27,14 @@ impl SecretManagerServices { } pub async fn init(&mut self) -> Result<(), std::io::Error> { - let snapshot_path = config_2().secret_manager.stronghold_path; - let password = config_2().secret_manager.stronghold_password; - let key_id = config_2() + let snapshot_path = config().secret_manager.stronghold_path; + let password = config().secret_manager.stronghold_password; + let key_id = config() .secret_manager .issuer_key_id .expect("Missing configuration: secret_manager.issuer_key_id"); - let issuer_did = config_2().secret_manager.issuer_did; - let issuer_fragment = config_2().secret_manager.issuer_fragment; + let issuer_did = config().secret_manager.issuer_did; + let issuer_fragment = config().secret_manager.issuer_fragment; let secret_manager = SecretManager::load(snapshot_path, password, key_id, issuer_did, issuer_fragment) .await diff --git a/agent_secret_manager/src/subject.rs b/agent_secret_manager/src/subject.rs index 2ba74084..6de8d7f8 100644 --- a/agent_secret_manager/src/subject.rs +++ b/agent_secret_manager/src/subject.rs @@ -1,4 +1,4 @@ -use agent_shared::config::config_2; +use agent_shared::config::config; use async_trait::async_trait; use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine}; use did_manager::{DidMethod, Resolver, SecretManager}; @@ -109,5 +109,5 @@ impl oid4vc_core::Subject for Subject { } fn origin() -> url::Origin { - config_2().url.parse::().unwrap().origin() + config().url.parse::().unwrap().origin() } diff --git a/agent_shared/src/config.rs b/agent_shared/src/config.rs index 032f1ee7..e7e527ba 100644 --- a/agent_shared/src/config.rs +++ b/agent_shared/src/config.rs @@ -150,14 +150,13 @@ impl ApplicationConfiguration { } /// Returns the application configuration or loads it, if it hasn't been loaded already. -pub fn config_2() -> ApplicationConfiguration { - // info!("config_2()"); +pub fn config() -> ApplicationConfiguration { CONFIG.get_or_init(|| ApplicationConfiguration::new().unwrap()).clone() // TODO: or return -> &'static ApplicationConfiguration, so we don't need to clone on every call } pub fn did_methods_enabled() -> Vec { - config_2() + config() .did_methods .iter() .filter(|(_, v)| v.enabled) @@ -166,7 +165,7 @@ pub fn did_methods_enabled() -> Vec { } pub fn did_method_preferred() -> String { - config_2() + config() .did_methods .iter() .filter(|(_, v)| v.enabled) diff --git a/agent_shared/src/metadata.rs b/agent_shared/src/metadata.rs index 453e6cb4..ac7fd82e 100644 --- a/agent_shared/src/metadata.rs +++ b/agent_shared/src/metadata.rs @@ -7,7 +7,7 @@ use std::{collections::HashMap, str::FromStr}; use tracing::info; use url::Url; -use crate::config::config_2; +use crate::config::config; #[skip_serializing_none] #[derive(Serialize, Deserialize, Debug, Clone)] @@ -70,14 +70,14 @@ pub fn load_metadata() -> Metadata { // ) // } - metadata.subject_syntax_types_supported = config_2() + metadata.subject_syntax_types_supported = config() .did_methods .into_iter() .filter(|(_, v)| v.enabled) .map(|(k, _)| SubjectSyntaxType::from_str(&k.replace("_", ":")).unwrap()) .collect(); - metadata.signing_algorithms_supported = config_2() + metadata.signing_algorithms_supported = config() .signing_algorithms_supported .iter() .filter(|(_, v)| v.enabled) @@ -89,7 +89,7 @@ pub fn load_metadata() -> Metadata { // TODO: vp_formats <= can they be implied or do we need to make them configurable? - metadata.display = config_2().display.clone(); + metadata.display = config().display.clone(); info!("Loaded metadata: {:?}", metadata); diff --git a/agent_verification/src/authorization_request/aggregate.rs b/agent_verification/src/authorization_request/aggregate.rs index 58126dd8..3c61d6f3 100644 --- a/agent_verification/src/authorization_request/aggregate.rs +++ b/agent_verification/src/authorization_request/aggregate.rs @@ -4,7 +4,7 @@ use crate::{ services::VerificationServices, }; use agent_shared::config; -use agent_shared::config::config_2; +use agent_shared::config::config; use async_trait::async_trait; use cqrs_es::Aggregate; use jsonwebtoken::Algorithm; @@ -52,7 +52,7 @@ impl Aggregate for AuthorizationRequest { .await .unwrap(); - let url = config_2().url; + let url = config().url; let request_uri = format!("{url}/request/{state}").parse().unwrap(); let redirect_uri = format!("{url}/redirect").parse::().unwrap(); diff --git a/agent_verification/src/services.rs b/agent_verification/src/services.rs index 6bc453c1..be1934b4 100644 --- a/agent_verification/src/services.rs +++ b/agent_verification/src/services.rs @@ -1,4 +1,4 @@ -use agent_shared::config::{config_2, did_methods_enabled}; +use agent_shared::config::{config, did_methods_enabled}; use jsonwebtoken::Algorithm; use oid4vc_core::{client_metadata::ClientMetadataResource, Subject}; use oid4vc_manager::RelyingPartyManager; @@ -16,7 +16,7 @@ pub struct VerificationServices { impl VerificationServices { pub fn new(verifier: Arc) -> Self { - let default_did_method = config_2() + let default_did_method = config() .did_methods .iter() .filter(|(_, v)| v.preferred.unwrap_or(false)) @@ -28,14 +28,14 @@ impl VerificationServices { .to_owned() .replace("_", ":"); - let client_name = config_2().display.first().as_ref().map(|display| display.name.clone()); + let client_name = config().display.first().as_ref().map(|display| display.name.clone()); - let logo_uri = config_2() + let logo_uri = config() .display .first() .and_then(|display| display.logo.as_ref().and_then(|logo| logo.url.clone())); - let signing_algorithms_supported: Vec = config_2() + let signing_algorithms_supported: Vec = config() .signing_algorithms_supported .iter() .filter(|(_, opts)| opts.enabled) @@ -64,7 +64,7 @@ impl VerificationServices { client_name, logo_uri, extension: oid4vp::authorization_request::ClientMetadataParameters { - vp_formats: config_2() + vp_formats: config() .vp_formats .iter() .filter(|(_, opts)| opts.enabled) From f572d4a015e3e08fce77913900bd6a57f9f2b76d Mon Sep 17 00:00:00 2001 From: Daniel Mader Date: Tue, 16 Jul 2024 17:07:34 +0200 Subject: [PATCH 20/37] chore: change example logo, disable `event_publisher`, respect `default_did_method` --- agent_application/example-config.yaml | 2 +- agent_event_publisher_http/src/lib.rs | 11 +++++++++-- agent_secret_manager/src/services.rs | 13 ++----------- agent_shared/src/config.rs | 7 ++++++- 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/agent_application/example-config.yaml b/agent_application/example-config.yaml index 32b4ff24..99357628 100644 --- a/agent_application/example-config.yaml +++ b/agent_application/example-config.yaml @@ -51,7 +51,7 @@ display: locale: en logo: # This value will also be used in the SIOPv2/OID4VP `client_metadata`'s `logo_uri` property. - uri: https://impierce.com/images/logo-blue.png + uri: "https://impierce.com/images/favicon/apple-touch-icon.png" alt_text: UniCore Logo # agent_issuance/issuance-config.yml diff --git a/agent_event_publisher_http/src/lib.rs b/agent_event_publisher_http/src/lib.rs index fc9921b9..a2902c1c 100644 --- a/agent_event_publisher_http/src/lib.rs +++ b/agent_event_publisher_http/src/lib.rs @@ -61,8 +61,15 @@ impl EventPublisherHttp { info!("event_publisher_http: {:?}", event_publisher_http); - if event_publisher_http.enabled { - // event_publisher_http.publishers + // If it's not enabled, return an empty event publisher. + if !event_publisher_http.enabled { + return Ok(EventPublisherHttp { + server_config: None, + credential: None, + offer: None, + connection: None, + authorization_request: None, + }); } // TODO: map events to aggregates diff --git a/agent_secret_manager/src/services.rs b/agent_secret_manager/src/services.rs index ab8e4865..95d0d914 100644 --- a/agent_secret_manager/src/services.rs +++ b/agent_secret_manager/src/services.rs @@ -1,5 +1,5 @@ use crate::subject::Subject; -use agent_shared::config::config; +use agent_shared::config::{config, did_method_preferred}; use anyhow::Result; use did_manager::SecretManager; @@ -10,16 +10,7 @@ pub struct SecretManagerServices { impl SecretManagerServices { pub fn new(subject: Option) -> Self { - let default_did_method: String = config() - .did_methods - .iter() - .filter(|(_, v)| v.preferred.unwrap_or(false)) - .map(|(k, _)| k.clone()) - .collect::>() - // TODO: should fail when there's more than one result - .first() - .cloned() - .unwrap_or("did:key".to_string()); + let default_did_method = did_method_preferred(); Self { subject, default_did_method, diff --git a/agent_shared/src/config.rs b/agent_shared/src/config.rs index e7e527ba..aeb5346c 100644 --- a/agent_shared/src/config.rs +++ b/agent_shared/src/config.rs @@ -155,6 +155,7 @@ pub fn config() -> ApplicationConfiguration { // TODO: or return -> &'static ApplicationConfiguration, so we don't need to clone on every call } +// TODO: should fail when none is enabled pub fn did_methods_enabled() -> Vec { config() .did_methods @@ -164,6 +165,7 @@ pub fn did_methods_enabled() -> Vec { .collect() } +// TODO: should fail when there's more than one result pub fn did_method_preferred() -> String { config() .did_methods @@ -171,5 +173,8 @@ pub fn did_method_preferred() -> String { .filter(|(_, v)| v.enabled) .filter(|(_, v)| v.preferred.unwrap_or(false)) .map(|(k, _)| k.clone().replace("_", ":")) - .collect() + .collect::>() + .first() + .cloned() + .expect("Please set a DID method as `preferred` in the configuration") } From e63e3a1ee83afdab6bbd3de376dfdea609571b54 Mon Sep 17 00:00:00 2001 From: Daniel Mader Date: Wed, 17 Jul 2024 10:10:29 +0200 Subject: [PATCH 21/37] chore: resolve clippy issues --- .../issuance/credential_issuer/credential.rs | 3 +- agent_api_rest/src/lib.rs | 2 +- agent_event_publisher_http/src/lib.rs | 2 +- agent_issuance/src/credential/aggregate.rs | 2 - agent_issuance/src/server_config/aggregate.rs | 4 +- agent_secret_manager/src/lib.rs | 6 +- agent_shared/src/config.rs | 4 +- agent_shared/src/metadata.rs | 76 +++++++------------ .../src/authorization_request/aggregate.rs | 1 - agent_verification/src/services.rs | 24 +++--- 10 files changed, 48 insertions(+), 76 deletions(-) diff --git a/agent_api_rest/src/issuance/credential_issuer/credential.rs b/agent_api_rest/src/issuance/credential_issuer/credential.rs index 96f955e8..4419dea8 100644 --- a/agent_api_rest/src/issuance/credential_issuer/credential.rs +++ b/agent_api_rest/src/issuance/credential_issuer/credential.rs @@ -10,7 +10,6 @@ use agent_issuance::{ state::{IssuanceState, SERVER_CONFIG_ID}, }; use agent_shared::{ - config, config::config, handlers::{command_handler, query_handler}, }; @@ -234,7 +233,7 @@ mod tests { } }; - std::thread::sleep(Duration::from_millis(delay.try_into().unwrap())); + std::thread::sleep(Duration::from_millis(delay)); // Sends the `CredentialsRequest` to the `credentials` endpoint. app_clone diff --git a/agent_api_rest/src/lib.rs b/agent_api_rest/src/lib.rs index ff9b5bdb..cca5e70a 100644 --- a/agent_api_rest/src/lib.rs +++ b/agent_api_rest/src/lib.rs @@ -2,7 +2,7 @@ mod issuance; mod verification; use agent_issuance::state::IssuanceState; -use agent_shared::{config, config::config, ConfigError}; +use agent_shared::{config::config, ConfigError}; use agent_verification::state::VerificationState; use axum::{ body::Bytes, diff --git a/agent_event_publisher_http/src/lib.rs b/agent_event_publisher_http/src/lib.rs index a2902c1c..e468f06b 100644 --- a/agent_event_publisher_http/src/lib.rs +++ b/agent_event_publisher_http/src/lib.rs @@ -88,7 +88,7 @@ impl EventPublisherHttp { let credential_events: Vec = event_publisher_http .events .credential - .unwrap_or(vec![]) + .unwrap_or_default() .iter() .map(|e| e.to_string()) .collect(); diff --git a/agent_issuance/src/credential/aggregate.rs b/agent_issuance/src/credential/aggregate.rs index 60fc860f..94ac196b 100644 --- a/agent_issuance/src/credential/aggregate.rs +++ b/agent_issuance/src/credential/aggregate.rs @@ -1,7 +1,5 @@ use agent_secret_manager::services::SecretManagerServices; -use agent_shared::config; use agent_shared::config::config; -use agent_shared::metadata::Display; use async_trait::async_trait; use cqrs_es::Aggregate; use derivative::Derivative; diff --git a/agent_issuance/src/server_config/aggregate.rs b/agent_issuance/src/server_config/aggregate.rs index c5fa0821..09603f6c 100644 --- a/agent_issuance/src/server_config/aggregate.rs +++ b/agent_issuance/src/server_config/aggregate.rs @@ -63,14 +63,14 @@ impl Aggregate for ServerConfig { .filter(|(_, v)| v.enabled) .map(|(k, _)| k.clone()) // TODO: find less hacky solution, possibly: enum + serde rename - .map(|did_method| did_method.replace("_", ":")) + .map(|did_method| did_method.replace('_', ":")) .collect(); let signing_algorithms_supported: Vec = config() .signing_algorithms_supported .iter() .filter(|(_, options)| options.enabled) - .map(|(alg, _)| alg.clone()) + .map(|(alg, _)| *alg) .collect(); let proof_types_supported = HashMap::from_iter([( diff --git a/agent_secret_manager/src/lib.rs b/agent_secret_manager/src/lib.rs index 501e3892..41fa1c9f 100644 --- a/agent_secret_manager/src/lib.rs +++ b/agent_secret_manager/src/lib.rs @@ -17,9 +17,11 @@ pub async fn secret_manager() -> SecretManager { match (snapshot_path, password, key_id, issuer_did, issuer_fragment) { (snapshot_path, password, Some(key_id), issuer_did, issuer_fragment) => { - SecretManager::load(snapshot_path, password, key_id, issuer_did, issuer_fragment).await.unwrap() + SecretManager::load(snapshot_path, password, key_id, issuer_did, issuer_fragment) + .await + .unwrap() } (snapshot_path, password, None, _, _) => SecretManager::generate(snapshot_path, password).await.unwrap(), - _ => panic!("Unable to load or generate `SecretManager`. Please make sure to set both `AGENT__SECRET_MANAGER__STRONGHOLD_PATH` and `AGENT__SECRET_MANAGER__STRONGHOLD_PASSWORD` environment variables."), + // _ => panic!("Unable to load or generate `SecretManager`. Please make sure to set both `AGENT__SECRET_MANAGER__STRONGHOLD_PATH` and `AGENT__SECRET_MANAGER__STRONGHOLD_PASSWORD` environment variables."), } } diff --git a/agent_shared/src/config.rs b/agent_shared/src/config.rs index aeb5346c..01b5edef 100644 --- a/agent_shared/src/config.rs +++ b/agent_shared/src/config.rs @@ -161,7 +161,7 @@ pub fn did_methods_enabled() -> Vec { .did_methods .iter() .filter(|(_, v)| v.enabled) - .map(|(k, _)| k.clone().replace("_", ":")) + .map(|(k, _)| k.clone().replace('_', ":")) .collect() } @@ -172,7 +172,7 @@ pub fn did_method_preferred() -> String { .iter() .filter(|(_, v)| v.enabled) .filter(|(_, v)| v.preferred.unwrap_or(false)) - .map(|(k, _)| k.clone().replace("_", ":")) + .map(|(k, _)| k.clone().replace('_', ":")) .collect::>() .first() .cloned() diff --git a/agent_shared/src/metadata.rs b/agent_shared/src/metadata.rs index ac7fd82e..43c3f3c1 100644 --- a/agent_shared/src/metadata.rs +++ b/agent_shared/src/metadata.rs @@ -3,11 +3,11 @@ use oid4vc_core::SubjectSyntaxType; use oid4vp::ClaimFormatDesignation; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; -use std::{collections::HashMap, str::FromStr}; -use tracing::info; +use std::collections::HashMap; +// use tracing::info; use url::Url; -use crate::config::config; +// use crate::config::config; #[skip_serializing_none] #[derive(Serialize, Deserialize, Debug, Clone)] @@ -45,58 +45,38 @@ pub struct Metadata { #[cfg(feature = "test_utils")] pub static TEST_METADATA: std::sync::Mutex> = std::sync::Mutex::new(None); -pub fn load_metadata() -> Metadata { - // #[cfg(feature = "test_utils")] - // let mut config = TEST_METADATA.lock().unwrap().as_ref().unwrap().clone(); - // #[cfg(not(feature = "test_utils"))] - // let mut config: serde_yaml::Value = { - // match std::fs::File::open("agent_application/config.yml") { - // Ok(config_file) => serde_yaml::from_reader(config_file).unwrap(), - // // If the config file does not exist, return an empty config. - // Err(_) => serde_yaml::Value::Null, - // } - // }; - // let mut config: serde_yaml::Value = crate::config::config("AGENT_APPLICATION").try_deserialize().unwrap(); +// fn load_metadata() -> Metadata { +// let mut metadata = Metadata::default(); - // info!("config: {:?}", config); - let mut metadata = Metadata::default(); - // metadata.subject_syntax_types_supported = config_2().did_methods.keys().into_iter().map(|k| k.clone()).collect(); +// metadata.subject_syntax_types_supported = config() +// .did_methods +// .into_iter() +// .filter(|(_, v)| v.enabled) +// .map(|(k, _)| SubjectSyntaxType::from_str(&k.replace('_', ":")).unwrap()) +// .collect(); - // let supported_algorithms: serde_yaml::Value = vec!["EdDSA".to_string()].into(); - // if config["signing_algorithms_supported"] != supported_algorithms { - // unimplemented!( - // "\n{}\nOnly the `EdDSA` signing algorithm is supported", - // serde_yaml::to_string(&config["signing_algorithms_supported"]).unwrap() - // ) - // } +// metadata.signing_algorithms_supported = config() +// .signing_algorithms_supported +// .iter() +// .filter(|(_, v)| v.enabled) +// .map(|(k, _)| k.clone()) +// .collect(); - metadata.subject_syntax_types_supported = config() - .did_methods - .into_iter() - .filter(|(_, v)| v.enabled) - .map(|(k, _)| SubjectSyntaxType::from_str(&k.replace("_", ":")).unwrap()) - .collect(); +// metadata +// .id_token_signing_alg_values_supported +// .clone_from(&metadata.signing_algorithms_supported); +// metadata +// .request_object_signing_alg_values_supported +// .clone_from(&metadata.signing_algorithms_supported); - metadata.signing_algorithms_supported = config() - .signing_algorithms_supported - .iter() - .filter(|(_, v)| v.enabled) - .map(|(k, _)| k.clone()) - .collect(); +// metadata.display.clone_from(&config().display); - metadata.id_token_signing_alg_values_supported = metadata.signing_algorithms_supported.clone(); - metadata.request_object_signing_alg_values_supported = metadata.signing_algorithms_supported.clone(); +// info!("Loaded metadata: {:?}", metadata); - // TODO: vp_formats <= can they be implied or do we need to make them configurable? +// info!("{:?}", serde_json::to_string(&metadata).unwrap()); - metadata.display = config().display.clone(); - - info!("Loaded metadata: {:?}", metadata); - - info!("{:?}", serde_json::to_string(&metadata).unwrap()); - - metadata -} +// metadata +// } #[cfg(feature = "test_utils")] pub fn set_metadata_configuration(default_did_method: &str) { diff --git a/agent_verification/src/authorization_request/aggregate.rs b/agent_verification/src/authorization_request/aggregate.rs index 3c61d6f3..418e4864 100644 --- a/agent_verification/src/authorization_request/aggregate.rs +++ b/agent_verification/src/authorization_request/aggregate.rs @@ -3,7 +3,6 @@ use crate::{ generic_oid4vc::{GenericAuthorizationRequest, OID4VPAuthorizationRequest, SIOPv2AuthorizationRequest}, services::VerificationServices, }; -use agent_shared::config; use agent_shared::config::config; use async_trait::async_trait; use cqrs_es::Aggregate; diff --git a/agent_verification/src/services.rs b/agent_verification/src/services.rs index be1934b4..3f7192fe 100644 --- a/agent_verification/src/services.rs +++ b/agent_verification/src/services.rs @@ -1,4 +1,4 @@ -use agent_shared::config::{config, did_methods_enabled}; +use agent_shared::config::{config, did_method_preferred, did_methods_enabled}; use jsonwebtoken::Algorithm; use oid4vc_core::{client_metadata::ClientMetadataResource, Subject}; use oid4vc_manager::RelyingPartyManager; @@ -16,18 +16,6 @@ pub struct VerificationServices { impl VerificationServices { pub fn new(verifier: Arc) -> Self { - let default_did_method = config() - .did_methods - .iter() - .filter(|(_, v)| v.preferred.unwrap_or(false)) - .map(|(k, _)| k.clone()) - .collect::>() - // TODO: throw error if more than one preferred DID method is found - .first() - .unwrap() - .to_owned() - .replace("_", ":"); - let client_name = config().display.first().as_ref().map(|display| display.name.clone()); let logo_uri = config() @@ -103,10 +91,16 @@ impl VerificationServices { )]), }; + let default_subject_syntax_type = did_method_preferred(); + Self { verifier: verifier.clone(), - relying_party: RelyingPartyManager::new(verifier, default_did_method, signing_algorithms_supported) - .unwrap(), + relying_party: RelyingPartyManager::new( + verifier, + default_subject_syntax_type, + signing_algorithms_supported, + ) + .unwrap(), siopv2_client_metadata, oid4vp_client_metadata, } From 5bf7de419474c9fdd21200a29e1a848c424e22e3 Mon Sep 17 00:00:00 2001 From: Daniel Mader Date: Wed, 17 Jul 2024 22:44:56 +0200 Subject: [PATCH 22/37] refactor: remove `set_metadata_configuration` --- .../issuance/credential_issuer/credential.rs | 8 ++- .../src/issuance/credential_issuer/token.rs | 3 - .../well_known/oauth_authorization_server.rs | 3 - .../well_known/openid_credential_issuer.rs | 4 +- agent_api_rest/src/issuance/credentials.rs | 3 - agent_api_rest/src/issuance/offers.rs | 3 - .../verification/authorization_requests.rs | 3 - .../verification/relying_party/redirect.rs | 3 - agent_application/example-config.yaml | 1 - agent_issuance/src/credential/aggregate.rs | 3 - agent_issuance/src/server_config/aggregate.rs | 3 - agent_shared/tests/test-config.yaml | 63 +++++++++++++++++++ 12 files changed, 69 insertions(+), 31 deletions(-) create mode 100644 agent_shared/tests/test-config.yaml diff --git a/agent_api_rest/src/issuance/credential_issuer/credential.rs b/agent_api_rest/src/issuance/credential_issuer/credential.rs index 4419dea8..53d2a617 100644 --- a/agent_api_rest/src/issuance/credential_issuer/credential.rs +++ b/agent_api_rest/src/issuance/credential_issuer/credential.rs @@ -141,7 +141,6 @@ pub(crate) async fn credential( #[cfg(test)] mod tests { - use agent_shared::metadata::set_metadata_configuration; use std::sync::Arc; use crate::{ @@ -276,13 +275,16 @@ mod tests { #[case] is_self_signed: bool, #[case] delay: u64, ) { - set_metadata_configuration("did:key"); - let (external_server, issuance_event_publishers, verification_event_publishers) = if with_external_server { let external_server = MockServer::start().await; let target_url = format!("{}/ssi-events-subscriber", &external_server.uri()); + // std::env::set_var("TEST_AGENT__EVENT_PUBLISHERS__HTTP__TARGET_URL", target_url.clone()); + // std::env::set_var("TEST_AGENT__DID_METHODS__DID_JWK__PREFERRED", "false".to_string()); + // std::env::set_var("TEST_AGENT__DID_METHODS__DID_KEY__PREFERRED", "true".to_string()); + // reload_config(); + TEST_EVENT_PUBLISHER_HTTP_CONFIG.lock().unwrap().replace( serde_yaml::from_str(&format!( r#" diff --git a/agent_api_rest/src/issuance/credential_issuer/token.rs b/agent_api_rest/src/issuance/credential_issuer/token.rs index 2f10d116..b225a2c3 100644 --- a/agent_api_rest/src/issuance/credential_issuer/token.rs +++ b/agent_api_rest/src/issuance/credential_issuer/token.rs @@ -68,7 +68,6 @@ pub mod tests { use super::*; use agent_issuance::{startup_commands::startup_commands, state::initialize}; - use agent_shared::metadata::set_metadata_configuration; use agent_store::in_memory; use agent_verification::services::test_utils::test_verification_services; use axum::{ @@ -111,8 +110,6 @@ pub mod tests { #[tokio::test] async fn test_token_endpoint() { - set_metadata_configuration("did:key"); - let issuance_state = in_memory::issuance_state(Default::default()).await; let verification_state = in_memory::verification_state(test_verification_services(), Default::default()).await; initialize(&issuance_state, startup_commands(BASE_URL.clone())).await; diff --git a/agent_api_rest/src/issuance/credential_issuer/well_known/oauth_authorization_server.rs b/agent_api_rest/src/issuance/credential_issuer/well_known/oauth_authorization_server.rs index dde97ff9..62585fe5 100644 --- a/agent_api_rest/src/issuance/credential_issuer/well_known/oauth_authorization_server.rs +++ b/agent_api_rest/src/issuance/credential_issuer/well_known/oauth_authorization_server.rs @@ -27,7 +27,6 @@ mod tests { use super::*; use agent_issuance::{startup_commands::startup_commands, state::initialize}; - use agent_shared::metadata::set_metadata_configuration; use agent_store::in_memory; use agent_verification::services::test_utils::test_verification_services; use axum::{ @@ -71,8 +70,6 @@ mod tests { #[tokio::test] async fn test_oauth_authorization_server_endpoint() { - set_metadata_configuration("did:key"); - let issuance_state = in_memory::issuance_state(Default::default()).await; let verification_state = in_memory::verification_state(test_verification_services(), Default::default()).await; initialize(&issuance_state, startup_commands(BASE_URL.clone())).await; diff --git a/agent_api_rest/src/issuance/credential_issuer/well_known/openid_credential_issuer.rs b/agent_api_rest/src/issuance/credential_issuer/well_known/openid_credential_issuer.rs index 2bbfcece..793fb5d2 100644 --- a/agent_api_rest/src/issuance/credential_issuer/well_known/openid_credential_issuer.rs +++ b/agent_api_rest/src/issuance/credential_issuer/well_known/openid_credential_issuer.rs @@ -29,7 +29,7 @@ mod tests { use super::*; use agent_issuance::{startup_commands::startup_commands, state::initialize}; - use agent_shared::{metadata::set_metadata_configuration, UrlAppendHelpers}; + use agent_shared::UrlAppendHelpers; use agent_store::in_memory; use agent_verification::services::test_utils::test_verification_services; use axum::{ @@ -130,8 +130,6 @@ mod tests { #[tokio::test] async fn test_openid_credential_issuer_endpoint() { - set_metadata_configuration("did:key"); - let issuance_state = in_memory::issuance_state(Default::default()).await; let verification_state = in_memory::verification_state(test_verification_services(), Default::default()).await; initialize(&issuance_state, startup_commands(BASE_URL.clone())).await; diff --git a/agent_api_rest/src/issuance/credentials.rs b/agent_api_rest/src/issuance/credentials.rs index 968c32a5..8136d010 100644 --- a/agent_api_rest/src/issuance/credentials.rs +++ b/agent_api_rest/src/issuance/credentials.rs @@ -159,7 +159,6 @@ pub mod tests { tests::{BASE_URL, CREDENTIAL_CONFIGURATION_ID, OFFER_ID}, }; use agent_issuance::{startup_commands::startup_commands, state::initialize}; - use agent_shared::metadata::set_metadata_configuration; use agent_store::in_memory; use agent_verification::services::test_utils::test_verification_services; use axum::{ @@ -253,8 +252,6 @@ pub mod tests { #[tokio::test] #[tracing_test::traced_test] async fn test_credentials_endpoint() { - set_metadata_configuration("did:key"); - let issuance_state = in_memory::issuance_state(Default::default()).await; let verification_state = in_memory::verification_state(test_verification_services(), Default::default()).await; initialize(&issuance_state, startup_commands(BASE_URL.clone())).await; diff --git a/agent_api_rest/src/issuance/offers.rs b/agent_api_rest/src/issuance/offers.rs index bd17d331..795d0c05 100644 --- a/agent_api_rest/src/issuance/offers.rs +++ b/agent_api_rest/src/issuance/offers.rs @@ -92,7 +92,6 @@ pub mod tests { use super::*; use crate::API_VERSION; use agent_issuance::{startup_commands::startup_commands, state::initialize}; - use agent_shared::metadata::set_metadata_configuration; use agent_store::in_memory; use agent_verification::services::test_utils::test_verification_services; use axum::{ @@ -155,8 +154,6 @@ pub mod tests { #[tokio::test] #[tracing_test::traced_test] async fn test_offers_endpoint() { - set_metadata_configuration("did:key"); - let issuance_state = in_memory::issuance_state(Default::default()).await; let verification_state = in_memory::verification_state(test_verification_services(), Default::default()).await; diff --git a/agent_api_rest/src/verification/authorization_requests.rs b/agent_api_rest/src/verification/authorization_requests.rs index ea30432f..6e0b6cb7 100644 --- a/agent_api_rest/src/verification/authorization_requests.rs +++ b/agent_api_rest/src/verification/authorization_requests.rs @@ -139,7 +139,6 @@ pub(crate) async fn authorization_requests( pub mod tests { use super::*; use crate::app; - use agent_shared::metadata::set_metadata_configuration; use agent_store::in_memory; use agent_verification::services::test_utils::test_verification_services; use axum::{ @@ -221,8 +220,6 @@ pub mod tests { #[tokio::test] #[tracing_test::traced_test] async fn test_authorization_requests_endpoint(#[case] by_value: bool) { - set_metadata_configuration("did:key"); - let issuance_state = in_memory::issuance_state(Default::default()).await; let verification_state = in_memory::verification_state(test_verification_services(), Default::default()).await; let mut app = app((issuance_state, verification_state)); diff --git a/agent_api_rest/src/verification/relying_party/redirect.rs b/agent_api_rest/src/verification/relying_party/redirect.rs index e69a9566..577c60a8 100644 --- a/agent_api_rest/src/verification/relying_party/redirect.rs +++ b/agent_api_rest/src/verification/relying_party/redirect.rs @@ -63,7 +63,6 @@ pub mod tests { }; use agent_event_publisher_http::{EventPublisherHttp, TEST_EVENT_PUBLISHER_HTTP_CONFIG}; use agent_secret_manager::{secret_manager, subject::Subject}; - use agent_shared::metadata::set_metadata_configuration; use agent_store::{in_memory, EventPublisher}; use agent_verification::services::test_utils::test_verification_services; use axum::{ @@ -142,8 +141,6 @@ pub mod tests { #[tokio::test(flavor = "multi_thread")] #[tracing_test::traced_test] async fn test_redirect_endpoint() { - set_metadata_configuration("did:key"); - let mock_server = MockServer::start().await; Mock::given(method("POST")) diff --git a/agent_application/example-config.yaml b/agent_application/example-config.yaml index 99357628..f3461dcb 100644 --- a/agent_application/example-config.yaml +++ b/agent_application/example-config.yaml @@ -54,7 +54,6 @@ display: uri: "https://impierce.com/images/favicon/apple-touch-icon.png" alt_text: UniCore Logo -# agent_issuance/issuance-config.yml credential_configurations: - credential_configuration_id: w3c_vc_credential format: jwt_vc_json diff --git a/agent_issuance/src/credential/aggregate.rs b/agent_issuance/src/credential/aggregate.rs index 94ac196b..77966264 100644 --- a/agent_issuance/src/credential/aggregate.rs +++ b/agent_issuance/src/credential/aggregate.rs @@ -264,7 +264,6 @@ pub mod credential_tests { use super::*; - use agent_shared::metadata::set_metadata_configuration; use lazy_static::lazy_static; use oid4vci::proof::KeyProofMetadata; use oid4vci::ProofType; @@ -296,8 +295,6 @@ pub mod credential_tests { #[case] credential_configuration: CredentialConfigurationsSupportedObject, #[case] unsigned_credential: serde_json::Value, ) { - set_metadata_configuration("did:key"); - CredentialTestFramework::with(CredentialServices) .given_no_previous_events() .when(CredentialCommand::CreateUnsignedCredential { diff --git a/agent_issuance/src/server_config/aggregate.rs b/agent_issuance/src/server_config/aggregate.rs index 09603f6c..c4f6f503 100644 --- a/agent_issuance/src/server_config/aggregate.rs +++ b/agent_issuance/src/server_config/aggregate.rs @@ -140,7 +140,6 @@ pub mod server_config_tests { use super::*; use agent_shared::issuance::CredentialConfiguration; - use agent_shared::metadata::set_metadata_configuration; use lazy_static::lazy_static; use oid4vci::credential_format_profiles::w3c_verifiable_credentials::jwt_vc_json::JwtVcJson; use oid4vci::credential_format_profiles::{w3c_verifiable_credentials, CredentialFormats, Parameters}; @@ -169,8 +168,6 @@ pub mod server_config_tests { } #[test] fn test_create_credentials_supported() { - set_metadata_configuration("did:key"); - ServerConfigTestFramework::with(ServerConfigServices) .given(vec![ServerConfigEvent::ServerMetadataInitialized { authorization_server_metadata: AUTHORIZATION_SERVER_METADATA.clone(), diff --git a/agent_shared/tests/test-config.yaml b/agent_shared/tests/test-config.yaml new file mode 100644 index 00000000..a00c1a3e --- /dev/null +++ b/agent_shared/tests/test-config.yaml @@ -0,0 +1,63 @@ +log_format: "text" +event_store: + type: "in_memory" +url: "https://my-domain.example.org" + +did_methods: + did_jwk: + enabled: true + # preferred: true + did_key: + enabled: true + preferred: true + did_iota_rms: + enabled: false + did_web: + enabled: false + +domain_linkage_enabled: false + +signing_algorithms_supported: + eddsa: + enabled: true + +vp_formats: + jwt_vc_json: + enabled: true + jwt_vp_json: + enabled: true + +event_publishers: + http: + enabled: false + target_url: "http://localhost" + events: + server_config: [] + credential: [UnsignedCredentialCreated, CredentialSigned] + +display: + - name: UniCore + locale: en + logo: + uri: "https://impierce.com/images/favicon/apple-touch-icon.png" + alt_text: UniCore Logo + +credential_configurations: + - credential_configuration_id: w3c_vc_credential + format: jwt_vc_json + credential_definition: + type: + - VerifiableCredential + display: + - name: Verifiable Credential + locale: en + logo: + url: https://impierce.com/images/logo-blue.png + alt_text: UniCore Logo + +secret_manager: + stronghold_path: "../agent_secret_manager/tests/res/test.stronghold" + stronghold_password: "secure_password" + issuer_key_id: "9O66nzWqYYy1LmmiOudOlh2SMIaUWoTS" + issuer_did: "did:iota:rms:0x42ad588322e58b3c07aa39e4948d021ee17ecb5747915e9e1f35f028d7ecaf90" + issuer_fragment: "bQKQRzaop7CgEvqVq8UlgLGsdF-R-hnLFkKFZqW2VN0" From d62f3b82dee9ee4f1392a6503b8061a2d4c8192b Mon Sep 17 00:00:00 2001 From: Daniel Mader Date: Thu, 18 Jul 2024 16:57:33 +0200 Subject: [PATCH 23/37] refactor: remove `TEST_METADATA` --- agent_secret_manager/src/services.rs | 4 +- agent_shared/src/config.rs | 71 +++++++++++++------ agent_shared/src/metadata.rs | 71 ------------------- agent_shared/tests/test-config.yaml | 8 +-- .../src/authorization_request/aggregate.rs | 13 +++- agent_verification/src/services.rs | 8 +-- 6 files changed, 71 insertions(+), 104 deletions(-) diff --git a/agent_secret_manager/src/services.rs b/agent_secret_manager/src/services.rs index 95d0d914..995455bf 100644 --- a/agent_secret_manager/src/services.rs +++ b/agent_secret_manager/src/services.rs @@ -1,5 +1,5 @@ use crate::subject::Subject; -use agent_shared::config::{config, did_method_preferred}; +use agent_shared::config::{config, get_preferred_did_method}; use anyhow::Result; use did_manager::SecretManager; @@ -10,7 +10,7 @@ pub struct SecretManagerServices { impl SecretManagerServices { pub fn new(subject: Option) -> Self { - let default_did_method = did_method_preferred(); + let default_did_method = get_preferred_did_method(); Self { subject, default_did_method, diff --git a/agent_shared/src/config.rs b/agent_shared/src/config.rs index 01b5edef..29e1f6e5 100644 --- a/agent_shared/src/config.rs +++ b/agent_shared/src/config.rs @@ -1,8 +1,8 @@ use config::ConfigError; use oid4vp::ClaimFormatDesignation; -use once_cell::sync::OnceCell; +use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; +use std::{collections::HashMap, sync::Mutex}; use tracing::info; use crate::{issuance::CredentialConfiguration, metadata::Display}; @@ -14,7 +14,7 @@ pub struct ApplicationConfiguration { pub url: String, pub base_path: Option, pub cors_enabled: Option, - pub did_methods: HashMap, + pub did_methods: HashMap, pub external_server_response_timeout_ms: Option, pub domain_linkage_enabled: bool, pub secret_manager: SecretManagerConfig, @@ -119,13 +119,18 @@ pub enum AuthorizationRequestEvent { AuthorizationRequestObjectSigned, } -// pub enum DidMethod { -// #[serde(rename = "did:jwk")] -// Jwk, -// Key, -// Web, -// IotaRms, -// } +/// All DID methods supported by UniCore +#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)] +pub enum DidMethod { + #[serde(rename = "did:jwk", alias = "did_jwk")] + Jwk, + #[serde(rename = "did:key")] + Key, + #[serde(rename = "did:web")] + Web, + #[serde(rename = "did:iota:rms")] + IotaRms, +} /// Generic options that add an "enabled" field and a "preferred" field (optional) to a configuration. #[derive(Debug, Deserialize, Default, Clone)] @@ -134,47 +139,71 @@ pub struct ToggleOptions { pub preferred: Option, } -static CONFIG: OnceCell = OnceCell::new(); +// pub static CONFIG: OnceCell = OnceCell::new(); + +pub static CONFIG: Lazy> = + Lazy::new(|| Mutex::new(ApplicationConfiguration::new().unwrap())); impl ApplicationConfiguration { pub fn new() -> Result { dotenvy::dotenv().ok(); info!("Environment variables loaded."); info!("Loading application configuration ..."); - let config = config::Config::builder() - .add_source(config::File::with_name("agent_application/example-config.yaml")) - .add_source(config::Environment::with_prefix("AGENT").separator("__")) - .build()?; + + println!("Current directory: {:?}", std::env::current_dir().unwrap()); + + let config = if cfg!(feature = "test_utils") { + config::Config::builder() + .add_source(config::File::with_name("../agent_shared/tests/test-config.yaml")) + // TODO: other prefix for tests + .add_source(config::Environment::with_prefix("TEST_AGENT").separator("__")) + .build()? + } else { + config::Config::builder() + .add_source(config::File::with_name("agent_application/example-config.yaml")) + .add_source(config::Environment::with_prefix("AGENT").separator("__")) + .build()? + }; + config.try_deserialize() } } /// Returns the application configuration or loads it, if it hasn't been loaded already. pub fn config() -> ApplicationConfiguration { - CONFIG.get_or_init(|| ApplicationConfiguration::new().unwrap()).clone() + // CONFIG.get_or_init(|| ApplicationConfiguration::new().unwrap()).clone() + CONFIG.lock().unwrap().clone() // TODO: or return -> &'static ApplicationConfiguration, so we don't need to clone on every call } +/// Reloads the config. Useful for testing after overwriting a env variable. +pub fn reload_config() { + // CONFIG.set(ApplicationConfiguration::new().unwrap()).unwrap(); + // CONFIG.lock().unwrap().clone() +} + // TODO: should fail when none is enabled -pub fn did_methods_enabled() -> Vec { +pub fn get_all_enabled_did_methods() -> Vec { config() .did_methods .iter() .filter(|(_, v)| v.enabled) - .map(|(k, _)| k.clone().replace('_', ":")) + .map(|(k, _)| k.clone()) .collect() } // TODO: should fail when there's more than one result -pub fn did_method_preferred() -> String { +pub fn get_preferred_did_method() -> DidMethod { config() .did_methods .iter() .filter(|(_, v)| v.enabled) .filter(|(_, v)| v.preferred.unwrap_or(false)) - .map(|(k, _)| k.clone().replace('_', ":")) - .collect::>() + .map(|(k, _)| k.clone()) + .collect::>() .first() .cloned() .expect("Please set a DID method as `preferred` in the configuration") } + +pub fn set_preferred_did_method() {} diff --git a/agent_shared/src/metadata.rs b/agent_shared/src/metadata.rs index 43c3f3c1..eeaa38cf 100644 --- a/agent_shared/src/metadata.rs +++ b/agent_shared/src/metadata.rs @@ -4,11 +4,8 @@ use oid4vp::ClaimFormatDesignation; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; use std::collections::HashMap; -// use tracing::info; use url::Url; -// use crate::config::config; - #[skip_serializing_none] #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Logo { @@ -41,71 +38,3 @@ pub struct Metadata { #[serde(default)] pub display: Vec, } - -#[cfg(feature = "test_utils")] -pub static TEST_METADATA: std::sync::Mutex> = std::sync::Mutex::new(None); - -// fn load_metadata() -> Metadata { -// let mut metadata = Metadata::default(); - -// metadata.subject_syntax_types_supported = config() -// .did_methods -// .into_iter() -// .filter(|(_, v)| v.enabled) -// .map(|(k, _)| SubjectSyntaxType::from_str(&k.replace('_', ":")).unwrap()) -// .collect(); - -// metadata.signing_algorithms_supported = config() -// .signing_algorithms_supported -// .iter() -// .filter(|(_, v)| v.enabled) -// .map(|(k, _)| k.clone()) -// .collect(); - -// metadata -// .id_token_signing_alg_values_supported -// .clone_from(&metadata.signing_algorithms_supported); -// metadata -// .request_object_signing_alg_values_supported -// .clone_from(&metadata.signing_algorithms_supported); - -// metadata.display.clone_from(&config().display); - -// info!("Loaded metadata: {:?}", metadata); - -// info!("{:?}", serde_json::to_string(&metadata).unwrap()); - -// metadata -// } - -#[cfg(feature = "test_utils")] -pub fn set_metadata_configuration(default_did_method: &str) { - // Set the test configuration. - TEST_METADATA.lock().unwrap().replace( - serde_yaml::from_str(&format!( - r#" - subject_syntax_types_supported: - - {default_did_method} - - did:key - - did:iota:rms - - did:jwk - signing_algorithms_supported: &signing_algorithms_supported - - EdDSA - id_token_signing_alg_values_supported: *signing_algorithms_supported - request_object_signing_alg_values_supported: *signing_algorithms_supported - vp_formats: - jwt_vc_json: - alg: *signing_algorithms_supported - jwt_vp_json: - alg: *signing_algorithms_supported - display: - - name: UniCore - locale: en - logo: - uri: https://impierce.com/images/logo-blue.png - alt_text: UniCore Logo - "# - )) - .unwrap(), - ); -} diff --git a/agent_shared/tests/test-config.yaml b/agent_shared/tests/test-config.yaml index a00c1a3e..808500a4 100644 --- a/agent_shared/tests/test-config.yaml +++ b/agent_shared/tests/test-config.yaml @@ -43,7 +43,7 @@ display: alt_text: UniCore Logo credential_configurations: - - credential_configuration_id: w3c_vc_credential + - credential_configuration_id: badge format: jwt_vc_json credential_definition: type: @@ -56,8 +56,8 @@ credential_configurations: alt_text: UniCore Logo secret_manager: - stronghold_path: "../agent_secret_manager/tests/res/test.stronghold" - stronghold_password: "secure_password" - issuer_key_id: "9O66nzWqYYy1LmmiOudOlh2SMIaUWoTS" + stronghold_path: "../agent_secret_manager/tests/res/selv.stronghold" + stronghold_password: "VNvRtH4tKyWwvJDpL6Vuc2aoLiKAecGQ" + issuer_key_id: "UVDxWhG2rB39FkaR7I27mHeUNrGtUgcr" issuer_did: "did:iota:rms:0x42ad588322e58b3c07aa39e4948d021ee17ecb5747915e9e1f35f028d7ecaf90" issuer_fragment: "bQKQRzaop7CgEvqVq8UlgLGsdF-R-hnLFkKFZqW2VN0" diff --git a/agent_verification/src/authorization_request/aggregate.rs b/agent_verification/src/authorization_request/aggregate.rs index 418e4864..be7c9c21 100644 --- a/agent_verification/src/authorization_request/aggregate.rs +++ b/agent_verification/src/authorization_request/aggregate.rs @@ -162,6 +162,8 @@ pub mod tests { use agent_secret_manager::secret_manager; use agent_secret_manager::subject::Subject; + use agent_shared::config::DidMethod; + use agent_shared::config::CONFIG; use agent_shared::metadata::set_metadata_configuration; use cqrs_es::test::TestFramework; use lazy_static::lazy_static; @@ -180,9 +182,16 @@ pub mod tests { #[rstest] #[serial_test::serial] async fn test_create_authorization_request( - #[values("did:key", "did:jwk", "did:iota:rms")] verifier_did_method: &str, + #[values(DidMethod::Key, DidMethod::Jwk, DidMethod::IotaRms)] verifier_did_method: DidMethod, ) { - set_metadata_configuration(verifier_did_method); + // set_metadata_configuration(verifier_did_method); + CONFIG + .lock() + .unwrap() + .did_methods + .get_mut(&verifier_did_method) + .unwrap() + .preferred = Some(true); let verification_services = test_verification_services(); let siopv2_client_metadata = verification_services.siopv2_client_metadata.clone(); diff --git a/agent_verification/src/services.rs b/agent_verification/src/services.rs index 3f7192fe..7eed2bbd 100644 --- a/agent_verification/src/services.rs +++ b/agent_verification/src/services.rs @@ -1,4 +1,4 @@ -use agent_shared::config::{config, did_method_preferred, did_methods_enabled}; +use agent_shared::config::{config, get_all_enabled_did_methods, get_preferred_did_method}; use jsonwebtoken::Algorithm; use oid4vc_core::{client_metadata::ClientMetadataResource, Subject}; use oid4vc_manager::RelyingPartyManager; @@ -36,7 +36,7 @@ impl VerificationServices { client_name: client_name.clone(), logo_uri: logo_uri.clone(), extension: siopv2::authorization_request::ClientMetadataParameters { - subject_syntax_types_supported: did_methods_enabled() + subject_syntax_types_supported: get_all_enabled_did_methods() .iter() .map(|method| oid4vc_core::SubjectSyntaxType::from_str(method).unwrap()) .collect(), @@ -87,11 +87,11 @@ impl VerificationServices { }, other: HashMap::from_iter([( "subject_syntax_types_supported".to_string(), - json!(did_methods_enabled()), + json!(get_all_enabled_did_methods()), )]), }; - let default_subject_syntax_type = did_method_preferred(); + let default_subject_syntax_type = get_preferred_did_method(); Self { verifier: verifier.clone(), From 7ff29a233dcdf9a16d6659058c898c687eb7da6d Mon Sep 17 00:00:00 2001 From: Nander Stabel Date: Mon, 22 Jul 2024 16:20:26 +0200 Subject: [PATCH 24/37] WIP --- .../issuance/credential_issuer/credential.rs | 65 ++++++++--- .../well_known/openid_credential_issuer.rs | 21 ++-- agent_api_rest/src/lib.rs | 1 + agent_application/src/main.rs | 10 +- agent_event_publisher_http/src/lib.rs | 36 +++--- agent_issuance/src/credential/aggregate.rs | 9 +- agent_issuance/src/server_config/aggregate.rs | 5 +- agent_secret_manager/src/lib.rs | 14 ++- agent_secret_manager/src/services.rs | 21 ++-- .../tests/res/temp.stronghold | Bin 0 -> 623 bytes agent_shared/src/config.rs | 104 ++++++++++++++---- .../src/authorization_request/aggregate.rs | 40 +++---- .../src/connection/aggregate.rs | 16 ++- agent_verification/src/services.rs | 4 +- 14 files changed, 222 insertions(+), 124 deletions(-) create mode 100644 agent_secret_manager/tests/res/temp.stronghold diff --git a/agent_api_rest/src/issuance/credential_issuer/credential.rs b/agent_api_rest/src/issuance/credential_issuer/credential.rs index 53d2a617..638c10c7 100644 --- a/agent_api_rest/src/issuance/credential_issuer/credential.rs +++ b/agent_api_rest/src/issuance/credential_issuer/credential.rs @@ -42,6 +42,8 @@ pub(crate) async fn credential( _ => return StatusCode::UNAUTHORIZED.into_response(), }; + info!("test1"); + // Get the `credential_issuer_metadata` and `authorization_server_metadata` from the `ServerConfigView`. let (credential_issuer_metadata, authorization_server_metadata) = match query_handler(SERVER_CONFIG_ID, &state.query.server_config).await { @@ -52,6 +54,8 @@ pub(crate) async fn credential( _ => return StatusCode::INTERNAL_SERVER_ERROR.into_response(), }; + info!("test2"); + let command = OfferCommand::VerifyCredentialRequest { offer_id: offer_id.clone(), credential_issuer_metadata, @@ -59,16 +63,23 @@ pub(crate) async fn credential( credential_request, }; + info!("test3"); + // Use the `offer_id` to verify the `proof` inside the `CredentialRequest`. if command_handler(&offer_id, &state.command.offer, command).await.is_err() { StatusCode::INTERNAL_SERVER_ERROR.into_response(); }; + info!("test4"); + let timeout = config() .external_server_response_timeout_ms .unwrap_or(DEFAULT_EXTERNAL_SERVER_RESPONSE_TIMEOUT_MS); let start_time = Instant::now(); + info!("time out: {}", timeout); + info!("test5"); + // TODO: replace this polling solution with a call to the `TxChannelRegistry` as described here: https://github.com/impierce/ssi-agent/issues/75 // Use the `offer_id` to get the `credential_ids` and `subject_id` from the `OfferView`. let (credential_ids, subject_id) = loop { @@ -93,6 +104,8 @@ pub(crate) async fn credential( } }; + info!("test6"); + // Use the `credential_ids` and `subject_id` to sign all the credentials. let mut signed_credentials = vec![]; for credential_id in credential_ids { @@ -119,16 +132,22 @@ pub(crate) async fn credential( signed_credentials.push(signed_credential); } + info!("test7"); + let command = OfferCommand::CreateCredentialResponse { offer_id: offer_id.clone(), signed_credentials, }; + info!("test8"); + // Use the `offer_id` to create a `CredentialResponse` from the `CredentialRequest` and `credentials`. if command_handler(&offer_id, &state.command.offer, command).await.is_err() { StatusCode::INTERNAL_SERVER_ERROR.into_response(); }; + info!("test9"); + // Use the `offer_id` to get the `credential_response` from the `OfferView`. match query_handler(&offer_id, &state.query.offer).await { Ok(Some(OfferView { @@ -156,6 +175,7 @@ mod tests { use crate::API_VERSION; use agent_event_publisher_http::{EventPublisherHttp, TEST_EVENT_PUBLISHER_HTTP_CONFIG}; use agent_issuance::{offer::event::OfferEvent, startup_commands::startup_commands, state::initialize}; + use agent_shared::config::{set_config, Events}; use agent_store::{in_memory, EventPublisher}; use agent_verification::services::test_utils::test_verification_services; use axum::{ @@ -193,6 +213,7 @@ mod tests { is_self_signed: bool, delay: u64, ) { + println!("HEEERRREE1"); Mock::given(method("POST")) .and(path("/ssi-events-subscriber")) .and( @@ -201,9 +222,13 @@ mod tests { OfferEvent::CredentialRequestVerified { offer_id, subject_id } => { let app_clone = app.clone(); - futures::executor::block_on(async { + let temp = futures::executor::block_on(async { + println!("testA"); + let app_clone = app_clone.lock().await.take().unwrap(); + println!("testB"); + // This assertion is a representation of the 'outside' backend server retrieving the // data that corresponds to the `offer_id`. assert_eq!(offer_id, OFFER_ID); @@ -232,8 +257,12 @@ mod tests { } }; + println!("testC"); + std::thread::sleep(Duration::from_millis(delay)); + println!("testD"); + // Sends the `CredentialsRequest` to the `credentials` endpoint. app_clone .oneshot( @@ -250,6 +279,8 @@ mod tests { }) .unwrap(); + println!("{}", temp.status()); + true } _ => false, @@ -285,21 +316,12 @@ mod tests { // std::env::set_var("TEST_AGENT__DID_METHODS__DID_KEY__PREFERRED", "true".to_string()); // reload_config(); - TEST_EVENT_PUBLISHER_HTTP_CONFIG.lock().unwrap().replace( - serde_yaml::from_str(&format!( - r#" - target_url: &target_url {target_url} - - offer: {{ - target_url: *target_url, - target_events: [ - CredentialRequestVerified - ] - }} - "#, - )) - .unwrap(), - ); + set_config().enable_event_publisher_http(); + set_config().set_event_publisher_http_target_url(target_url.clone()); + set_config().set_event_publisher_http_target_events(Events { + offer: Some(vec![agent_shared::config::OfferEvent::CredentialRequestVerified]), + ..Default::default() + }); ( Some(external_server), @@ -310,6 +332,17 @@ mod tests { (None, Default::default(), Default::default()) }; + println!("issuance_event_publishers: {:#?}", EventPublisherHttp::load().unwrap()); + + println!("CONFIG: {:#?}", config()); + println!( + "with_external_server: {:#?}, external_server: {}", + with_external_server, + external_server.is_some() + ); + println!("is_self_signed: {:#?}", is_self_signed); + println!("delay: {:#?}", delay); + let issuance_state = in_memory::issuance_state(issuance_event_publishers).await; let verification_state = in_memory::verification_state(test_verification_services(), verification_event_publishers).await; diff --git a/agent_api_rest/src/issuance/credential_issuer/well_known/openid_credential_issuer.rs b/agent_api_rest/src/issuance/credential_issuer/well_known/openid_credential_issuer.rs index 793fb5d2..1b0b1836 100644 --- a/agent_api_rest/src/issuance/credential_issuer/well_known/openid_credential_issuer.rs +++ b/agent_api_rest/src/issuance/credential_issuer/well_known/openid_credential_issuer.rs @@ -29,7 +29,7 @@ mod tests { use super::*; use agent_issuance::{startup_commands::startup_commands, state::initialize}; - use agent_shared::UrlAppendHelpers; + use agent_shared::{config::config, UrlAppendHelpers}; use agent_store::in_memory; use agent_verification::services::test_utils::test_verification_services; use axum::{ @@ -90,12 +90,7 @@ mod tests { .into(), }), scope: None, - cryptographic_binding_methods_supported: vec![ - "did:key".to_string(), - "did:key".to_string(), - "did:iota:rms".to_string(), - "did:jwk".to_string(), - ], + cryptographic_binding_methods_supported: vec!["did:jwk".to_string(), "did:key".to_string(),], credential_signing_alg_values_supported: vec!["EdDSA".to_string()], proof_types_supported: HashMap::from_iter([( ProofType::Jwt, @@ -104,10 +99,12 @@ mod tests { }, )]), display: vec![json!({ - "name": "Badge", - "logo": { - "url": "https://example.com/logo.png", - } + "name": "Verifiable Credential", + "locale": "en", + "logo": { + "url": "https://impierce.com/images/logo-blue.png", + "alt_text": "UniCore Logo" + } })], } )] @@ -117,7 +114,7 @@ mod tests { "name": "UniCore", "locale": "en", "logo": { - "url": "https://impierce.com/images/logo-blue.png", + "url": "https://impierce.com/images/favicon/apple-touch-icon.png", "alt_text": "UniCore Logo" } })]), diff --git a/agent_api_rest/src/lib.rs b/agent_api_rest/src/lib.rs index cca5e70a..f5c948ec 100644 --- a/agent_api_rest/src/lib.rs +++ b/agent_api_rest/src/lib.rs @@ -101,6 +101,7 @@ pub fn app(state: ApplicationState) -> Router { fn get_base_path() -> Result { config() .base_path + .clone() .ok_or_else(|| ConfigError::NotFound("No configuration for `base_path` found".to_string())) .map(|mut base_path| { if base_path.starts_with('/') { diff --git a/agent_application/src/main.rs b/agent_application/src/main.rs index 3387b8d5..f68a5c2f 100644 --- a/agent_application/src/main.rs +++ b/agent_application/src/main.rs @@ -3,7 +3,7 @@ use agent_event_publisher_http::EventPublisherHttp; use agent_issuance::{startup_commands::startup_commands, state::initialize}; use agent_secret_manager::{secret_manager, subject::Subject}; use agent_shared::{ - config::{config, LogFormat, ToggleOptions}, + config::{config, LogFormat, SupportedDidMethod, ToggleOptions}, domain_linkage::create_did_configuration_resource, }; use agent_store::{in_memory, postgres, EventPublisher}; @@ -44,7 +44,7 @@ async fn main() -> io::Result<()> { let (issuance_state, verification_state) = match agent_shared::config::config().event_store.type_ { agent_shared::config::EventStoreType::Postgres => { - let connection_string = config().event_store.connection_string.expect( + let connection_string = config().event_store.connection_string.clone().expect( "Missing config parameter `event_store.connection_string` or `AGENT__EVENT_STORE__CONNECTION_STRING`", ); ( @@ -61,11 +61,11 @@ async fn main() -> io::Result<()> { info!("{:?}", config()); - let url = config().url; + let url = &config().url; info!("Application url: {:?}", url); - let url = url::Url::parse(&url).unwrap(); + let url = url::Url::parse(url).unwrap(); initialize(&issuance_state, startup_commands(url.clone())).await; @@ -80,7 +80,7 @@ async fn main() -> io::Result<()> { // did:web let enable_did_web = config() .did_methods - .get("did_web") + .get(&SupportedDidMethod::Web) .unwrap_or(&ToggleOptions::default()) .enabled; diff --git a/agent_event_publisher_http/src/lib.rs b/agent_event_publisher_http/src/lib.rs index e468f06b..8363f398 100644 --- a/agent_event_publisher_http/src/lib.rs +++ b/agent_event_publisher_http/src/lib.rs @@ -49,7 +49,7 @@ impl EventPublisherHttp { // }; // let config = agent_shared::config::config("AGENT_EVENT_PUBLISHER_HTTP"); - let event_publisher_http = config().event_publishers.unwrap().http.unwrap(); + let event_publisher_http = config().event_publishers.clone().unwrap().http.unwrap(); // let event_publishers = config.get_table("event_publishers").unwrap_or_default(); // let event_publisher_http = event_publishers @@ -59,7 +59,7 @@ impl EventPublisherHttp { // .into_table() // .unwrap_or_default(); - info!("event_publisher_http: {:?}", event_publisher_http); + println!("event_publisher_http: {:?}", event_publisher_http); // If it's not enabled, return an empty event publisher. if !event_publisher_http.enabled { @@ -87,20 +87,20 @@ impl EventPublisherHttp { let credential_events: Vec = event_publisher_http .events - .credential + .offer .unwrap_or_default() .iter() .map(|e| e.to_string()) .collect(); - info!("credential_events: {:?}", credential_events); + println!("credential_events: {:?}", credential_events); let event_publisher: EventPublisherHttp = EventPublisherHttp { server_config: None, - credential: Some(AggregateEventPublisherHttp::new( + credential: None, + offer: Some(AggregateEventPublisherHttp::new( event_publisher_http.target_url.clone(), credential_events, )), - offer: None, connection: None, authorization_request: None, }; @@ -234,6 +234,7 @@ mod tests { use super::*; use agent_issuance::offer::event::OfferEvent; + use agent_shared::config::{set_config, Events}; use wiremock::matchers::{method, path}; use wiremock::{Mock, MockServer, ResponseTemplate}; @@ -250,21 +251,14 @@ mod tests { let target_url = format!("{}/ssi-events-subscriber", &mock_server.uri()); // Set the test configuration. - TEST_EVENT_PUBLISHER_HTTP_CONFIG.lock().unwrap().replace( - serde_yaml::from_str(&format!( - r#" - target_url: &target_url {target_url} - - offer: {{ - target_url: *target_url, - target_events: [ - FormUrlEncodedCredentialOfferCreated - ] - }} - "# - )) - .unwrap(), - ); + set_config().enable_event_publisher_http(); + set_config().set_event_publisher_http_target_url(target_url.clone()); + set_config().set_event_publisher_http_target_events(Events { + offer: Some(vec![ + agent_shared::config::OfferEvent::FormUrlEncodedCredentialOfferCreated, + ]), + ..Default::default() + }); let publisher = EventPublisherHttp::load().unwrap(); diff --git a/agent_issuance/src/credential/aggregate.rs b/agent_issuance/src/credential/aggregate.rs index 77966264..4cfa302f 100644 --- a/agent_issuance/src/credential/aggregate.rs +++ b/agent_issuance/src/credential/aggregate.rs @@ -86,7 +86,7 @@ impl Aggregate for Credential { .clone(); let issuer: Profile = ProfileBuilder::default() - .id(config().url) + .id(config().url.clone()) .type_("Profile") .name(name) .try_into() @@ -184,7 +184,10 @@ impl Aggregate for Credential { services.init().await.unwrap(); (Arc::new(services.subject.unwrap()), services.default_did_method.clone()) }; - let issuer_did = issuer.identifier(&default_did_method, Algorithm::EdDSA).await.unwrap(); + let issuer_did = issuer + .identifier(&default_did_method.to_string(), Algorithm::EdDSA) + .await + .unwrap(); let signed_credential = { let mut credential = self.data.as_ref().ok_or(MissingCredentialDataError)?.clone(); @@ -224,7 +227,7 @@ impl Aggregate for Credential { .verifiable_credential(credential.raw) .build() .ok(), - &default_did_method + &default_did_method.to_string() ) .await .ok()) diff --git a/agent_issuance/src/server_config/aggregate.rs b/agent_issuance/src/server_config/aggregate.rs index c4f6f503..9afd2383 100644 --- a/agent_issuance/src/server_config/aggregate.rs +++ b/agent_issuance/src/server_config/aggregate.rs @@ -60,10 +60,7 @@ impl Aggregate for ServerConfig { let cryptographic_binding_methods_supported = config() .did_methods .iter() - .filter(|(_, v)| v.enabled) - .map(|(k, _)| k.clone()) - // TODO: find less hacky solution, possibly: enum + serde rename - .map(|did_method| did_method.replace('_', ":")) + .filter_map(|(did_method, options)| options.enabled.then(|| did_method.to_string())) .collect(); let signing_algorithms_supported: Vec = config() diff --git a/agent_secret_manager/src/lib.rs b/agent_secret_manager/src/lib.rs index 41fa1c9f..56722829 100644 --- a/agent_secret_manager/src/lib.rs +++ b/agent_secret_manager/src/lib.rs @@ -1,4 +1,4 @@ -use agent_shared::config::config; +use agent_shared::config::{config, SecretManagerConfig}; use did_manager::SecretManager; pub mod aggregate; @@ -9,11 +9,13 @@ pub mod subject; // TODO: find better solution for this pub async fn secret_manager() -> SecretManager { - let snapshot_path = config().secret_manager.stronghold_path; - let password = config().secret_manager.stronghold_password; - let key_id = config().secret_manager.issuer_key_id; - let issuer_did = config().secret_manager.issuer_did; - let issuer_fragment = config().secret_manager.issuer_fragment; + let SecretManagerConfig { + stronghold_path: snapshot_path, + stronghold_password: password, + issuer_key_id: key_id, + issuer_did, + issuer_fragment, + } = config().secret_manager.clone(); match (snapshot_path, password, key_id, issuer_did, issuer_fragment) { (snapshot_path, password, Some(key_id), issuer_did, issuer_fragment) => { diff --git a/agent_secret_manager/src/services.rs b/agent_secret_manager/src/services.rs index 995455bf..79512b4a 100644 --- a/agent_secret_manager/src/services.rs +++ b/agent_secret_manager/src/services.rs @@ -1,11 +1,11 @@ use crate::subject::Subject; -use agent_shared::config::{config, get_preferred_did_method}; +use agent_shared::config::{config, get_preferred_did_method, SecretManagerConfig, SupportedDidMethod}; use anyhow::Result; use did_manager::SecretManager; pub struct SecretManagerServices { pub subject: Option, - pub default_did_method: String, + pub default_did_method: SupportedDidMethod, } impl SecretManagerServices { @@ -18,14 +18,15 @@ impl SecretManagerServices { } pub async fn init(&mut self) -> Result<(), std::io::Error> { - let snapshot_path = config().secret_manager.stronghold_path; - let password = config().secret_manager.stronghold_password; - let key_id = config() - .secret_manager - .issuer_key_id - .expect("Missing configuration: secret_manager.issuer_key_id"); - let issuer_did = config().secret_manager.issuer_did; - let issuer_fragment = config().secret_manager.issuer_fragment; + let SecretManagerConfig { + stronghold_path: snapshot_path, + stronghold_password: password, + issuer_key_id, + issuer_did, + issuer_fragment, + } = config().secret_manager.clone(); + + let key_id = issuer_key_id.expect("Missing configuration: secret_manager.issuer_key_id"); let secret_manager = SecretManager::load(snapshot_path, password, key_id, issuer_did, issuer_fragment) .await diff --git a/agent_secret_manager/tests/res/temp.stronghold b/agent_secret_manager/tests/res/temp.stronghold new file mode 100644 index 0000000000000000000000000000000000000000..0075d2b329d0061661067b027cbe4adb7e25d03e GIT binary patch literal 623 zcmV-#0+9VsK~hvn0{~%XWi4fHV{&s7WCnhAyGUx3pbH)ky2^OJ0zDe+D{(mSEN~3 zo(z6j4d4HIi2lqZf-S1xZbJ#{i)`_pd!PLdD9Krg1-5)O6EDv#UnpE+VXUCvD>2r2 zZwU@H+l8hlP&JLv&vVdX*vVU}}5AZqvJY4uhwFl9N=1mrxz0k!X z5cUql(8R@&=!}trhEefMLXHp`1O&pBZoDm_y+G$A_Y}q8;bik=uOn_9fj6D=iHNH< zMvLf#AoU=|f1(cKN9tQI!_SH_#1t}HrMa!B|H8o5q16G<%7d_VSfFvy$pGYmy2pss znSKey;sxDl2~iFY(jUw~*%_ksa4CwNYc#OR?uI*@q~< JS5JZYf?yGp8q@#) literal 0 HcmV?d00001 diff --git a/agent_shared/src/config.rs b/agent_shared/src/config.rs index 29e1f6e5..35f3f8d4 100644 --- a/agent_shared/src/config.rs +++ b/agent_shared/src/config.rs @@ -2,7 +2,11 @@ use config::ConfigError; use oid4vp::ClaimFormatDesignation; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, sync::Mutex}; +use serde_with::SerializeDisplay; +use std::{ + collections::HashMap, + sync::{Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard}, +}; use tracing::info; use crate::{issuance::CredentialConfiguration, metadata::Display}; @@ -14,7 +18,7 @@ pub struct ApplicationConfiguration { pub url: String, pub base_path: Option, pub cors_enabled: Option, - pub did_methods: HashMap, + pub did_methods: HashMap, pub external_server_response_timeout_ms: Option, pub domain_linkage_enabled: bool, pub secret_manager: SecretManagerConfig, @@ -25,6 +29,48 @@ pub struct ApplicationConfiguration { pub vp_formats: HashMap, } +impl ApplicationConfiguration { + pub fn set_preferred_did_method(&mut self, preferred_did_method: SupportedDidMethod) { + // Set the current preferred did_method to false if available. + if let Some((_, options)) = self.did_methods.iter_mut().find(|(_, v)| v.preferred == Some(true)) { + options.preferred = Some(false); + } + + // Set the current preferred did_method to true if available. + self.did_methods + .entry(preferred_did_method) + .or_insert_with(|| ToggleOptions { + enabled: true, + preferred: Some(true), + }) + .preferred = Some(true); + } + + pub fn enable_event_publisher_http(&mut self) { + if let Some(event_publishers) = &mut self.event_publishers { + if let Some(http) = &mut event_publishers.http { + http.enabled = true; + } + } + } + + pub fn set_event_publisher_http_target_url(&mut self, target_url: String) { + if let Some(event_publishers) = &mut self.event_publishers { + if let Some(http) = &mut event_publishers.http { + http.target_url = target_url; + } + } + } + + pub fn set_event_publisher_http_target_events(&mut self, events: Events) { + if let Some(event_publishers) = &mut self.event_publishers { + if let Some(http) = &mut event_publishers.http { + http.events = events; + } + } + } +} + #[derive(Debug, Deserialize, Clone, Default)] #[serde(rename_all = "lowercase")] pub enum LogFormat { @@ -44,7 +90,7 @@ pub struct EventStoreConfig { #[serde(rename_all = "snake_case")] pub enum EventStoreType { InMemory, - // Postgres(EventStorePostgresConfig), // <== TODO: "config-rs" panicks with "unreachable code", other solution? + // Postgres(EventStorePostgresConfig), // <== TODO: "config-rs" panics with "unreachable code", other solution? Postgres, } @@ -74,7 +120,7 @@ pub struct EventPublisherHttp { pub events: Events, } -#[derive(Debug, Deserialize, Clone)] +#[derive(Debug, Deserialize, Clone, Default)] pub struct Events { pub server_config: Option>, pub credential: Option>, @@ -120,15 +166,33 @@ pub enum AuthorizationRequestEvent { } /// All DID methods supported by UniCore -#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)] -pub enum DidMethod { - #[serde(rename = "did:jwk", alias = "did_jwk")] +/// ``` +/// use agent_shared::config::SupportedDidMethod; +/// use serde_json::json; +/// +/// let supported_did_method: SupportedDidMethod = serde_json::from_value(json!("did_jwk")).unwrap(); +/// assert_eq!(supported_did_method, SupportedDidMethod::Jwk); +/// assert_eq!(supported_did_method.to_string(), "did:jwk"); +/// ``` +#[derive(Debug, Deserialize, Clone, Eq, PartialEq, Hash, strum::EnumString, strum::Display, SerializeDisplay)] +pub enum SupportedDidMethod { + #[serde(alias = "did_jwk", rename = "did_jwk")] + #[strum(serialize = "did:jwk")] Jwk, - #[serde(rename = "did:key")] + #[serde(alias = "did_key", rename = "did_key")] + #[strum(serialize = "did:key")] Key, - #[serde(rename = "did:web")] + #[serde(alias = "did_web", rename = "did_web")] + #[strum(serialize = "did:web")] Web, - #[serde(rename = "did:iota:rms")] + #[serde(alias = "did_iota", rename = "did_iota")] + #[strum(serialize = "did:iota")] + Iota, + #[serde(alias = "did_iota_smr", rename = "did_iota_smr")] + #[strum(serialize = "did:iota:smr")] + IotaSmr, + #[serde(alias = "did_iota_rms", rename = "did_iota_rms")] + #[strum(serialize = "did:iota:rms")] IotaRms, } @@ -141,8 +205,8 @@ pub struct ToggleOptions { // pub static CONFIG: OnceCell = OnceCell::new(); -pub static CONFIG: Lazy> = - Lazy::new(|| Mutex::new(ApplicationConfiguration::new().unwrap())); +pub static CONFIG: Lazy> = + Lazy::new(|| RwLock::new(ApplicationConfiguration::new().unwrap())); impl ApplicationConfiguration { pub fn new() -> Result { @@ -170,12 +234,16 @@ impl ApplicationConfiguration { } /// Returns the application configuration or loads it, if it hasn't been loaded already. -pub fn config() -> ApplicationConfiguration { +pub fn config<'a>() -> RwLockReadGuard<'a, ApplicationConfiguration> { // CONFIG.get_or_init(|| ApplicationConfiguration::new().unwrap()).clone() - CONFIG.lock().unwrap().clone() + CONFIG.read().unwrap() // TODO: or return -> &'static ApplicationConfiguration, so we don't need to clone on every call } +pub fn set_config<'a>() -> RwLockWriteGuard<'a, ApplicationConfiguration> { + CONFIG.write().unwrap() +} + /// Reloads the config. Useful for testing after overwriting a env variable. pub fn reload_config() { // CONFIG.set(ApplicationConfiguration::new().unwrap()).unwrap(); @@ -183,7 +251,7 @@ pub fn reload_config() { } // TODO: should fail when none is enabled -pub fn get_all_enabled_did_methods() -> Vec { +pub fn get_all_enabled_did_methods() -> Vec { config() .did_methods .iter() @@ -193,17 +261,15 @@ pub fn get_all_enabled_did_methods() -> Vec { } // TODO: should fail when there's more than one result -pub fn get_preferred_did_method() -> DidMethod { +pub fn get_preferred_did_method() -> SupportedDidMethod { config() .did_methods .iter() .filter(|(_, v)| v.enabled) .filter(|(_, v)| v.preferred.unwrap_or(false)) .map(|(k, _)| k.clone()) - .collect::>() + .collect::>() .first() .cloned() .expect("Please set a DID method as `preferred` in the configuration") } - -pub fn set_preferred_did_method() {} diff --git a/agent_verification/src/authorization_request/aggregate.rs b/agent_verification/src/authorization_request/aggregate.rs index be7c9c21..e34faa82 100644 --- a/agent_verification/src/authorization_request/aggregate.rs +++ b/agent_verification/src/authorization_request/aggregate.rs @@ -51,7 +51,7 @@ impl Aggregate for AuthorizationRequest { .await .unwrap(); - let url = config().url; + let url = &config().url; let request_uri = format!("{url}/request/{state}").parse().unwrap(); let redirect_uri = format!("{url}/redirect").parse::().unwrap(); @@ -162,9 +162,8 @@ pub mod tests { use agent_secret_manager::secret_manager; use agent_secret_manager::subject::Subject; - use agent_shared::config::DidMethod; - use agent_shared::config::CONFIG; - use agent_shared::metadata::set_metadata_configuration; + use agent_shared::config::set_config; + use agent_shared::config::SupportedDidMethod; use cqrs_es::test::TestFramework; use lazy_static::lazy_static; use oid4vc_core::Subject as _; @@ -182,16 +181,10 @@ pub mod tests { #[rstest] #[serial_test::serial] async fn test_create_authorization_request( - #[values(DidMethod::Key, DidMethod::Jwk, DidMethod::IotaRms)] verifier_did_method: DidMethod, + #[values(SupportedDidMethod::Key, SupportedDidMethod::Jwk, SupportedDidMethod::IotaRms)] + verifier_did_method: SupportedDidMethod, ) { - // set_metadata_configuration(verifier_did_method); - CONFIG - .lock() - .unwrap() - .did_methods - .get_mut(&verifier_did_method) - .unwrap() - .preferred = Some(true); + set_config().set_preferred_did_method(verifier_did_method.clone()); let verification_services = test_verification_services(); let siopv2_client_metadata = verification_services.siopv2_client_metadata.clone(); @@ -209,7 +202,7 @@ pub mod tests { authorization_request: Box::new( authorization_request( "id_token", - verifier_did_method, + &verifier_did_method.to_string(), siopv2_client_metadata, oid4vp_client_metadata, ) @@ -217,7 +210,9 @@ pub mod tests { ), }, AuthorizationRequestEvent::FormUrlEncodedAuthorizationRequestCreated { - form_url_encoded_authorization_request: form_url_encoded_authorization_request(verifier_did_method), + form_url_encoded_authorization_request: form_url_encoded_authorization_request( + &verifier_did_method.to_string(), + ), }, ]); } @@ -225,9 +220,10 @@ pub mod tests { #[rstest] #[serial_test::serial] async fn test_sign_authorization_request_object( - #[values("did:key", "did:jwk", "did:iota:rms")] verifier_did_method: &str, + #[values(SupportedDidMethod::Key, SupportedDidMethod::Jwk, SupportedDidMethod::IotaRms)] + verifier_did_method: SupportedDidMethod, ) { - set_metadata_configuration(verifier_did_method); + set_config().set_preferred_did_method(verifier_did_method.clone()); let verification_services = test_verification_services(); let siopv2_client_metadata = verification_services.siopv2_client_metadata.clone(); @@ -239,7 +235,7 @@ pub mod tests { authorization_request: Box::new( authorization_request( "id_token", - verifier_did_method, + &verifier_did_method.to_string(), siopv2_client_metadata, oid4vp_client_metadata, ) @@ -247,12 +243,16 @@ pub mod tests { ), }, AuthorizationRequestEvent::FormUrlEncodedAuthorizationRequestCreated { - form_url_encoded_authorization_request: form_url_encoded_authorization_request(verifier_did_method), + form_url_encoded_authorization_request: form_url_encoded_authorization_request( + &verifier_did_method.to_string(), + ), }, ]) .when(AuthorizationRequestCommand::SignAuthorizationRequestObject) .then_expect_events(vec![AuthorizationRequestEvent::AuthorizationRequestObjectSigned { - signed_authorization_request_object: signed_authorization_request_object(verifier_did_method), + signed_authorization_request_object: signed_authorization_request_object( + &verifier_did_method.to_string(), + ), }]); } diff --git a/agent_verification/src/connection/aggregate.rs b/agent_verification/src/connection/aggregate.rs index 104d5d6b..312afb8c 100644 --- a/agent_verification/src/connection/aggregate.rs +++ b/agent_verification/src/connection/aggregate.rs @@ -100,11 +100,12 @@ pub mod tests { use agent_secret_manager::secret_manager; use agent_secret_manager::subject::Subject; - use agent_shared::metadata::set_metadata_configuration; + use agent_shared::config::SupportedDidMethod; use cqrs_es::test::TestFramework; use identity_credential::credential::Jwt; use identity_credential::presentation::Presentation; + use agent_shared::config::set_config; use jsonwebtoken::Algorithm; use oid4vc_manager::managers::presentation::create_presentation_submission; use oid4vc_manager::ProviderManager; @@ -128,10 +129,12 @@ pub mod tests { // "id_token" represents the `SIOPv2` flow, and "vp_token" represents the `OID4VP` flow. #[values("id_token", "vp_token")] response_type: &str, // TODO: add `did:web`, check for other tests as well. Probably should be moved to E2E test. - #[values("did:key", "did:jwk", "did:iota:rms")] verifier_did_method: &str, - #[values("did:key", "did:jwk", "did:iota:rms")] provider_did_method: &str, + #[values(SupportedDidMethod::Key, SupportedDidMethod::Jwk, SupportedDidMethod::IotaRms)] + verifier_did_method: SupportedDidMethod, + #[values(SupportedDidMethod::Key, SupportedDidMethod::Jwk, SupportedDidMethod::IotaRms)] + provider_did_method: SupportedDidMethod, ) { - set_metadata_configuration(verifier_did_method); + set_config().set_preferred_did_method(verifier_did_method.clone()); let verification_services = test_verification_services(); let siopv2_client_metadata = verification_services.siopv2_client_metadata.clone(); @@ -139,13 +142,14 @@ pub mod tests { let authorization_request = authorization_request( response_type, - verifier_did_method, + &verifier_did_method.to_string(), siopv2_client_metadata, oid4vp_client_metadata, ) .await; - let authorization_response = authorization_response(provider_did_method, &authorization_request).await; + let authorization_response = + authorization_response(&provider_did_method.to_string(), &authorization_request).await; let token = authorization_response.token(); ConnectionTestFramework::with(verification_services) diff --git a/agent_verification/src/services.rs b/agent_verification/src/services.rs index 7eed2bbd..5f839e39 100644 --- a/agent_verification/src/services.rs +++ b/agent_verification/src/services.rs @@ -38,7 +38,7 @@ impl VerificationServices { extension: siopv2::authorization_request::ClientMetadataParameters { subject_syntax_types_supported: get_all_enabled_did_methods() .iter() - .map(|method| oid4vc_core::SubjectSyntaxType::from_str(method).unwrap()) + .map(|method| oid4vc_core::SubjectSyntaxType::from_str(&method.to_string()).unwrap()) .collect(), id_token_signed_response_alg: signing_algorithms_supported.first().cloned(), }, @@ -97,7 +97,7 @@ impl VerificationServices { verifier: verifier.clone(), relying_party: RelyingPartyManager::new( verifier, - default_subject_syntax_type, + default_subject_syntax_type.to_string(), signing_algorithms_supported, ) .unwrap(), From f0610aa1d16481b6f5c380be9fa3273d633e4c37 Mon Sep 17 00:00:00 2001 From: Nander Stabel Date: Tue, 23 Jul 2024 12:32:15 +0200 Subject: [PATCH 25/37] test: fix tests --- .../issuance/credential_issuer/credential.rs | 4 +- .../well_known/openid_credential_issuer.rs | 8 +- .../verification/relying_party/redirect.rs | 24 ++--- agent_application/src/main.rs | 2 + agent_event_publisher_http/src/lib.rs | 87 ++++++++++++++----- agent_issuance/src/server_config/aggregate.rs | 10 ++- agent_shared/src/config.rs | 51 ++++++++--- agent_shared/src/lib.rs | 1 - agent_shared/src/metadata.rs | 40 --------- agent_shared/tests/test-config.yaml | 2 +- .../src/authorization_request/aggregate.rs | 6 +- 11 files changed, 133 insertions(+), 102 deletions(-) delete mode 100644 agent_shared/src/metadata.rs diff --git a/agent_api_rest/src/issuance/credential_issuer/credential.rs b/agent_api_rest/src/issuance/credential_issuer/credential.rs index 638c10c7..ed15278e 100644 --- a/agent_api_rest/src/issuance/credential_issuer/credential.rs +++ b/agent_api_rest/src/issuance/credential_issuer/credential.rs @@ -173,7 +173,7 @@ mod tests { use super::*; use crate::issuance::credentials::tests::credentials; use crate::API_VERSION; - use agent_event_publisher_http::{EventPublisherHttp, TEST_EVENT_PUBLISHER_HTTP_CONFIG}; + use agent_event_publisher_http::EventPublisherHttp; use agent_issuance::{offer::event::OfferEvent, startup_commands::startup_commands, state::initialize}; use agent_shared::config::{set_config, Events}; use agent_store::{in_memory, EventPublisher}; @@ -319,7 +319,7 @@ mod tests { set_config().enable_event_publisher_http(); set_config().set_event_publisher_http_target_url(target_url.clone()); set_config().set_event_publisher_http_target_events(Events { - offer: Some(vec![agent_shared::config::OfferEvent::CredentialRequestVerified]), + offer: vec![agent_shared::config::OfferEvent::CredentialRequestVerified], ..Default::default() }); diff --git a/agent_api_rest/src/issuance/credential_issuer/well_known/openid_credential_issuer.rs b/agent_api_rest/src/issuance/credential_issuer/well_known/openid_credential_issuer.rs index 1b0b1836..4878c11f 100644 --- a/agent_api_rest/src/issuance/credential_issuer/well_known/openid_credential_issuer.rs +++ b/agent_api_rest/src/issuance/credential_issuer/well_known/openid_credential_issuer.rs @@ -29,7 +29,7 @@ mod tests { use super::*; use agent_issuance::{startup_commands::startup_commands, state::initialize}; - use agent_shared::{config::config, UrlAppendHelpers}; + use agent_shared::UrlAppendHelpers; use agent_store::in_memory; use agent_verification::services::test_utils::test_verification_services; use axum::{ @@ -90,7 +90,11 @@ mod tests { .into(), }), scope: None, - cryptographic_binding_methods_supported: vec!["did:jwk".to_string(), "did:key".to_string(),], + cryptographic_binding_methods_supported: vec![ + "did:iota:rms".to_string(), + "did:jwk".to_string(), + "did:key".to_string() + ], credential_signing_alg_values_supported: vec!["EdDSA".to_string()], proof_types_supported: HashMap::from_iter([( ProofType::Jwt, diff --git a/agent_api_rest/src/verification/relying_party/redirect.rs b/agent_api_rest/src/verification/relying_party/redirect.rs index 577c60a8..968920d9 100644 --- a/agent_api_rest/src/verification/relying_party/redirect.rs +++ b/agent_api_rest/src/verification/relying_party/redirect.rs @@ -61,8 +61,9 @@ pub mod tests { app, verification::{authorization_requests::tests::authorization_requests, relying_party::request::tests::request}, }; - use agent_event_publisher_http::{EventPublisherHttp, TEST_EVENT_PUBLISHER_HTTP_CONFIG}; + use agent_event_publisher_http::EventPublisherHttp; use agent_secret_manager::{secret_manager, subject::Subject}; + use agent_shared::config::{set_config, Events}; use agent_store::{in_memory, EventPublisher}; use agent_verification::services::test_utils::test_verification_services; use axum::{ @@ -151,21 +152,12 @@ pub mod tests { let target_url = format!("{}/ssi-events-subscriber", &mock_server.uri()); - TEST_EVENT_PUBLISHER_HTTP_CONFIG.lock().unwrap().replace( - serde_yaml::from_str(&format!( - r#" - target_url: &target_url {target_url} - - connection: {{ - target_url: *target_url, - target_events: [ - SIOPv2AuthorizationResponseVerified - ] - }} - "#, - )) - .unwrap(), - ); + set_config().enable_event_publisher_http(); + set_config().set_event_publisher_http_target_url(target_url.clone()); + set_config().set_event_publisher_http_target_events(Events { + connection: vec![agent_shared::config::ConnectionEvent::SIOPv2AuthorizationResponseVerified], + ..Default::default() + }); let event_publishers = vec![Box::new(EventPublisherHttp::load().unwrap()) as Box]; diff --git a/agent_application/src/main.rs b/agent_application/src/main.rs index f68a5c2f..231bfa45 100644 --- a/agent_application/src/main.rs +++ b/agent_application/src/main.rs @@ -1,3 +1,5 @@ +#![allow(clippy::await_holding_lock)] + use agent_api_rest::app; use agent_event_publisher_http::EventPublisherHttp; use agent_issuance::{startup_commands::startup_commands, state::initialize}; diff --git a/agent_event_publisher_http/src/lib.rs b/agent_event_publisher_http/src/lib.rs index 8363f398..b4abcd87 100644 --- a/agent_event_publisher_http/src/lib.rs +++ b/agent_event_publisher_http/src/lib.rs @@ -13,9 +13,6 @@ use serde::Deserialize; use serde_with::skip_serializing_none; use tracing::info; -#[cfg(feature = "test_utils")] -pub static TEST_EVENT_PUBLISHER_HTTP_CONFIG: std::sync::Mutex> = std::sync::Mutex::new(None); - /// A struct that contains all the event publishers for the different aggregates. #[skip_serializing_none] #[derive(Debug, Deserialize)] @@ -85,24 +82,72 @@ impl EventPublisherHttp { // (Event::SIOPv2AuthorizationResponseVerified, DomainAggregate::Connection), // ]); - let credential_events: Vec = event_publisher_http - .events - .offer - .unwrap_or_default() - .iter() - .map(|e| e.to_string()) - .collect(); - println!("credential_events: {:?}", credential_events); + let server_config = (!event_publisher_http.events.server_config.is_empty()).then(|| { + AggregateEventPublisherHttp::::new( + event_publisher_http.target_url.clone(), + event_publisher_http + .events + .server_config + .iter() + .map(ToString::to_string) + .collect(), + ) + }); - let event_publisher: EventPublisherHttp = EventPublisherHttp { - server_config: None, - credential: None, - offer: Some(AggregateEventPublisherHttp::new( + let credential = (!event_publisher_http.events.offer.is_empty()).then(|| { + AggregateEventPublisherHttp::::new( + event_publisher_http.target_url.clone(), + event_publisher_http + .events + .offer + .iter() + .map(ToString::to_string) + .collect(), + ) + }); + + let offer = (!event_publisher_http.events.offer.is_empty()).then(|| { + AggregateEventPublisherHttp::::new( + event_publisher_http.target_url.clone(), + event_publisher_http + .events + .offer + .iter() + .map(ToString::to_string) + .collect(), + ) + }); + + let connection = (!event_publisher_http.events.connection.is_empty()).then(|| { + AggregateEventPublisherHttp::::new( + event_publisher_http.target_url.clone(), + event_publisher_http + .events + .connection + .iter() + .map(ToString::to_string) + .collect(), + ) + }); + + let authorization_request = (!event_publisher_http.events.authorization_request.is_empty()).then(|| { + AggregateEventPublisherHttp::::new( event_publisher_http.target_url.clone(), - credential_events, - )), - connection: None, - authorization_request: None, + event_publisher_http + .events + .authorization_request + .iter() + .map(ToString::to_string) + .collect(), + ) + }); + + let event_publisher: EventPublisherHttp = EventPublisherHttp { + server_config, + credential, + offer, + connection, + authorization_request, }; info!("Loaded HTTP event publisher: {:?}", event_publisher); @@ -254,9 +299,7 @@ mod tests { set_config().enable_event_publisher_http(); set_config().set_event_publisher_http_target_url(target_url.clone()); set_config().set_event_publisher_http_target_events(Events { - offer: Some(vec![ - agent_shared::config::OfferEvent::FormUrlEncodedCredentialOfferCreated, - ]), + offer: vec![agent_shared::config::OfferEvent::FormUrlEncodedCredentialOfferCreated], ..Default::default() }); diff --git a/agent_issuance/src/server_config/aggregate.rs b/agent_issuance/src/server_config/aggregate.rs index 9afd2383..f5271ebf 100644 --- a/agent_issuance/src/server_config/aggregate.rs +++ b/agent_issuance/src/server_config/aggregate.rs @@ -57,12 +57,15 @@ impl Aggregate for ServerConfig { AddCredentialConfiguration { credential_configuration, } => { - let cryptographic_binding_methods_supported = config() + let mut cryptographic_binding_methods_supported: Vec<_> = config() .did_methods .iter() - .filter_map(|(did_method, options)| options.enabled.then(|| did_method.to_string())) + .filter(|&(_, options)| options.enabled) + .map(|(did_method, _)| did_method.to_string()) .collect(); + cryptographic_binding_methods_supported.sort(); + let signing_algorithms_supported: Vec = config() .signing_algorithms_supported .iter() @@ -198,10 +201,9 @@ pub mod server_config_tests { serde_json::from_value(json!({ "format": "jwt_vc_json", "cryptographic_binding_methods_supported": [ - "did:key", - "did:key", "did:iota:rms", "did:jwk", + "did:key", ], "credential_signing_alg_values_supported": [ "EdDSA" diff --git a/agent_shared/src/config.rs b/agent_shared/src/config.rs index 35f3f8d4..ce3098e7 100644 --- a/agent_shared/src/config.rs +++ b/agent_shared/src/config.rs @@ -2,14 +2,15 @@ use config::ConfigError; use oid4vp::ClaimFormatDesignation; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; -use serde_with::SerializeDisplay; +use serde_with::{skip_serializing_none, SerializeDisplay}; use std::{ collections::HashMap, - sync::{Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard}, + sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}, }; use tracing::info; +use url::Url; -use crate::{issuance::CredentialConfiguration, metadata::Display}; +use crate::issuance::CredentialConfiguration; #[derive(Debug, Deserialize, Clone)] pub struct ApplicationConfiguration { @@ -108,6 +109,23 @@ pub struct SecretManagerConfig { pub issuer_fragment: Option, } +#[skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Logo { + // TODO: remove this alias and change field to `uri`. + #[serde(alias = "uri")] + pub url: Option, + pub alt_text: Option, +} + +#[skip_serializing_none] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Display { + pub name: String, + pub locale: Option, + pub logo: Option, +} + #[derive(Debug, Deserialize, Clone)] pub struct EventPublishers { pub http: Option, @@ -122,11 +140,16 @@ pub struct EventPublisherHttp { #[derive(Debug, Deserialize, Clone, Default)] pub struct Events { - pub server_config: Option>, - pub credential: Option>, - pub offer: Option>, - pub connection: Option>, - pub authorization_request: Option>, + #[serde(default)] + pub server_config: Vec, + #[serde(default)] + pub credential: Vec, + #[serde(default)] + pub offer: Vec, + #[serde(default)] + pub connection: Vec, + #[serde(default)] + pub authorization_request: Vec, } #[derive(Debug, Serialize, Deserialize, Clone, strum::Display)] @@ -174,7 +197,9 @@ pub enum AuthorizationRequestEvent { /// assert_eq!(supported_did_method, SupportedDidMethod::Jwk); /// assert_eq!(supported_did_method.to_string(), "did:jwk"); /// ``` -#[derive(Debug, Deserialize, Clone, Eq, PartialEq, Hash, strum::EnumString, strum::Display, SerializeDisplay)] +#[derive( + Debug, Deserialize, Clone, Eq, PartialEq, Hash, strum::EnumString, strum::Display, SerializeDisplay, Ord, PartialOrd, +)] pub enum SupportedDidMethod { #[serde(alias = "did_jwk", rename = "did_jwk")] #[strum(serialize = "did:jwk")] @@ -252,12 +277,16 @@ pub fn reload_config() { // TODO: should fail when none is enabled pub fn get_all_enabled_did_methods() -> Vec { - config() + let mut did_methods: Vec<_> = config() .did_methods .iter() .filter(|(_, v)| v.enabled) .map(|(k, _)| k.clone()) - .collect() + .collect(); + + did_methods.sort(); + + did_methods } // TODO: should fail when there's more than one result diff --git a/agent_shared/src/lib.rs b/agent_shared/src/lib.rs index cb2a37ae..6ae61d5e 100644 --- a/agent_shared/src/lib.rs +++ b/agent_shared/src/lib.rs @@ -5,7 +5,6 @@ pub mod error; pub mod generic_query; pub mod handlers; pub mod issuance; -pub mod metadata; pub mod url_utils; pub use ::config::ConfigError; diff --git a/agent_shared/src/metadata.rs b/agent_shared/src/metadata.rs deleted file mode 100644 index eeaa38cf..00000000 --- a/agent_shared/src/metadata.rs +++ /dev/null @@ -1,40 +0,0 @@ -use jsonwebtoken::Algorithm; -use oid4vc_core::SubjectSyntaxType; -use oid4vp::ClaimFormatDesignation; -use serde::{Deserialize, Serialize}; -use serde_with::skip_serializing_none; -use std::collections::HashMap; -use url::Url; - -#[skip_serializing_none] -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct Logo { - // TODO: remove this alias and change field to `uri`. - #[serde(alias = "uri")] - pub url: Option, - pub alt_text: Option, -} - -#[skip_serializing_none] -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct Display { - pub name: String, - pub locale: Option, - pub logo: Option, -} - -#[derive(Serialize, Deserialize, Debug, Default)] -pub struct Metadata { - #[serde(default)] - pub subject_syntax_types_supported: Vec, - #[serde(default)] - pub signing_algorithms_supported: Vec, - #[serde(default)] - pub id_token_signing_alg_values_supported: Vec, - #[serde(default)] - pub request_object_signing_alg_values_supported: Vec, - #[serde(default)] - pub vp_formats: HashMap, - #[serde(default)] - pub display: Vec, -} diff --git a/agent_shared/tests/test-config.yaml b/agent_shared/tests/test-config.yaml index 808500a4..0b8a9810 100644 --- a/agent_shared/tests/test-config.yaml +++ b/agent_shared/tests/test-config.yaml @@ -11,7 +11,7 @@ did_methods: enabled: true preferred: true did_iota_rms: - enabled: false + enabled: true did_web: enabled: false diff --git a/agent_verification/src/authorization_request/aggregate.rs b/agent_verification/src/authorization_request/aggregate.rs index e34faa82..9f96d835 100644 --- a/agent_verification/src/authorization_request/aggregate.rs +++ b/agent_verification/src/authorization_request/aggregate.rs @@ -392,7 +392,7 @@ pub mod tests { openid://?\ client_id=did%3Aiota%3Arms%3A0x42ad588322e58b3c07aa39e4948d021ee17ecb5747915e9e1f35f028d7ecaf90&\ request_uri=https%3A%2F%2Fmy-domain.example.org%2Frequest%2Fstate"; - const SIGNED_AUTHORIZATION_REQUEST_OBJECT_DID_KEY: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa2dFODROQ01wTWVBeDlqSzljZjVXNEc4Z2NaOXh1d0p2RzFlN3dOazhLQ2d0I3o2TWtnRTg0TkNNcE1lQXg5aks5Y2Y1VzRHOGdjWjl4dXdKdkcxZTd3Tms4S0NndCJ9.eyJjbGllbnRfaWQiOiJkaWQ6a2V5Ono2TWtnRTg0TkNNcE1lQXg5aks5Y2Y1VzRHOGdjWjl4dXdKdkcxZTd3Tms4S0NndCIsInJlZGlyZWN0X3VyaSI6Imh0dHBzOi8vbXktZG9tYWluLmV4YW1wbGUub3JnL3JlZGlyZWN0Iiwic3RhdGUiOiJzdGF0ZSIsInJlc3BvbnNlX3R5cGUiOiJpZF90b2tlbiIsInNjb3BlIjoib3BlbmlkIiwicmVzcG9uc2VfbW9kZSI6ImRpcmVjdF9wb3N0Iiwibm9uY2UiOiJub25jZSIsImNsaWVudF9tZXRhZGF0YSI6eyJjbGllbnRfbmFtZSI6IlVuaUNvcmUiLCJsb2dvX3VyaSI6Imh0dHBzOi8vaW1waWVyY2UuY29tL2ltYWdlcy9sb2dvLWJsdWUucG5nIiwic3ViamVjdF9zeW50YXhfdHlwZXNfc3VwcG9ydGVkIjpbImRpZDprZXkiLCJkaWQ6a2V5IiwiZGlkOmlvdGE6cm1zIiwiZGlkOmp3ayJdLCJpZF90b2tlbl9zaWduZWRfcmVzcG9uc2VfYWxnIjoiRWREU0EiLCJpZF90b2tlbl9zaWduaW5nX2FsZ192YWx1ZXNfc3VwcG9ydGVkIjpbIkVkRFNBIl19fQ.8faeoQBfx-F-IVHB8VMUOwget_XicSnr4u6rQ2SABq63U7bzAX-G_LOqoQBg8c_UNEw1Rfo3N4CjqsLlBYNRAw"; - const SIGNED_AUTHORIZATION_REQUEST_OBJECT_DID_JWK: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSIsImtpZCI6ImRpZDpqd2s6ZXlKaGJHY2lPaUpGWkVSVFFTSXNJbU55ZGlJNklrVmtNalUxTVRraUxDSnJhV1FpT2lKaVVVdFJVbnBoYjNBM1EyZEZkbkZXY1RoVmJHZE1SM05rUmkxU0xXaHVURVpyUzBaYWNWY3lWazR3SWl3aWEzUjVJam9pVDB0UUlpd2llQ0k2SWtkc2JrczVaVkJ6T0RBeVdIaEJaMnhTVDFGNmIwZDFjbTA1VVhCMk1FbEdVRVZpWkUxRFNVeE9YMVVpZlEjMCJ9.eyJjbGllbnRfaWQiOiJkaWQ6andrOmV5SmhiR2NpT2lKRlpFUlRRU0lzSW1OeWRpSTZJa1ZrTWpVMU1Ua2lMQ0pyYVdRaU9pSmlVVXRSVW5waGIzQTNRMmRGZG5GV2NUaFZiR2RNUjNOa1JpMVNMV2h1VEVaclMwWmFjVmN5Vms0d0lpd2lhM1I1SWpvaVQwdFFJaXdpZUNJNklrZHNia3M1WlZCek9EQXlXSGhCWjJ4U1QxRjZiMGQxY20wNVVYQjJNRWxHVUVWaVpFMURTVXhPWDFVaWZRIiwicmVkaXJlY3RfdXJpIjoiaHR0cHM6Ly9teS1kb21haW4uZXhhbXBsZS5vcmcvcmVkaXJlY3QiLCJzdGF0ZSI6InN0YXRlIiwicmVzcG9uc2VfdHlwZSI6ImlkX3Rva2VuIiwic2NvcGUiOiJvcGVuaWQiLCJyZXNwb25zZV9tb2RlIjoiZGlyZWN0X3Bvc3QiLCJub25jZSI6Im5vbmNlIiwiY2xpZW50X21ldGFkYXRhIjp7ImNsaWVudF9uYW1lIjoiVW5pQ29yZSIsImxvZ29fdXJpIjoiaHR0cHM6Ly9pbXBpZXJjZS5jb20vaW1hZ2VzL2xvZ28tYmx1ZS5wbmciLCJzdWJqZWN0X3N5bnRheF90eXBlc19zdXBwb3J0ZWQiOlsiZGlkOmp3ayIsImRpZDprZXkiLCJkaWQ6aW90YTpybXMiLCJkaWQ6andrIl0sImlkX3Rva2VuX3NpZ25lZF9yZXNwb25zZV9hbGciOiJFZERTQSIsImlkX3Rva2VuX3NpZ25pbmdfYWxnX3ZhbHVlc19zdXBwb3J0ZWQiOlsiRWREU0EiXX19.FzcVyHb2zHh-Y3PA0vCkruD8nO0Uh8rrYkyEXiQliab54gJ6A6vDqFyWIsNJNg2CQ3OPge9DV3hRerRrzIlHCg"; - const SIGNED_AUTHORIZATION_REQUEST_OBJECT_DID_IOTA: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSIsImtpZCI6ImRpZDppb3RhOnJtczoweDQyYWQ1ODgzMjJlNThiM2MwN2FhMzllNDk0OGQwMjFlZTE3ZWNiNTc0NzkxNWU5ZTFmMzVmMDI4ZDdlY2FmOTAjYlFLUVJ6YW9wN0NnRXZxVnE4VWxnTEdzZEYtUi1obkxGa0tGWnFXMlZOMCJ9.eyJjbGllbnRfaWQiOiJkaWQ6aW90YTpybXM6MHg0MmFkNTg4MzIyZTU4YjNjMDdhYTM5ZTQ5NDhkMDIxZWUxN2VjYjU3NDc5MTVlOWUxZjM1ZjAyOGQ3ZWNhZjkwIiwicmVkaXJlY3RfdXJpIjoiaHR0cHM6Ly9teS1kb21haW4uZXhhbXBsZS5vcmcvcmVkaXJlY3QiLCJzdGF0ZSI6InN0YXRlIiwicmVzcG9uc2VfdHlwZSI6ImlkX3Rva2VuIiwic2NvcGUiOiJvcGVuaWQiLCJyZXNwb25zZV9tb2RlIjoiZGlyZWN0X3Bvc3QiLCJub25jZSI6Im5vbmNlIiwiY2xpZW50X21ldGFkYXRhIjp7ImNsaWVudF9uYW1lIjoiVW5pQ29yZSIsImxvZ29fdXJpIjoiaHR0cHM6Ly9pbXBpZXJjZS5jb20vaW1hZ2VzL2xvZ28tYmx1ZS5wbmciLCJzdWJqZWN0X3N5bnRheF90eXBlc19zdXBwb3J0ZWQiOlsiZGlkOmlvdGE6cm1zIiwiZGlkOmtleSIsImRpZDppb3RhOnJtcyIsImRpZDpqd2siXSwiaWRfdG9rZW5fc2lnbmVkX3Jlc3BvbnNlX2FsZyI6IkVkRFNBIiwiaWRfdG9rZW5fc2lnbmluZ19hbGdfdmFsdWVzX3N1cHBvcnRlZCI6WyJFZERTQSJdfX0.9i0JmcDT_z8reB-X71smDbb48xkgGhrgcrjDZghs2ps1MB1chP3S1Nw6ew4R07775yOicrHq8MRcKvHwIiNbAA"; + const SIGNED_AUTHORIZATION_REQUEST_OBJECT_DID_KEY: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk6ejZNa2dFODROQ01wTWVBeDlqSzljZjVXNEc4Z2NaOXh1d0p2RzFlN3dOazhLQ2d0I3o2TWtnRTg0TkNNcE1lQXg5aks5Y2Y1VzRHOGdjWjl4dXdKdkcxZTd3Tms4S0NndCJ9.eyJjbGllbnRfaWQiOiJkaWQ6a2V5Ono2TWtnRTg0TkNNcE1lQXg5aks5Y2Y1VzRHOGdjWjl4dXdKdkcxZTd3Tms4S0NndCIsInJlZGlyZWN0X3VyaSI6Imh0dHBzOi8vbXktZG9tYWluLmV4YW1wbGUub3JnL3JlZGlyZWN0Iiwic3RhdGUiOiJzdGF0ZSIsInJlc3BvbnNlX3R5cGUiOiJpZF90b2tlbiIsInNjb3BlIjoib3BlbmlkIiwicmVzcG9uc2VfbW9kZSI6ImRpcmVjdF9wb3N0Iiwibm9uY2UiOiJub25jZSIsImNsaWVudF9tZXRhZGF0YSI6eyJjbGllbnRfbmFtZSI6IlVuaUNvcmUiLCJsb2dvX3VyaSI6Imh0dHBzOi8vaW1waWVyY2UuY29tL2ltYWdlcy9mYXZpY29uL2FwcGxlLXRvdWNoLWljb24ucG5nIiwic3ViamVjdF9zeW50YXhfdHlwZXNfc3VwcG9ydGVkIjpbImRpZDpqd2siLCJkaWQ6a2V5IiwiZGlkOmlvdGE6cm1zIl0sImlkX3Rva2VuX3NpZ25lZF9yZXNwb25zZV9hbGciOiJFZERTQSIsImlkX3Rva2VuX3NpZ25pbmdfYWxnX3ZhbHVlc19zdXBwb3J0ZWQiOlsiRWREU0EiXX19.bSJic_ZsIygLNYCi2cZBeAncGw68RNN64-nTOC6Mi09yF1NXuPJE3J5qWupjycVLf7LscYKDCjO50kvGf4fPDw"; + const SIGNED_AUTHORIZATION_REQUEST_OBJECT_DID_JWK: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSIsImtpZCI6ImRpZDpqd2s6ZXlKaGJHY2lPaUpGWkVSVFFTSXNJbU55ZGlJNklrVmtNalUxTVRraUxDSnJhV1FpT2lKaVVVdFJVbnBoYjNBM1EyZEZkbkZXY1RoVmJHZE1SM05rUmkxU0xXaHVURVpyUzBaYWNWY3lWazR3SWl3aWEzUjVJam9pVDB0UUlpd2llQ0k2SWtkc2JrczVaVkJ6T0RBeVdIaEJaMnhTVDFGNmIwZDFjbTA1VVhCMk1FbEdVRVZpWkUxRFNVeE9YMVVpZlEjMCJ9.eyJjbGllbnRfaWQiOiJkaWQ6andrOmV5SmhiR2NpT2lKRlpFUlRRU0lzSW1OeWRpSTZJa1ZrTWpVMU1Ua2lMQ0pyYVdRaU9pSmlVVXRSVW5waGIzQTNRMmRGZG5GV2NUaFZiR2RNUjNOa1JpMVNMV2h1VEVaclMwWmFjVmN5Vms0d0lpd2lhM1I1SWpvaVQwdFFJaXdpZUNJNklrZHNia3M1WlZCek9EQXlXSGhCWjJ4U1QxRjZiMGQxY20wNVVYQjJNRWxHVUVWaVpFMURTVXhPWDFVaWZRIiwicmVkaXJlY3RfdXJpIjoiaHR0cHM6Ly9teS1kb21haW4uZXhhbXBsZS5vcmcvcmVkaXJlY3QiLCJzdGF0ZSI6InN0YXRlIiwicmVzcG9uc2VfdHlwZSI6ImlkX3Rva2VuIiwic2NvcGUiOiJvcGVuaWQiLCJyZXNwb25zZV9tb2RlIjoiZGlyZWN0X3Bvc3QiLCJub25jZSI6Im5vbmNlIiwiY2xpZW50X21ldGFkYXRhIjp7ImNsaWVudF9uYW1lIjoiVW5pQ29yZSIsImxvZ29fdXJpIjoiaHR0cHM6Ly9pbXBpZXJjZS5jb20vaW1hZ2VzL2Zhdmljb24vYXBwbGUtdG91Y2gtaWNvbi5wbmciLCJzdWJqZWN0X3N5bnRheF90eXBlc19zdXBwb3J0ZWQiOlsiZGlkOmp3ayIsImRpZDprZXkiLCJkaWQ6aW90YTpybXMiXSwiaWRfdG9rZW5fc2lnbmVkX3Jlc3BvbnNlX2FsZyI6IkVkRFNBIiwiaWRfdG9rZW5fc2lnbmluZ19hbGdfdmFsdWVzX3N1cHBvcnRlZCI6WyJFZERTQSJdfX0.8Qj3u6rC5Qb0W54duip_HeJdp9It104Im8BKNR4H6Pw5AY6U826q-GBO618TLwavm2I20ehA8XWGYoOBzPyGDQ"; + const SIGNED_AUTHORIZATION_REQUEST_OBJECT_DID_IOTA: &str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSIsImtpZCI6ImRpZDppb3RhOnJtczoweDQyYWQ1ODgzMjJlNThiM2MwN2FhMzllNDk0OGQwMjFlZTE3ZWNiNTc0NzkxNWU5ZTFmMzVmMDI4ZDdlY2FmOTAjYlFLUVJ6YW9wN0NnRXZxVnE4VWxnTEdzZEYtUi1obkxGa0tGWnFXMlZOMCJ9.eyJjbGllbnRfaWQiOiJkaWQ6aW90YTpybXM6MHg0MmFkNTg4MzIyZTU4YjNjMDdhYTM5ZTQ5NDhkMDIxZWUxN2VjYjU3NDc5MTVlOWUxZjM1ZjAyOGQ3ZWNhZjkwIiwicmVkaXJlY3RfdXJpIjoiaHR0cHM6Ly9teS1kb21haW4uZXhhbXBsZS5vcmcvcmVkaXJlY3QiLCJzdGF0ZSI6InN0YXRlIiwicmVzcG9uc2VfdHlwZSI6ImlkX3Rva2VuIiwic2NvcGUiOiJvcGVuaWQiLCJyZXNwb25zZV9tb2RlIjoiZGlyZWN0X3Bvc3QiLCJub25jZSI6Im5vbmNlIiwiY2xpZW50X21ldGFkYXRhIjp7ImNsaWVudF9uYW1lIjoiVW5pQ29yZSIsImxvZ29fdXJpIjoiaHR0cHM6Ly9pbXBpZXJjZS5jb20vaW1hZ2VzL2Zhdmljb24vYXBwbGUtdG91Y2gtaWNvbi5wbmciLCJzdWJqZWN0X3N5bnRheF90eXBlc19zdXBwb3J0ZWQiOlsiZGlkOmp3ayIsImRpZDprZXkiLCJkaWQ6aW90YTpybXMiXSwiaWRfdG9rZW5fc2lnbmVkX3Jlc3BvbnNlX2FsZyI6IkVkRFNBIiwiaWRfdG9rZW5fc2lnbmluZ19hbGdfdmFsdWVzX3N1cHBvcnRlZCI6WyJFZERTQSJdfX0.TGQ_9RQYwltCjE8mRVG1CFveoQjWH9Xf55pm8TcYLkOmUitHeK_PKwwO16vWXHfgLeAVe7Y5b98hKCAupZ6FBg"; } From 0e96942f014745f94566affa6dce46f7628ab2f0 Mon Sep 17 00:00:00 2001 From: Nander Stabel Date: Tue, 23 Jul 2024 14:14:58 +0200 Subject: [PATCH 26/37] ci: update docker-compose --- agent_application/docker/docker-compose.yml | 28 ++++++++------------- agent_application/src/main.rs | 4 +++ 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/agent_application/docker/docker-compose.yml b/agent_application/docker/docker-compose.yml index 1177f4fd..a2ea5a08 100644 --- a/agent_application/docker/docker-compose.yml +++ b/agent_application/docker/docker-compose.yml @@ -34,26 +34,20 @@ services: ports: - 3033:3033 environment: - # AGENT_CONFIG_LOG_FORMAT: json - AGENT_CONFIG_EVENT_STORE: postgres - # AGENT_CONFIG_BASE_PATH: "unicore" - AGENT_CONFIG_URL: ${AGENT_CONFIG_URL} - AGENT_STORE_DB_CONNECTION_STRING: postgresql://demo_user:demo_pass@cqrs-postgres-db:5432/demo - AGENT_SECRET_MANAGER_STRONGHOLD_PATH: /app/res/stronghold + AGENT__LOG_FORMAT: text + AGENT__EVENT_STORE__TYPE: postgres + AGENT__EVENT_STORE__CONNECTION_STRING: postgresql://demo_user:demo_pass@localhost:5432/demo + AGENT__URL: http://192.168.1.234:3033 - AGENT_CONFIG_STRONGHOLD_PASSWORD: "VNvRtH4tKyWwvJDpL6Vuc2aoLiKAecGQ" - AGENT_CONFIG_ISSUER_KEY_ID: "UVDxWhG2rB39FkaR7I27mHeUNrGtUgcr" - - # Uncomment the following lines to use the DID method `did:iota:rms` - # AGENT_CONFIG_ISSUER_DID: "did:iota:rms:0x42ad588322e58b3c07aa39e4948d021ee17ecb5747915e9e1f35f028d7ecaf90" - # AGENT_CONFIG_ISSUER_FRAGMENT: "bQKQRzaop7CgEvqVq8UlgLGsdF-R-hnLFkKFZqW2VN0" - # AGENT_CONFIG_DEFAULT_DID_METHOD: "did:iota:rms" + AGENT__SECRET_MANAGER__STRONGHOLD_PATH: "/app/res/stronghold" + AGENT__SECRET_MANAGER__STRONGHOLD_PASSWORD: "secure_password" + AGENT__SECRET_MANAGER__ISSUER_KEY_ID: "9O66nzWqYYy1LmmiOudOlh2SMIaUWoTS" + AGENT__SECRET_MANAGER__ISSUER_DID: "did:iota:rms:0x42ad588322e58b3c07aa39e4948d021ee17ecb5747915e9e1f35f028d7ecaf90" + AGENT__SECRET_MANAGER__ISSUER_FRAGMENT: "bQKQRzaop7CgEvqVq8UlgLGsdF-R-hnLFkKFZqW2VN0" volumes: - - ../../agent_application/config.yml:/app/agent_application/config.yml - - ../../agent_secret_manager/tests/res/selv.stronghold:/app/res/stronghold - - ../../agent_event_publisher_http/config.yml:/app/agent_event_publisher_http/config.yml + - ../../agent_application/example-config.yaml:/app/agent_application/example-config.yaml + - ../../agent_secret_manager/tests/res/test.stronghold:/app/res/stronghold - ../../agent_verification/presentation_definitions:/app/agent_verification/presentation_definitions - - ../../agent_issuance/issuance-config.yml:/app/agent_issuance/issuance-config.yml # TODO: Remove this. This is a workaround that ensures that the `agent_verification/presentation_definitions` # folder can be accessed by the agent from the `fn authorization_requests` endpoint. - ./tmp:/app/agent_api_rest diff --git a/agent_application/src/main.rs b/agent_application/src/main.rs index 231bfa45..dc24ee7a 100644 --- a/agent_application/src/main.rs +++ b/agent_application/src/main.rs @@ -33,6 +33,9 @@ async fn main() -> io::Result<()> { // _ => tracing_subscriber.with(tracing_subscriber::fmt::layer()).init(), } + info!("Configuration loaded."); + info!("Configuration: {:#?}", config()); + let verification_services = Arc::new(VerificationServices::new(Arc::new(Subject { secret_manager: secret_manager().await, }))); @@ -49,6 +52,7 @@ async fn main() -> io::Result<()> { let connection_string = config().event_store.connection_string.clone().expect( "Missing config parameter `event_store.connection_string` or `AGENT__EVENT_STORE__CONNECTION_STRING`", ); + info!("Connecting to Postgres: {}", connection_string); ( postgres::issuance_state(issuance_event_publishers, &connection_string).await, postgres::verification_state(verification_services, verification_event_publishers, &connection_string) From dd630e097f67375d6dacd48a99aec4faf2393163 Mon Sep 17 00:00:00 2001 From: Daniel Mader Date: Tue, 23 Jul 2024 15:00:25 +0200 Subject: [PATCH 27/37] fix: replace `localhost` with container name --- agent_application/docker/docker-compose.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/agent_application/docker/docker-compose.yml b/agent_application/docker/docker-compose.yml index a2ea5a08..5290327d 100644 --- a/agent_application/docker/docker-compose.yml +++ b/agent_application/docker/docker-compose.yml @@ -4,6 +4,7 @@ name: ssi-agent services: cqrs-postgres-db: + container_name: cqrs-postgres-db image: postgres restart: always ports: @@ -36,7 +37,7 @@ services: environment: AGENT__LOG_FORMAT: text AGENT__EVENT_STORE__TYPE: postgres - AGENT__EVENT_STORE__CONNECTION_STRING: postgresql://demo_user:demo_pass@localhost:5432/demo + AGENT__EVENT_STORE__CONNECTION_STRING: postgresql://demo_user:demo_pass@cqrs-postgres-db:5432/demo AGENT__URL: http://192.168.1.234:3033 AGENT__SECRET_MANAGER__STRONGHOLD_PATH: "/app/res/stronghold" From 3295f41e1fe81e06db2e5cb7cb2ecc9546a17f9c Mon Sep 17 00:00:00 2001 From: Nander Stabel Date: Tue, 23 Jul 2024 19:31:51 +0200 Subject: [PATCH 28/37] refactor: clean up code --- .env.example | 3 +- Cargo.lock | 1 - agent_api_rest/README.md | 2 +- .../issuance/credential_issuer/credential.rs | 48 +------------ agent_api_rest/src/lib.rs | 2 +- agent_application/docker/docker-compose.yml | 8 ++- agent_application/src/main.rs | 7 -- agent_event_publisher_http/Cargo.toml | 1 - agent_event_publisher_http/README.md | 19 +++--- agent_event_publisher_http/src/lib.rs | 64 ------------------ agent_issuance/src/server_config/aggregate.rs | 2 +- agent_issuance/src/server_config/command.rs | 2 +- agent_secret_manager/src/lib.rs | 1 - .../tests/res/temp.stronghold | Bin 623 -> 0 bytes agent_shared/src/config.rs | 24 +++---- agent_shared/src/issuance.rs | 34 ---------- agent_shared/src/lib.rs | 1 - agent_store/Cargo.toml | 4 +- agent_verification/src/services.rs | 23 ------- 19 files changed, 33 insertions(+), 213 deletions(-) delete mode 100644 agent_secret_manager/tests/res/temp.stronghold delete mode 100644 agent_shared/src/issuance.rs diff --git a/.env.example b/.env.example index f1b8f930..b4159998 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,6 @@ AGENT__LOG_FORMAT=text AGENT__EVENT_STORE__TYPE=postgres -AGENT__EVENT_STORE__CONNECTION_STRING="postgresql://demo_user:demo_pass@localhost:5432/demo" -AGENT__URL="http://192.168.1.234:3033" +AGENT__EVENT_STORE__CONNECTION_STRING="postgresql://demo_user:demo_pass@cqrs-postgres-db:5432/demo" AGENT__SECRET_MANAGER__STRONGHOLD_PATH="agent_secret_manager/tests/res/test.stronghold" AGENT__SECRET_MANAGER__STRONGHOLD_PASSWORD="secure_password" diff --git a/Cargo.lock b/Cargo.lock index 78ddf256..b45297d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -244,7 +244,6 @@ dependencies = [ "async-trait", "cqrs-es", "postgres-es", - "serde", "serde_json", "sqlx", "tokio", diff --git a/agent_api_rest/README.md b/agent_api_rest/README.md index c5b14b19..1846a2b1 100644 --- a/agent_api_rest/README.md +++ b/agent_api_rest/README.md @@ -18,7 +18,7 @@ Browse to http://localhost:9090 ### CORS -If you want to access UniCore's API from a browser, you can set the `AGENT_APPLICATION_CORS_ENABLED` environment variable to `true`. This will enable a permissive CORS policy (allow all). +If you want to access UniCore's API from a browser, you can set the `AGENT__CORS_ENABLED` environment variable to `true`. This will enable a permissive CORS policy (allow all). ## Usage Below we describe a typical usage of the REST API for UniCore. diff --git a/agent_api_rest/src/issuance/credential_issuer/credential.rs b/agent_api_rest/src/issuance/credential_issuer/credential.rs index ed15278e..13df35c8 100644 --- a/agent_api_rest/src/issuance/credential_issuer/credential.rs +++ b/agent_api_rest/src/issuance/credential_issuer/credential.rs @@ -42,8 +42,6 @@ pub(crate) async fn credential( _ => return StatusCode::UNAUTHORIZED.into_response(), }; - info!("test1"); - // Get the `credential_issuer_metadata` and `authorization_server_metadata` from the `ServerConfigView`. let (credential_issuer_metadata, authorization_server_metadata) = match query_handler(SERVER_CONFIG_ID, &state.query.server_config).await { @@ -54,8 +52,6 @@ pub(crate) async fn credential( _ => return StatusCode::INTERNAL_SERVER_ERROR.into_response(), }; - info!("test2"); - let command = OfferCommand::VerifyCredentialRequest { offer_id: offer_id.clone(), credential_issuer_metadata, @@ -63,23 +59,16 @@ pub(crate) async fn credential( credential_request, }; - info!("test3"); - // Use the `offer_id` to verify the `proof` inside the `CredentialRequest`. if command_handler(&offer_id, &state.command.offer, command).await.is_err() { StatusCode::INTERNAL_SERVER_ERROR.into_response(); }; - info!("test4"); - let timeout = config() .external_server_response_timeout_ms .unwrap_or(DEFAULT_EXTERNAL_SERVER_RESPONSE_TIMEOUT_MS); let start_time = Instant::now(); - info!("time out: {}", timeout); - info!("test5"); - // TODO: replace this polling solution with a call to the `TxChannelRegistry` as described here: https://github.com/impierce/ssi-agent/issues/75 // Use the `offer_id` to get the `credential_ids` and `subject_id` from the `OfferView`. let (credential_ids, subject_id) = loop { @@ -104,8 +93,6 @@ pub(crate) async fn credential( } }; - info!("test6"); - // Use the `credential_ids` and `subject_id` to sign all the credentials. let mut signed_credentials = vec![]; for credential_id in credential_ids { @@ -132,22 +119,16 @@ pub(crate) async fn credential( signed_credentials.push(signed_credential); } - info!("test7"); - let command = OfferCommand::CreateCredentialResponse { offer_id: offer_id.clone(), signed_credentials, }; - info!("test8"); - // Use the `offer_id` to create a `CredentialResponse` from the `CredentialRequest` and `credentials`. if command_handler(&offer_id, &state.command.offer, command).await.is_err() { StatusCode::INTERNAL_SERVER_ERROR.into_response(); }; - info!("test9"); - // Use the `offer_id` to get the `credential_response` from the `OfferView`. match query_handler(&offer_id, &state.query.offer).await { Ok(Some(OfferView { @@ -213,7 +194,6 @@ mod tests { is_self_signed: bool, delay: u64, ) { - println!("HEEERRREE1"); Mock::given(method("POST")) .and(path("/ssi-events-subscriber")) .and( @@ -222,13 +202,9 @@ mod tests { OfferEvent::CredentialRequestVerified { offer_id, subject_id } => { let app_clone = app.clone(); - let temp = futures::executor::block_on(async { - println!("testA"); - + futures::executor::block_on(async { let app_clone = app_clone.lock().await.take().unwrap(); - println!("testB"); - // This assertion is a representation of the 'outside' backend server retrieving the // data that corresponds to the `offer_id`. assert_eq!(offer_id, OFFER_ID); @@ -257,12 +233,8 @@ mod tests { } }; - println!("testC"); - std::thread::sleep(Duration::from_millis(delay)); - println!("testD"); - // Sends the `CredentialsRequest` to the `credentials` endpoint. app_clone .oneshot( @@ -279,8 +251,6 @@ mod tests { }) .unwrap(); - println!("{}", temp.status()); - true } _ => false, @@ -311,11 +281,6 @@ mod tests { let target_url = format!("{}/ssi-events-subscriber", &external_server.uri()); - // std::env::set_var("TEST_AGENT__EVENT_PUBLISHERS__HTTP__TARGET_URL", target_url.clone()); - // std::env::set_var("TEST_AGENT__DID_METHODS__DID_JWK__PREFERRED", "false".to_string()); - // std::env::set_var("TEST_AGENT__DID_METHODS__DID_KEY__PREFERRED", "true".to_string()); - // reload_config(); - set_config().enable_event_publisher_http(); set_config().set_event_publisher_http_target_url(target_url.clone()); set_config().set_event_publisher_http_target_events(Events { @@ -332,17 +297,6 @@ mod tests { (None, Default::default(), Default::default()) }; - println!("issuance_event_publishers: {:#?}", EventPublisherHttp::load().unwrap()); - - println!("CONFIG: {:#?}", config()); - println!( - "with_external_server: {:#?}, external_server: {}", - with_external_server, - external_server.is_some() - ); - println!("is_self_signed: {:#?}", is_self_signed); - println!("delay: {:#?}", delay); - let issuance_state = in_memory::issuance_state(issuance_event_publishers).await; let verification_state = in_memory::verification_state(test_verification_services(), verification_event_publishers).await; diff --git a/agent_api_rest/src/lib.rs b/agent_api_rest/src/lib.rs index f5c948ec..2e057c35 100644 --- a/agent_api_rest/src/lib.rs +++ b/agent_api_rest/src/lib.rs @@ -113,7 +113,7 @@ fn get_base_path() -> Result { } if base_path.is_empty() { - panic!("AGENT_APPLICATION_BASE_PATH can't be empty, remove or set path"); + panic!("AGENT__BASE_PATH can't be empty, remove or set path"); } tracing::info!("Base path: {:?}", base_path); diff --git a/agent_application/docker/docker-compose.yml b/agent_application/docker/docker-compose.yml index 5290327d..648da65d 100644 --- a/agent_application/docker/docker-compose.yml +++ b/agent_application/docker/docker-compose.yml @@ -42,9 +42,11 @@ services: AGENT__SECRET_MANAGER__STRONGHOLD_PATH: "/app/res/stronghold" AGENT__SECRET_MANAGER__STRONGHOLD_PASSWORD: "secure_password" - AGENT__SECRET_MANAGER__ISSUER_KEY_ID: "9O66nzWqYYy1LmmiOudOlh2SMIaUWoTS" - AGENT__SECRET_MANAGER__ISSUER_DID: "did:iota:rms:0x42ad588322e58b3c07aa39e4948d021ee17ecb5747915e9e1f35f028d7ecaf90" - AGENT__SECRET_MANAGER__ISSUER_FRAGMENT: "bQKQRzaop7CgEvqVq8UlgLGsdF-R-hnLFkKFZqW2VN0" + + # Uncomment the following lines to use the DID method `did:iota:rms` + # AGENT__SECRET_MANAGER__ISSUER_KEY_ID: "9O66nzWqYYy1LmmiOudOlh2SMIaUWoTS" + # AGENT__SECRET_MANAGER__ISSUER_DID: "did:iota:rms:0x42ad588322e58b3c07aa39e4948d021ee17ecb5747915e9e1f35f028d7ecaf90" + # AGENT__SECRET_MANAGER__ISSUER_FRAGMENT: "bQKQRzaop7CgEvqVq8UlgLGsdF-R-hnLFkKFZqW2VN0" volumes: - ../../agent_application/example-config.yaml:/app/agent_application/example-config.yaml - ../../agent_secret_manager/tests/res/test.stronghold:/app/res/stronghold diff --git a/agent_application/src/main.rs b/agent_application/src/main.rs index dc24ee7a..fd8184b0 100644 --- a/agent_application/src/main.rs +++ b/agent_application/src/main.rs @@ -27,15 +27,8 @@ async fn main() -> io::Result<()> { match config().log_format { LogFormat::Json => tracing_subscriber.with(tracing_subscriber::fmt::layer().json()).init(), LogFormat::Text => tracing_subscriber.with(tracing_subscriber::fmt::layer()).init(), - // Ok(log_format) if log_format == "json" => { - // tracing_subscriber.with(tracing_subscriber::fmt::layer().json()).init() - // } - // _ => tracing_subscriber.with(tracing_subscriber::fmt::layer()).init(), } - info!("Configuration loaded."); - info!("Configuration: {:#?}", config()); - let verification_services = Arc::new(VerificationServices::new(Arc::new(Subject { secret_manager: secret_manager().await, }))); diff --git a/agent_event_publisher_http/Cargo.toml b/agent_event_publisher_http/Cargo.toml index b6c39a2e..a7e4f590 100644 --- a/agent_event_publisher_http/Cargo.toml +++ b/agent_event_publisher_http/Cargo.toml @@ -20,7 +20,6 @@ rustls = { version = "0.23", default-features = false, features = [ ] } reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] } serde.workspace = true -serde_json.workspace = true serde_with.workspace = true serde_yaml.workspace = true tokio.workspace = true diff --git a/agent_event_publisher_http/README.md b/agent_event_publisher_http/README.md index a2f900b5..028d2308 100644 --- a/agent_event_publisher_http/README.md +++ b/agent_event_publisher_http/README.md @@ -2,22 +2,21 @@ A simple HTTP event publisher for the SSI Agent. -To make use of this publisher you need to configure it by creating a `config.yaml` file in this same directory. For each -aggregate you want to publish events for, you need to set the following properties: +To make use of this publisher you need to configure it by adding the `http` object to your configuration file. - The `target_url` is the URL to which the events will be published. - The `target_events` is a list of events that will be published to the `target_url`. -Example `config.yaml`: +Example: ```yaml -target_url: &target_url "https://my-domain.example.org/ssi-event-subscriber" - -connection: - { - target_url: *target_url, - target_events: [SIOPv2AuthorizationResponseVerified], - } +event_publishers: + http: + enabled: false + target_url: "https://my-domain.example.org/event-subscriber" + events: + server_config: [] + credential: [UnsignedCredentialCreated, CredentialSigned] ``` ### Available events diff --git a/agent_event_publisher_http/src/lib.rs b/agent_event_publisher_http/src/lib.rs index b4abcd87..a8543298 100644 --- a/agent_event_publisher_http/src/lib.rs +++ b/agent_event_publisher_http/src/lib.rs @@ -29,35 +29,8 @@ pub struct EventPublisherHttp { impl EventPublisherHttp { pub fn load() -> anyhow::Result { - // #[cfg(feature = "test_utils")] - // let mut config = TEST_EVENT_PUBLISHER_HTTP_CONFIG - // .lock() - // .unwrap() - // .as_ref() - // .unwrap() - // .clone(); - // #[cfg(not(feature = "test_utils"))] - // let mut config: serde_yaml::Value = { - // match std::fs::File::open("agent_event_publisher_http/config.yml") { - // Ok(config_file) => serde_yaml::from_reader(config_file)?, - // // If the config file does not exist, return an empty config. - // Err(_) => serde_yaml::Value::Null, - // } - // }; - // let config = agent_shared::config::config("AGENT_EVENT_PUBLISHER_HTTP"); - let event_publisher_http = config().event_publishers.clone().unwrap().http.unwrap(); - // let event_publishers = config.get_table("event_publishers").unwrap_or_default(); - // let event_publisher_http = event_publishers - // .get("http") - // .unwrap() - // .clone() - // .into_table() - // .unwrap_or_default(); - - println!("event_publisher_http: {:?}", event_publisher_http); - // If it's not enabled, return an empty event publisher. if !event_publisher_http.enabled { return Ok(EventPublisherHttp { @@ -69,19 +42,6 @@ impl EventPublisherHttp { }); } - // TODO: map events to aggregates - // let mapping = HashMap::::new(); - // let mapping: HashMap = HashMap::from([ - // // credential - // (Event::UnsignedCredentialCreated, DomainAggregate::Credential), - // (Event::SignedCredentialCreated, DomainAggregate::Credential), - // (Event::CredentialSigned, DomainAggregate::Credential), - // // offer - // (Event::CredentialOfferCreated, DomainAggregate::Offer), - // // connection - // (Event::SIOPv2AuthorizationResponseVerified, DomainAggregate::Connection), - // ]); - let server_config = (!event_publisher_http.events.server_config.is_empty()).then(|| { AggregateEventPublisherHttp::::new( event_publisher_http.target_url.clone(), @@ -153,30 +113,6 @@ impl EventPublisherHttp { info!("Loaded HTTP event publisher: {:?}", event_publisher); Ok(event_publisher) - - // let config = if event_publisher_http - // .get("enabled") - // .unwrap() - // .clone() - // .into_bool() - // .unwrap_or_default() - // { - // let publishers: serde_yaml::Value = event_publisher_http - // .get("publishers") - // .unwrap() - // .clone() - // .try_deserialize() - // .unwrap(); - // publishers - // } else { - // serde_yaml::Value::Null - // }; - - // serde_yaml::from_value(config) - // .map_err(Into::into) - // .inspect(|event_publisher| { - // info!("Loaded HTTP event publisher: {:?}", event_publisher); - // }) } } diff --git a/agent_issuance/src/server_config/aggregate.rs b/agent_issuance/src/server_config/aggregate.rs index f5271ebf..a9fe2042 100644 --- a/agent_issuance/src/server_config/aggregate.rs +++ b/agent_issuance/src/server_config/aggregate.rs @@ -139,7 +139,7 @@ pub mod server_config_tests { use super::*; - use agent_shared::issuance::CredentialConfiguration; + use agent_shared::config::CredentialConfiguration; use lazy_static::lazy_static; use oid4vci::credential_format_profiles::w3c_verifiable_credentials::jwt_vc_json::JwtVcJson; use oid4vci::credential_format_profiles::{w3c_verifiable_credentials, CredentialFormats, Parameters}; diff --git a/agent_issuance/src/server_config/command.rs b/agent_issuance/src/server_config/command.rs index d95dc702..d0367e4a 100644 --- a/agent_issuance/src/server_config/command.rs +++ b/agent_issuance/src/server_config/command.rs @@ -1,4 +1,4 @@ -use agent_shared::issuance::CredentialConfiguration; +use agent_shared::config::CredentialConfiguration; use oid4vci::credential_issuer::{ authorization_server_metadata::AuthorizationServerMetadata, credential_issuer_metadata::CredentialIssuerMetadata, }; diff --git a/agent_secret_manager/src/lib.rs b/agent_secret_manager/src/lib.rs index 56722829..3c83a632 100644 --- a/agent_secret_manager/src/lib.rs +++ b/agent_secret_manager/src/lib.rs @@ -24,6 +24,5 @@ pub async fn secret_manager() -> SecretManager { .unwrap() } (snapshot_path, password, None, _, _) => SecretManager::generate(snapshot_path, password).await.unwrap(), - // _ => panic!("Unable to load or generate `SecretManager`. Please make sure to set both `AGENT__SECRET_MANAGER__STRONGHOLD_PATH` and `AGENT__SECRET_MANAGER__STRONGHOLD_PASSWORD` environment variables."), } } diff --git a/agent_secret_manager/tests/res/temp.stronghold b/agent_secret_manager/tests/res/temp.stronghold deleted file mode 100644 index 0075d2b329d0061661067b027cbe4adb7e25d03e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 623 zcmV-#0+9VsK~hvn0{~%XWi4fHV{&s7WCnhAyGUx3pbH)ky2^OJ0zDe+D{(mSEN~3 zo(z6j4d4HIi2lqZf-S1xZbJ#{i)`_pd!PLdD9Krg1-5)O6EDv#UnpE+VXUCvD>2r2 zZwU@H+l8hlP&JLv&vVdX*vVU}}5AZqvJY4uhwFl9N=1mrxz0k!X z5cUql(8R@&=!}trhEefMLXHp`1O&pBZoDm_y+G$A_Y}q8;bik=uOn_9fj6D=iHNH< zMvLf#AoU=|f1(cKN9tQI!_SH_#1t}HrMa!B|H8o5q16G<%7d_VSfFvy$pGYmy2pss znSKey;sxDl2~iFY(jUw~*%_ksa4CwNYc#OR?uI*@q~< JS5JZYf?yGp8q@#) diff --git a/agent_shared/src/config.rs b/agent_shared/src/config.rs index ce3098e7..04bbd790 100644 --- a/agent_shared/src/config.rs +++ b/agent_shared/src/config.rs @@ -1,4 +1,5 @@ use config::ConfigError; +use oid4vci::credential_format_profiles::{CredentialFormats, WithParameters}; use oid4vp::ClaimFormatDesignation; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; @@ -10,8 +11,6 @@ use std::{ use tracing::info; use url::Url; -use crate::issuance::CredentialConfiguration; - #[derive(Debug, Deserialize, Clone)] pub struct ApplicationConfiguration { pub log_format: LogFormat, @@ -109,6 +108,15 @@ pub struct SecretManagerConfig { pub issuer_fragment: Option, } +#[derive(Deserialize, Serialize, Debug, Clone)] +pub struct CredentialConfiguration { + pub credential_configuration_id: String, + #[serde(flatten)] + pub credential_format_with_parameters: CredentialFormats, + #[serde(default)] + pub display: Vec, +} + #[skip_serializing_none] #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Logo { @@ -228,8 +236,6 @@ pub struct ToggleOptions { pub preferred: Option, } -// pub static CONFIG: OnceCell = OnceCell::new(); - pub static CONFIG: Lazy> = Lazy::new(|| RwLock::new(ApplicationConfiguration::new().unwrap())); @@ -260,21 +266,15 @@ impl ApplicationConfiguration { /// Returns the application configuration or loads it, if it hasn't been loaded already. pub fn config<'a>() -> RwLockReadGuard<'a, ApplicationConfiguration> { - // CONFIG.get_or_init(|| ApplicationConfiguration::new().unwrap()).clone() CONFIG.read().unwrap() - // TODO: or return -> &'static ApplicationConfiguration, so we don't need to clone on every call } +/// Returns Write Guard for the application configuration that can be used to update the configuration during runtime. +#[cfg(feature = "test_utils")] pub fn set_config<'a>() -> RwLockWriteGuard<'a, ApplicationConfiguration> { CONFIG.write().unwrap() } -/// Reloads the config. Useful for testing after overwriting a env variable. -pub fn reload_config() { - // CONFIG.set(ApplicationConfiguration::new().unwrap()).unwrap(); - // CONFIG.lock().unwrap().clone() -} - // TODO: should fail when none is enabled pub fn get_all_enabled_did_methods() -> Vec { let mut did_methods: Vec<_> = config() diff --git a/agent_shared/src/issuance.rs b/agent_shared/src/issuance.rs deleted file mode 100644 index 1519db05..00000000 --- a/agent_shared/src/issuance.rs +++ /dev/null @@ -1,34 +0,0 @@ -use oid4vci::credential_format_profiles::{CredentialFormats, WithParameters}; -use serde::{Deserialize, Serialize}; - -#[derive(Deserialize, Serialize, Debug, Clone)] -pub struct CredentialConfiguration { - pub credential_configuration_id: String, - #[serde(flatten)] - pub credential_format_with_parameters: CredentialFormats, - #[serde(default)] - pub display: Vec, -} - -#[cfg(feature = "test_utils")] -use once_cell::sync::Lazy; - -#[cfg(feature = "test_utils")] -pub static TEST_ISSUER_CONFIG: Lazy = Lazy::new(|| { - serde_yaml::from_str( - r#" - server_config: - credential_configurations: - - credential_configuration_id: badge - format: jwt_vc_json - credential_definition: - type: - - VerifiableCredential - display: - - name: Badge - logo: - url: https://example.com/logo.png - "#, - ) - .unwrap() -}); diff --git a/agent_shared/src/lib.rs b/agent_shared/src/lib.rs index 6ae61d5e..578573e0 100644 --- a/agent_shared/src/lib.rs +++ b/agent_shared/src/lib.rs @@ -4,7 +4,6 @@ pub mod domain_linkage; pub mod error; pub mod generic_query; pub mod handlers; -pub mod issuance; pub mod url_utils; pub use ::config::ConfigError; diff --git a/agent_store/Cargo.toml b/agent_store/Cargo.toml index 31edde3a..05eafeae 100644 --- a/agent_store/Cargo.toml +++ b/agent_store/Cargo.toml @@ -9,11 +9,9 @@ agent_issuance = { path = "../agent_issuance" } agent_shared = { path = "../agent_shared" } agent_verification = { path = "../agent_verification" } +async-trait = "0.1" cqrs-es.workspace = true postgres-es = "0.4.10" - -async-trait = "0.1" -serde.workspace = true serde_json.workspace = true sqlx = { version = "0.7", features = [ "postgres", diff --git a/agent_verification/src/services.rs b/agent_verification/src/services.rs index 5f839e39..d4547b5d 100644 --- a/agent_verification/src/services.rs +++ b/agent_verification/src/services.rs @@ -30,8 +30,6 @@ impl VerificationServices { .map(|(alg, _)| *alg) .collect(); - // let id_token_signed_response_alg = signing_algorithms_supported.first().cloned(); - let siopv2_client_metadata = ClientMetadataResource::ClientMetadata { client_name: client_name.clone(), logo_uri: logo_uri.clone(), @@ -63,27 +61,6 @@ impl VerificationServices { ) }) .collect(), - // vp_formats: metadata - // .vp_formats - // .iter() - // .map(|(k, v)| { - // ( - // k.clone(), - // ClaimFormatProperty::Alg( - // v.get("alg") - // .map(|value| { - // value - // .as_sequence() - // .unwrap() - // .iter() - // .map(|value| value.as_str().unwrap().parse().unwrap()) - // .collect::>() - // }) - // .unwrap(), - // ), - // ) - // }) - // .collect(), }, other: HashMap::from_iter([( "subject_syntax_types_supported".to_string(), From deabbcc36c4df9da744d39f85f5e296750c34920 Mon Sep 17 00:00:00 2001 From: Nander Stabel Date: Tue, 23 Jul 2024 19:42:37 +0200 Subject: [PATCH 29/37] chore: fix unused import --- agent_shared/src/config.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agent_shared/src/config.rs b/agent_shared/src/config.rs index 04bbd790..d80a6a26 100644 --- a/agent_shared/src/config.rs +++ b/agent_shared/src/config.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; use serde_with::{skip_serializing_none, SerializeDisplay}; use std::{ collections::HashMap, - sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}, + sync::{RwLock, RwLockReadGuard}, }; use tracing::info; use url::Url; @@ -271,7 +271,7 @@ pub fn config<'a>() -> RwLockReadGuard<'a, ApplicationConfiguration> { /// Returns Write Guard for the application configuration that can be used to update the configuration during runtime. #[cfg(feature = "test_utils")] -pub fn set_config<'a>() -> RwLockWriteGuard<'a, ApplicationConfiguration> { +pub fn set_config<'a>() -> std::sync::RwLockWriteGuard<'a, ApplicationConfiguration> { CONFIG.write().unwrap() } From 68d181cb058f66baca957ef4aae24a77c89b7667 Mon Sep 17 00:00:00 2001 From: Daniel Mader Date: Tue, 23 Jul 2024 21:28:43 +0200 Subject: [PATCH 30/37] feat: install `@commitlint` and `semantic-release` --- .gitignore | 2 + README.md | 5 + commitlint.config.js | 1 + package-lock.json | 6705 ++++++++++++++++++++++++++++++++++++++++++ package.json | 25 + 5 files changed, 6738 insertions(+) create mode 100644 commitlint.config.js create mode 100644 package-lock.json create mode 100644 package.json diff --git a/.gitignore b/.gitignore index d43a0f1e..229e4e78 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ **/*.env !**/.env.example + +node_modules diff --git a/README.md b/README.md index 92d9857e..bd4d7eb9 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ # SSI Agent +[![semantic-release: angular](https://img.shields.io/badge/semantic--release-angular-e10079?logo=semantic-release)](https://github.com/semantic-release/semantic-release) +[![License](https://img.shields.io/github/license/impierce/ssi-agent.svg)](https://github.com/impierce/ssi-agent/blob/HEAD/LICENSE) + +--- + ## API specification [Follow these instructions](./agent_api_rest/README.md) to inspect the REST API. diff --git a/commitlint.config.js b/commitlint.config.js new file mode 100644 index 00000000..5d2cf86a --- /dev/null +++ b/commitlint.config.js @@ -0,0 +1 @@ +export default { extends: ["@commitlint/config-angular"] }; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..e016f627 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6705 @@ +{ + "name": "ssi-agent", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "ssi-agent", + "version": "0.1.0", + "license": "Apache-2.0", + "devDependencies": { + "@commitlint/cli": "^19.3.0", + "@commitlint/config-angular": "^19.3.0", + "semantic-release": "^24.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", + "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.24.7", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", + "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", + "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.24.7", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@commitlint/cli": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-19.3.0.tgz", + "integrity": "sha512-LgYWOwuDR7BSTQ9OLZ12m7F/qhNY+NpAyPBgo4YNMkACE7lGuUnuQq1yi9hz1KA4+3VqpOYl8H1rY/LYK43v7g==", + "dev": true, + "dependencies": { + "@commitlint/format": "^19.3.0", + "@commitlint/lint": "^19.2.2", + "@commitlint/load": "^19.2.0", + "@commitlint/read": "^19.2.1", + "@commitlint/types": "^19.0.3", + "execa": "^8.0.1", + "yargs": "^17.0.0" + }, + "bin": { + "commitlint": "cli.js" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/cli/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/@commitlint/cli/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/cli/node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/@commitlint/cli/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/cli/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/config-angular": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-angular/-/config-angular-19.3.0.tgz", + "integrity": "sha512-D8ue6s7f/A/ph/4vSEj32zxg/WHRF21vguOigAymUJ7SfUPF/BD+C/UGt7I1aEEhdgoq7MIS8bNAJroYvSzMwQ==", + "dev": true, + "dependencies": { + "@commitlint/config-angular-type-enum": "^19.1.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/config-angular-type-enum": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-angular-type-enum/-/config-angular-type-enum-19.1.0.tgz", + "integrity": "sha512-eLjt7vSArP62kpDmmIZNdIBjPzbqY8jss6mVOcSDm4t1KfDw4UmPrtPh/7zcIL3GI5uf/7W8d2s3K0qisf9C+g==", + "dev": true, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/config-validator": { + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-19.0.3.tgz", + "integrity": "sha512-2D3r4PKjoo59zBc2auodrSCaUnCSALCx54yveOFwwP/i2kfEAQrygwOleFWswLqK0UL/F9r07MFi5ev2ohyM4Q==", + "dev": true, + "dependencies": { + "@commitlint/types": "^19.0.3", + "ajv": "^8.11.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/ensure": { + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-19.0.3.tgz", + "integrity": "sha512-SZEpa/VvBLoT+EFZVb91YWbmaZ/9rPH3ESrINOl0HD2kMYsjvl0tF7nMHh0EpTcv4+gTtZBAe1y/SS6/OhfZzQ==", + "dev": true, + "dependencies": { + "@commitlint/types": "^19.0.3", + "lodash.camelcase": "^4.3.0", + "lodash.kebabcase": "^4.1.1", + "lodash.snakecase": "^4.1.1", + "lodash.startcase": "^4.4.0", + "lodash.upperfirst": "^4.3.1" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/execute-rule": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-19.0.0.tgz", + "integrity": "sha512-mtsdpY1qyWgAO/iOK0L6gSGeR7GFcdW7tIjcNFxcWkfLDF5qVbPHKuGATFqRMsxcO8OUKNj0+3WOHB7EHm4Jdw==", + "dev": true, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/format": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-19.3.0.tgz", + "integrity": "sha512-luguk5/aF68HiF4H23ACAfk8qS8AHxl4LLN5oxPc24H+2+JRPsNr1OS3Gaea0CrH7PKhArBMKBz5RX9sA5NtTg==", + "dev": true, + "dependencies": { + "@commitlint/types": "^19.0.3", + "chalk": "^5.3.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/is-ignored": { + "version": "19.2.2", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-19.2.2.tgz", + "integrity": "sha512-eNX54oXMVxncORywF4ZPFtJoBm3Tvp111tg1xf4zWXGfhBPKpfKG6R+G3G4v5CPlRROXpAOpQ3HMhA9n1Tck1g==", + "dev": true, + "dependencies": { + "@commitlint/types": "^19.0.3", + "semver": "^7.6.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/lint": { + "version": "19.2.2", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-19.2.2.tgz", + "integrity": "sha512-xrzMmz4JqwGyKQKTpFzlN0dx0TAiT7Ran1fqEBgEmEj+PU98crOFtysJgY+QdeSagx6EDRigQIXJVnfrI0ratA==", + "dev": true, + "dependencies": { + "@commitlint/is-ignored": "^19.2.2", + "@commitlint/parse": "^19.0.3", + "@commitlint/rules": "^19.0.3", + "@commitlint/types": "^19.0.3" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/load": { + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-19.2.0.tgz", + "integrity": "sha512-XvxxLJTKqZojCxaBQ7u92qQLFMMZc4+p9qrIq/9kJDy8DOrEa7P1yx7Tjdc2u2JxIalqT4KOGraVgCE7eCYJyQ==", + "dev": true, + "dependencies": { + "@commitlint/config-validator": "^19.0.3", + "@commitlint/execute-rule": "^19.0.0", + "@commitlint/resolve-extends": "^19.1.0", + "@commitlint/types": "^19.0.3", + "chalk": "^5.3.0", + "cosmiconfig": "^9.0.0", + "cosmiconfig-typescript-loader": "^5.0.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "lodash.uniq": "^4.5.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/message": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-19.0.0.tgz", + "integrity": "sha512-c9czf6lU+9oF9gVVa2lmKaOARJvt4soRsVmbR7Njwp9FpbBgste5i7l/2l5o8MmbwGh4yE1snfnsy2qyA2r/Fw==", + "dev": true, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/parse": { + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-19.0.3.tgz", + "integrity": "sha512-Il+tNyOb8VDxN3P6XoBBwWJtKKGzHlitEuXA5BP6ir/3loWlsSqDr5aecl6hZcC/spjq4pHqNh0qPlfeWu38QA==", + "dev": true, + "dependencies": { + "@commitlint/types": "^19.0.3", + "conventional-changelog-angular": "^7.0.0", + "conventional-commits-parser": "^5.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/parse/node_modules/conventional-changelog-angular": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz", + "integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==", + "dev": true, + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@commitlint/parse/node_modules/conventional-commits-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", + "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==", + "dev": true, + "dependencies": { + "is-text-path": "^2.0.0", + "JSONStream": "^1.3.5", + "meow": "^12.0.1", + "split2": "^4.0.0" + }, + "bin": { + "conventional-commits-parser": "cli.mjs" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@commitlint/parse/node_modules/meow": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", + "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", + "dev": true, + "engines": { + "node": ">=16.10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/parse/node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true, + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/@commitlint/read": { + "version": "19.2.1", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-19.2.1.tgz", + "integrity": "sha512-qETc4+PL0EUv7Q36lJbPG+NJiBOGg7SSC7B5BsPWOmei+Dyif80ErfWQ0qXoW9oCh7GTpTNRoaVhiI8RbhuaNw==", + "dev": true, + "dependencies": { + "@commitlint/top-level": "^19.0.0", + "@commitlint/types": "^19.0.3", + "execa": "^8.0.1", + "git-raw-commits": "^4.0.0", + "minimist": "^1.2.8" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/read/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/@commitlint/read/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/read/node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/@commitlint/read/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/read/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/resolve-extends": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-19.1.0.tgz", + "integrity": "sha512-z2riI+8G3CET5CPgXJPlzftH+RiWYLMYv4C9tSLdLXdr6pBNimSKukYP9MS27ejmscqCTVA4almdLh0ODD2KYg==", + "dev": true, + "dependencies": { + "@commitlint/config-validator": "^19.0.3", + "@commitlint/types": "^19.0.3", + "global-directory": "^4.0.1", + "import-meta-resolve": "^4.0.0", + "lodash.mergewith": "^4.6.2", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/rules": { + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-19.0.3.tgz", + "integrity": "sha512-TspKb9VB6svklxNCKKwxhELn7qhtY1rFF8ls58DcFd0F97XoG07xugPjjbVnLqmMkRjZDbDIwBKt9bddOfLaPw==", + "dev": true, + "dependencies": { + "@commitlint/ensure": "^19.0.3", + "@commitlint/message": "^19.0.0", + "@commitlint/to-lines": "^19.0.0", + "@commitlint/types": "^19.0.3", + "execa": "^8.0.1" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/rules/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/@commitlint/rules/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/rules/node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/@commitlint/rules/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/rules/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/to-lines": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-19.0.0.tgz", + "integrity": "sha512-vkxWo+VQU5wFhiP9Ub9Sre0FYe019JxFikrALVoD5UGa8/t3yOJEpEhxC5xKiENKKhUkTpEItMTRAjHw2SCpZw==", + "dev": true, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/top-level": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-19.0.0.tgz", + "integrity": "sha512-KKjShd6u1aMGNkCkaX4aG1jOGdn7f8ZI8TR1VEuNqUOjWTOdcDSsmglinglJ18JTjuBX5I1PtjrhQCRcixRVFQ==", + "dev": true, + "dependencies": { + "find-up": "^7.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/top-level/node_modules/find-up": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz", + "integrity": "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==", + "dev": true, + "dependencies": { + "locate-path": "^7.2.0", + "path-exists": "^5.0.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dev": true, + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/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, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/@commitlint/types": { + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-19.0.3.tgz", + "integrity": "sha512-tpyc+7i6bPG9mvaBbtKUeghfyZSDgWquIDfMgqYtTbmZ9Y9VzEm2je9EYcQ0aoz5o7NvGS+rcDec93yO08MHYA==", + "dev": true, + "dependencies": { + "@types/conventional-commits-parser": "^5.0.0", + "chalk": "^5.3.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@octokit/auth-token": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.1.tgz", + "integrity": "sha512-rh3G3wDO8J9wSjfI436JUKzHIxq8NaiL0tVeB2aXmG6p/9859aUOAjA9pmSPNGGZxfwmaJ9ozOJImuNVJdpvbA==", + "dev": true, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/core": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.2.tgz", + "integrity": "sha512-hEb7Ma4cGJGEUNOAVmyfdB/3WirWMg5hDuNFVejGEDFqupeOysLc2sG6HJxY2etBp5YQu5Wtxwi020jS9xlUwg==", + "dev": true, + "dependencies": { + "@octokit/auth-token": "^5.0.0", + "@octokit/graphql": "^8.0.0", + "@octokit/request": "^9.0.0", + "@octokit/request-error": "^6.0.1", + "@octokit/types": "^13.0.0", + "before-after-hook": "^3.0.2", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/endpoint": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.1.tgz", + "integrity": "sha512-JYjh5rMOwXMJyUpj028cu0Gbp7qe/ihxfJMLc8VZBMMqSwLgOxDI1911gV4Enl1QSavAQNJcwmwBF9M0VvLh6Q==", + "dev": true, + "dependencies": { + "@octokit/types": "^13.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/graphql": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.1.1.tgz", + "integrity": "sha512-ukiRmuHTi6ebQx/HFRCXKbDlOh/7xEV6QUXaE7MJEKGNAncGI/STSbOkl12qVXZrfZdpXctx5O9X1AIaebiDBg==", + "dev": true, + "dependencies": { + "@octokit/request": "^9.0.0", + "@octokit/types": "^13.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "22.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz", + "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==", + "dev": true + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "11.3.3", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.3.3.tgz", + "integrity": "sha512-o4WRoOJZlKqEEgj+i9CpcmnByvtzoUYC6I8PD2SA95M+BJ2x8h7oLcVOg9qcowWXBOdcTRsMZiwvM3EyLm9AfA==", + "dev": true, + "dependencies": { + "@octokit/types": "^13.5.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-retry": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-7.1.1.tgz", + "integrity": "sha512-G9Ue+x2odcb8E1XIPhaFBnTTIrrUDfXN05iFXiqhR+SeeeDMMILcAnysOsxUpEWcQp2e5Ft397FCXTcPkiPkLw==", + "dev": true, + "dependencies": { + "@octokit/request-error": "^6.0.0", + "@octokit/types": "^13.0.0", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-throttling": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-9.3.1.tgz", + "integrity": "sha512-Qd91H4liUBhwLB2h6jZ99bsxoQdhgPk6TdwnClPyTBSDAdviGPceViEgUwj+pcQDmB/rfAXAXK7MTochpHM3yQ==", + "dev": true, + "dependencies": { + "@octokit/types": "^13.0.0", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "^6.0.0" + } + }, + "node_modules/@octokit/request": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.1.3.tgz", + "integrity": "sha512-V+TFhu5fdF3K58rs1pGUJIDH5RZLbZm5BI+MNF+6o/ssFNT4vWlCh/tVpF3NxGtP15HUxTTMUbsG5llAuU2CZA==", + "dev": true, + "dependencies": { + "@octokit/endpoint": "^10.0.0", + "@octokit/request-error": "^6.0.1", + "@octokit/types": "^13.1.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/request-error": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.4.tgz", + "integrity": "sha512-VpAhIUxwhWZQImo/dWAN/NpPqqojR6PSLgLYAituLM6U+ddx9hCioFGwBr5Mi+oi5CLeJkcAs3gJ0PYYzU6wUg==", + "dev": true, + "dependencies": { + "@octokit/types": "^13.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/types": { + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.5.0.tgz", + "integrity": "sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==", + "dev": true, + "dependencies": { + "@octokit/openapi-types": "^22.2.0" + } + }, + "node_modules/@pnpm/config.env-replace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", + "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", + "dev": true, + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/network.ca-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", + "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", + "dev": true, + "dependencies": { + "graceful-fs": "4.2.10" + }, + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "node_modules/@pnpm/npm-conf": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.2.2.tgz", + "integrity": "sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==", + "dev": true, + "dependencies": { + "@pnpm/config.env-replace": "^1.1.0", + "@pnpm/network.ca-file": "^1.0.1", + "config-chain": "^1.1.11" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", + "dev": true + }, + "node_modules/@semantic-release/commit-analyzer": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-13.0.0.tgz", + "integrity": "sha512-KtXWczvTAB1ZFZ6B4O+w8HkfYm/OgQb1dUGNFZtDgQ0csggrmkq8sTxhd+lwGF8kMb59/RnG9o4Tn7M/I8dQ9Q==", + "dev": true, + "dependencies": { + "conventional-changelog-angular": "^8.0.0", + "conventional-changelog-writer": "^8.0.0", + "conventional-commits-filter": "^5.0.0", + "conventional-commits-parser": "^6.0.0", + "debug": "^4.0.0", + "import-from-esm": "^1.0.3", + "lodash-es": "^4.17.21", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=20.8.1" + }, + "peerDependencies": { + "semantic-release": ">=20.1.0" + } + }, + "node_modules/@semantic-release/error": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-4.0.0.tgz", + "integrity": "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@semantic-release/github": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-10.1.1.tgz", + "integrity": "sha512-sSmsBKGpAlTtXf9rUJf/si16p+FwPEsvsJRjl3KCwFP0WywaSpynvUhlYvE18n5rzkQNbGJnObAKIoo3xFMSjA==", + "dev": true, + "dependencies": { + "@octokit/core": "^6.0.0", + "@octokit/plugin-paginate-rest": "^11.0.0", + "@octokit/plugin-retry": "^7.0.0", + "@octokit/plugin-throttling": "^9.0.0", + "@semantic-release/error": "^4.0.0", + "aggregate-error": "^5.0.0", + "debug": "^4.3.4", + "dir-glob": "^3.0.1", + "globby": "^14.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "issue-parser": "^7.0.0", + "lodash-es": "^4.17.21", + "mime": "^4.0.0", + "p-filter": "^4.0.0", + "url-join": "^5.0.0" + }, + "engines": { + "node": ">=20.8.1" + }, + "peerDependencies": { + "semantic-release": ">=20.1.0" + } + }, + "node_modules/@semantic-release/npm": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-12.0.1.tgz", + "integrity": "sha512-/6nntGSUGK2aTOI0rHPwY3ZjgY9FkXmEHbW9Kr+62NVOsyqpKKeP0lrCH+tphv+EsNdJNmqqwijTEnVWUMQ2Nw==", + "dev": true, + "dependencies": { + "@semantic-release/error": "^4.0.0", + "aggregate-error": "^5.0.0", + "execa": "^9.0.0", + "fs-extra": "^11.0.0", + "lodash-es": "^4.17.21", + "nerf-dart": "^1.0.0", + "normalize-url": "^8.0.0", + "npm": "^10.5.0", + "rc": "^1.2.8", + "read-pkg": "^9.0.0", + "registry-auth-token": "^5.0.0", + "semver": "^7.1.2", + "tempy": "^3.0.0" + }, + "engines": { + "node": ">=20.8.1" + }, + "peerDependencies": { + "semantic-release": ">=20.1.0" + } + }, + "node_modules/@semantic-release/release-notes-generator": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-14.0.1.tgz", + "integrity": "sha512-K0w+5220TM4HZTthE5dDpIuFrnkN1NfTGPidJFm04ULT1DEZ9WG89VNXN7F0c+6nMEpWgqmPvb7vY7JkB2jyyA==", + "dev": true, + "dependencies": { + "conventional-changelog-angular": "^8.0.0", + "conventional-changelog-writer": "^8.0.0", + "conventional-commits-filter": "^5.0.0", + "conventional-commits-parser": "^6.0.0", + "debug": "^4.0.0", + "get-stream": "^7.0.0", + "import-from-esm": "^1.0.3", + "into-stream": "^7.0.0", + "lodash-es": "^4.17.21", + "read-package-up": "^11.0.0" + }, + "engines": { + "node": ">=20.8.1" + }, + "peerDependencies": { + "semantic-release": ">=20.1.0" + } + }, + "node_modules/@semantic-release/release-notes-generator/node_modules/get-stream": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-7.0.1.tgz", + "integrity": "sha512-3M8C1EOFN6r8AMUhwUAACIoXZJEOufDU5+0gFFN5uNs6XYOralD2Pqkl7m046va6x77FwposWXbAhPPIOus7mQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@types/conventional-commits-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", + "integrity": "sha512-loB369iXNmAZglwWATL+WRe+CRMmmBPtpolYzIebFaX4YA3x+BEfLqhUAV9WanycKI3TG1IMr5bMJDajDKLlUQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "20.14.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.12.tgz", + "integrity": "sha512-r7wNXakLeSsGT0H1AU863vS2wa5wBOK4bWMjZz2wj+8nBx+m5PeIn0k8AloSLpRuiwdRQZwarZqHE4FNArPuJQ==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true + }, + "node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dev": true, + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/aggregate-error": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-5.0.0.tgz", + "integrity": "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==", + "dev": true, + "dependencies": { + "clean-stack": "^5.2.0", + "indent-string": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", + "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", + "dev": true, + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/argv-formatter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/argv-formatter/-/argv-formatter-1.0.0.tgz", + "integrity": "sha512-F2+Hkm9xFaRg+GkaNnbwXNDV5O6pnCFEmqyhvfC/Ic5LbgOWjJh3L+mN/s91rxVL3znE7DYVpW0GJFT+4YBgWw==", + "dev": true + }, + "node_modules/array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", + "dev": true + }, + "node_modules/before-after-hook": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz", + "integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==", + "dev": true + }, + "node_modules/bottleneck": { + "version": "2.19.5", + "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", + "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", + "dev": true + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/clean-stack": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-5.2.0.tgz", + "integrity": "sha512-TyUIUJgdFnCISzG5zu3291TAsE77ddchd0bepon1VVQrKLGKFED4iXFEDQ24mIPdPBbyE16PK3F8MYE1CmcBEQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "5.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-highlight": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", + "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "highlight.js": "^10.7.1", + "mz": "^2.4.0", + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^6.0.0", + "yargs": "^16.0.0" + }, + "bin": { + "highlight": "bin/highlight" + }, + "engines": { + "node": ">=8.0.0", + "npm": ">=5.0.0" + } + }, + "node_modules/cli-highlight/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cli-highlight/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cli-highlight/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cli-highlight/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, + "dependencies": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/conventional-changelog-angular": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-8.0.0.tgz", + "integrity": "sha512-CLf+zr6St0wIxos4bmaKHRXWAcsCXrJU6F4VdNDrGRK3B8LDLKoX3zuMV5GhtbGkVR/LohZ6MT6im43vZLSjmA==", + "dev": true, + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-changelog-writer": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-8.0.0.tgz", + "integrity": "sha512-TQcoYGRatlAnT2qEWDON/XSfnVG38JzA7E0wcGScu7RElQBkg9WWgZd1peCWFcWDh1xfb2CfsrcvOn1bbSzztA==", + "dev": true, + "dependencies": { + "@types/semver": "^7.5.5", + "conventional-commits-filter": "^5.0.0", + "handlebars": "^4.7.7", + "meow": "^13.0.0", + "semver": "^7.5.2" + }, + "bin": { + "conventional-changelog-writer": "dist/cli/index.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-commits-filter": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-5.0.0.tgz", + "integrity": "sha512-tQMagCOC59EVgNZcC5zl7XqO30Wki9i9J3acbUvkaosCT6JX3EeFwJD7Qqp4MCikRnzS18WXV3BLIQ66ytu6+Q==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-commits-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.0.0.tgz", + "integrity": "sha512-TbsINLp48XeMXR8EvGjTnKGsZqBemisPoyWESlpRyR8lif0lcwzqz+NMtYSj1ooF/WYjSuu7wX0CtdeeMEQAmA==", + "dev": true, + "dependencies": { + "meow": "^13.0.0" + }, + "bin": { + "conventional-commits-parser": "dist/cli/index.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/convert-hrtime": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/convert-hrtime/-/convert-hrtime-5.0.0.tgz", + "integrity": "sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "dev": true, + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/cosmiconfig-typescript-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-5.0.0.tgz", + "integrity": "sha512-+8cK7jRAReYkMwMiG+bxhcNKiHJDM6bR9FD/nGBXOWdMLuYawjF5cGrtLilJ+LGd3ZjCXnJjR5DkfWPoIVlqJA==", + "dev": true, + "dependencies": { + "jiti": "^1.19.1" + }, + "engines": { + "node": ">=v16" + }, + "peerDependencies": { + "@types/node": "*", + "cosmiconfig": ">=8.2", + "typescript": ">=4" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", + "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", + "dev": true, + "dependencies": { + "type-fest": "^1.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/crypto-random-string/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dargs": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-8.1.0.tgz", + "integrity": "sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/emojilib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", + "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==", + "dev": true + }, + "node_modules/env-ci": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-11.0.0.tgz", + "integrity": "sha512-apikxMgkipkgTvMdRT9MNqWx5VLOci79F4VBd7Op/7OPjjoanjdAvn6fglMCCEf/1bAh8eOiuEVCUs4V3qP3nQ==", + "dev": true, + "dependencies": { + "execa": "^8.0.0", + "java-properties": "^1.0.2" + }, + "engines": { + "node": "^18.17 || >=20.6.1" + } + }, + "node_modules/env-ci/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/env-ci/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-ci/node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/env-ci/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-ci/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/execa": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.3.0.tgz", + "integrity": "sha512-l6JFbqnHEadBoVAVpN5dl2yCyfX28WoBAGaoQcNmLLSedOxTxcn2Qa83s8I/PA5i56vWru2OHOtrwF7Om2vqlg==", + "dev": true, + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.3", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^7.0.0", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^5.2.0", + "pretty-ms": "^9.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.5.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "dev": true, + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-uri": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", + "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "dev": true, + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/find-up-simple": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.0.tgz", + "integrity": "sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-versions": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-6.0.0.tgz", + "integrity": "sha512-2kCCtc+JvcZ86IGAz3Z2Y0A1baIz9fL31pH/0S1IqZr9Iwnjq8izfPtrCyQKO6TLMPELLsQMre7VDqeIKCsHkA==", + "dev": true, + "dependencies": { + "semver-regex": "^4.0.5", + "super-regex": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/function-timeout": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/function-timeout/-/function-timeout-1.0.2.tgz", + "integrity": "sha512-939eZS4gJ3htTHAldmyyuzlrD58P03fHG49v2JfFXbV6OhvZKRC9j2yAtdHw/zrp2zXHuv05zMIy40F0ge7spA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/git-log-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/git-log-parser/-/git-log-parser-1.2.1.tgz", + "integrity": "sha512-PI+sPDvHXNPl5WNOErAK05s3j0lgwUzMN6o8cyQrDaKfT3qd7TmNJKeXX+SknI5I0QhG5fVPAEwSY4tRGDtYoQ==", + "dev": true, + "dependencies": { + "argv-formatter": "~1.0.0", + "spawn-error-forwarder": "~1.0.0", + "split2": "~1.0.0", + "stream-combiner2": "~1.1.1", + "through2": "~2.0.0", + "traverse": "0.6.8" + } + }, + "node_modules/git-raw-commits": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-4.0.0.tgz", + "integrity": "sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==", + "dev": true, + "dependencies": { + "dargs": "^8.0.0", + "meow": "^12.0.1", + "split2": "^4.0.0" + }, + "bin": { + "git-raw-commits": "cli.mjs" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/git-raw-commits/node_modules/meow": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", + "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", + "dev": true, + "engines": { + "node": ">=16.10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/git-raw-commits/node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true, + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/global-directory": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", + "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", + "dev": true, + "dependencies": { + "ini": "4.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/global-directory/node_modules/ini": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", + "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/globby": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.2.tgz", + "integrity": "sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==", + "dev": true, + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.2", + "ignore": "^5.2.4", + "path-type": "^5.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby/node_modules/path-type": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", + "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/hook-std": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-3.0.0.tgz", + "integrity": "sha512-jHRQzjSDzMtFy34AGj1DN+vq54WVuhSvKgrHf0OMiFQTwDD4L/qqofVEWjLOBMTn5+lCD3fPg32W9yOfnEJTTw==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "dev": true, + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "dev": true, + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-7.0.0.tgz", + "integrity": "sha512-74kytxOUSvNbjrT9KisAbaTZ/eJwD/LrbM/kh5j0IhPuJzwuA19dWvniFGwBzN9rVjg+O/e+F310PjObDXS+9Q==", + "dev": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-from-esm": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/import-from-esm/-/import-from-esm-1.3.4.tgz", + "integrity": "sha512-7EyUlPFC0HOlBDpUFGfYstsU7XHxZJKAAMzCT8wZ0hMW7b+hG51LIKTDcsgtz8Pu6YC0HqRVbX+rVUtsGMUKvg==", + "dev": true, + "dependencies": { + "debug": "^4.3.4", + "import-meta-resolve": "^4.0.0" + }, + "engines": { + "node": ">=16.20" + } + }, + "node_modules/import-meta-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", + "integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/indent-string": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", + "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/index-to-position": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-0.1.2.tgz", + "integrity": "sha512-MWDKS3AS1bGCHLBA2VLImJz42f7bJh8wQsTGCzI3j519/CASStoDONUBVz2I/VID0MpiX3SGSnbOD2xUalbE5g==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/into-stream": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-7.0.0.tgz", + "integrity": "sha512-2dYz766i9HprMBasCMvHMuazJ7u4WzhJwo5kb3iPSiW/iRYV6uPari3zHoqZlnuaR7V1bEiNMxikhp37rdBXbw==", + "dev": true, + "dependencies": { + "from2": "^2.3.0", + "p-is-promise": "^3.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-text-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz", + "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==", + "dev": true, + "dependencies": { + "text-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.0.0.tgz", + "integrity": "sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/issue-parser": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-7.0.1.tgz", + "integrity": "sha512-3YZcUUR2Wt1WsapF+S/WiA2WmlW0cWAoPccMqne7AxEBhCdFeTPjfv/Axb8V2gyCgY3nRw+ksZ3xSUX+R47iAg==", + "dev": true, + "dependencies": { + "lodash.capitalize": "^4.2.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.uniqby": "^4.7.0" + }, + "engines": { + "node": "^18.17 || >=20.6.1" + } + }, + "node_modules/java-properties": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/java-properties/-/java-properties-1.0.2.tgz", + "integrity": "sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/jiti": { + "version": "1.21.6", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", + "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ] + }, + "node_modules/JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "dev": true + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true + }, + "node_modules/lodash.capitalize": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", + "integrity": "sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw==", + "dev": true + }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", + "dev": true + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true + }, + "node_modules/lodash.kebabcase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", + "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "dev": true + }, + "node_modules/lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", + "dev": true + }, + "node_modules/lodash.startcase": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", + "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", + "dev": true + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true + }, + "node_modules/lodash.uniqby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", + "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==", + "dev": true + }, + "node_modules/lodash.upperfirst": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", + "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/marked": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-12.0.2.tgz", + "integrity": "sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==", + "dev": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/marked-terminal": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-7.1.0.tgz", + "integrity": "sha512-+pvwa14KZL74MVXjYdPR3nSInhGhNvPce/3mqLVZT2oUvt654sL1XImFuLZ1pkA866IYZ3ikDTOFUIC7XzpZZg==", + "dev": true, + "dependencies": { + "ansi-escapes": "^7.0.0", + "chalk": "^5.3.0", + "cli-highlight": "^2.1.11", + "cli-table3": "^0.6.5", + "node-emoji": "^2.1.3", + "supports-hyperlinks": "^3.0.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "marked": ">=1 <14" + } + }, + "node_modules/meow": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", + "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.4.tgz", + "integrity": "sha512-v8yqInVjhXyqP6+Kw4fV3ZzeMRqEW6FotRsKXjRS5VMTNIuXsdRoAvklpoRgSqXm6o9VNH4/C0mgedko9DdLsQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa" + ], + "bin": { + "mime": "bin/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/nerf-dart": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/nerf-dart/-/nerf-dart-1.0.0.tgz", + "integrity": "sha512-EZSPZB70jiVsivaBLYDCyntd5eH8NTSMOn3rB+HxwdmKThGELLdYv8qVIMWvZEFy9w8ZZpW9h9OB32l1rGtj7g==", + "dev": true + }, + "node_modules/node-emoji": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.1.3.tgz", + "integrity": "sha512-E2WEOVsgs7O16zsURJ/eH8BqhF029wGpEOnv7Urwdo2wmQanOACwJQh0devF9D9RhoZru0+9JXIS0dBXIAz+lA==", + "dev": true, + "dependencies": { + "@sindresorhus/is": "^4.6.0", + "char-regex": "^1.0.2", + "emojilib": "^2.4.0", + "skin-tone": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", + "dev": true, + "dependencies": { + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/normalize-url": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz", + "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm": { + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/npm/-/npm-10.8.2.tgz", + "integrity": "sha512-x/AIjFIKRllrhcb48dqUNAAZl0ig9+qMuN91RpZo3Cb2+zuibfh+KISl6+kVVyktDz230JKc208UkQwwMqyB+w==", + "bundleDependencies": [ + "@isaacs/string-locale-compare", + "@npmcli/arborist", + "@npmcli/config", + "@npmcli/fs", + "@npmcli/map-workspaces", + "@npmcli/package-json", + "@npmcli/promise-spawn", + "@npmcli/redact", + "@npmcli/run-script", + "@sigstore/tuf", + "abbrev", + "archy", + "cacache", + "chalk", + "ci-info", + "cli-columns", + "fastest-levenshtein", + "fs-minipass", + "glob", + "graceful-fs", + "hosted-git-info", + "ini", + "init-package-json", + "is-cidr", + "json-parse-even-better-errors", + "libnpmaccess", + "libnpmdiff", + "libnpmexec", + "libnpmfund", + "libnpmhook", + "libnpmorg", + "libnpmpack", + "libnpmpublish", + "libnpmsearch", + "libnpmteam", + "libnpmversion", + "make-fetch-happen", + "minimatch", + "minipass", + "minipass-pipeline", + "ms", + "node-gyp", + "nopt", + "normalize-package-data", + "npm-audit-report", + "npm-install-checks", + "npm-package-arg", + "npm-pick-manifest", + "npm-profile", + "npm-registry-fetch", + "npm-user-validate", + "p-map", + "pacote", + "parse-conflict-json", + "proc-log", + "qrcode-terminal", + "read", + "semver", + "spdx-expression-parse", + "ssri", + "supports-color", + "tar", + "text-table", + "tiny-relative-date", + "treeverse", + "validate-npm-package-name", + "which", + "write-file-atomic" + ], + "dev": true, + "workspaces": [ + "docs", + "smoke-tests", + "mock-globals", + "mock-registry", + "workspaces/*" + ], + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/arborist": "^7.5.4", + "@npmcli/config": "^8.3.4", + "@npmcli/fs": "^3.1.1", + "@npmcli/map-workspaces": "^3.0.6", + "@npmcli/package-json": "^5.2.0", + "@npmcli/promise-spawn": "^7.0.2", + "@npmcli/redact": "^2.0.1", + "@npmcli/run-script": "^8.1.0", + "@sigstore/tuf": "^2.3.4", + "abbrev": "^2.0.0", + "archy": "~1.0.0", + "cacache": "^18.0.3", + "chalk": "^5.3.0", + "ci-info": "^4.0.0", + "cli-columns": "^4.0.0", + "fastest-levenshtein": "^1.0.16", + "fs-minipass": "^3.0.3", + "glob": "^10.4.2", + "graceful-fs": "^4.2.11", + "hosted-git-info": "^7.0.2", + "ini": "^4.1.3", + "init-package-json": "^6.0.3", + "is-cidr": "^5.1.0", + "json-parse-even-better-errors": "^3.0.2", + "libnpmaccess": "^8.0.6", + "libnpmdiff": "^6.1.4", + "libnpmexec": "^8.1.3", + "libnpmfund": "^5.0.12", + "libnpmhook": "^10.0.5", + "libnpmorg": "^6.0.6", + "libnpmpack": "^7.0.4", + "libnpmpublish": "^9.0.9", + "libnpmsearch": "^7.0.6", + "libnpmteam": "^6.0.5", + "libnpmversion": "^6.0.3", + "make-fetch-happen": "^13.0.1", + "minimatch": "^9.0.5", + "minipass": "^7.1.1", + "minipass-pipeline": "^1.2.4", + "ms": "^2.1.2", + "node-gyp": "^10.1.0", + "nopt": "^7.2.1", + "normalize-package-data": "^6.0.2", + "npm-audit-report": "^5.0.0", + "npm-install-checks": "^6.3.0", + "npm-package-arg": "^11.0.2", + "npm-pick-manifest": "^9.1.0", + "npm-profile": "^10.0.0", + "npm-registry-fetch": "^17.1.0", + "npm-user-validate": "^2.0.1", + "p-map": "^4.0.0", + "pacote": "^18.0.6", + "parse-conflict-json": "^3.0.1", + "proc-log": "^4.2.0", + "qrcode-terminal": "^0.12.0", + "read": "^3.0.1", + "semver": "^7.6.2", + "spdx-expression-parse": "^4.0.0", + "ssri": "^10.0.6", + "supports-color": "^9.4.0", + "tar": "^6.2.1", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "treeverse": "^3.0.0", + "validate-npm-package-name": "^5.0.1", + "which": "^4.0.0", + "write-file-atomic": "^5.0.1" + }, + "bin": { + "npm": "bin/npm-cli.js", + "npx": "bin/npx-cli.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui": { + "version": "8.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/string-locale-compare": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/@npmcli/agent": { + "version": "2.2.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/arborist": { + "version": "7.5.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/fs": "^3.1.1", + "@npmcli/installed-package-contents": "^2.1.0", + "@npmcli/map-workspaces": "^3.0.2", + "@npmcli/metavuln-calculator": "^7.1.1", + "@npmcli/name-from-folder": "^2.0.0", + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/package-json": "^5.1.0", + "@npmcli/query": "^3.1.0", + "@npmcli/redact": "^2.0.0", + "@npmcli/run-script": "^8.1.0", + "bin-links": "^4.0.4", + "cacache": "^18.0.3", + "common-ancestor-path": "^1.0.1", + "hosted-git-info": "^7.0.2", + "json-parse-even-better-errors": "^3.0.2", + "json-stringify-nice": "^1.1.4", + "lru-cache": "^10.2.2", + "minimatch": "^9.0.4", + "nopt": "^7.2.1", + "npm-install-checks": "^6.2.0", + "npm-package-arg": "^11.0.2", + "npm-pick-manifest": "^9.0.1", + "npm-registry-fetch": "^17.0.1", + "pacote": "^18.0.6", + "parse-conflict-json": "^3.0.0", + "proc-log": "^4.2.0", + "proggy": "^2.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^3.0.1", + "read-package-json-fast": "^3.0.2", + "semver": "^7.3.7", + "ssri": "^10.0.6", + "treeverse": "^3.0.0", + "walk-up-path": "^3.0.1" + }, + "bin": { + "arborist": "bin/index.js" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/config": { + "version": "8.3.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/map-workspaces": "^3.0.2", + "@npmcli/package-json": "^5.1.1", + "ci-info": "^4.0.0", + "ini": "^4.1.2", + "nopt": "^7.2.1", + "proc-log": "^4.2.0", + "semver": "^7.3.5", + "walk-up-path": "^3.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/fs": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/git": { + "version": "5.0.8", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^7.0.0", + "ini": "^4.1.3", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^9.0.0", + "proc-log": "^4.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/installed-package-contents": { + "version": "2.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "bin": { + "installed-package-contents": "bin/index.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/map-workspaces": { + "version": "3.0.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/name-from-folder": "^2.0.0", + "glob": "^10.2.2", + "minimatch": "^9.0.0", + "read-package-json-fast": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { + "version": "7.1.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cacache": "^18.0.0", + "json-parse-even-better-errors": "^3.0.0", + "pacote": "^18.0.0", + "proc-log": "^4.1.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/name-from-folder": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/node-gyp": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/package-json": { + "version": "5.2.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^5.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^7.0.0", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^6.0.0", + "proc-log": "^4.0.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/promise-spawn": { + "version": "7.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/query": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/redact": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/run-script": { + "version": "8.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/package-json": "^5.0.0", + "@npmcli/promise-spawn": "^7.0.0", + "node-gyp": "^10.0.0", + "proc-log": "^4.0.0", + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/@sigstore/bundle": { + "version": "2.3.2", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.2" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/core": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/protobuf-specs": { + "version": "0.3.2", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/sign": { + "version": "2.3.2", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "make-fetch-happen": "^13.0.1", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/tuf": { + "version": "2.3.4", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.2", + "tuf-js": "^2.2.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/verify": { + "version": "1.2.1", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.1.0", + "@sigstore/protobuf-specs": "^0.3.2" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@tufjs/models": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/abbrev": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/agent-base": { + "version": "7.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/aggregate-error": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ansi-styles": { + "version": "6.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/aproba": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/archy": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/bin-links": { + "version": "4.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cmd-shim": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "read-cmd-shim": "^4.0.0", + "write-file-atomic": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/binary-extensions": { + "version": "2.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/npm/node_modules/cacache": { + "version": "18.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/chalk": { + "version": "5.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/npm/node_modules/chownr": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ci-info": { + "version": "4.0.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/cidr-regex": { + "version": "4.1.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "ip-regex": "^5.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/clean-stack": { + "version": "2.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/cli-columns": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/cmd-shim": { + "version": "6.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/npm/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/common-ancestor-path": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/cross-spawn": { + "version": "7.0.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cssesc": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/debug": { + "version": "4.3.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/npm/node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/diff": { + "version": "5.2.0", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/npm/node_modules/eastasianwidth": { + "version": "0.2.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/encoding": { + "version": "0.1.13", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/npm/node_modules/env-paths": { + "version": "2.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/err-code": { + "version": "2.0.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/exponential-backoff": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/npm/node_modules/fastest-levenshtein": { + "version": "1.0.16", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/npm/node_modules/foreground-child": { + "version": "3.2.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/fs-minipass": { + "version": "3.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/glob": { + "version": "10.4.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/graceful-fs": { + "version": "4.2.11", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/hosted-git-info": { + "version": "7.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/http-cache-semantics": { + "version": "4.1.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause" + }, + "node_modules/npm/node_modules/http-proxy-agent": { + "version": "7.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/https-proxy-agent": { + "version": "7.0.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/iconv-lite": { + "version": "0.6.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/ignore-walk": { + "version": "6.0.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/npm/node_modules/indent-string": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ini": { + "version": "4.1.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/init-package-json": { + "version": "6.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/package-json": "^5.0.0", + "npm-package-arg": "^11.0.0", + "promzard": "^1.0.0", + "read": "^3.0.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/ip-address": { + "version": "9.0.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/npm/node_modules/ip-regex": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/is-cidr": { + "version": "5.1.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "cidr-regex": "^4.1.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/is-lambda": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/jackspeak": { + "version": "3.4.0", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/npm/node_modules/jsbn": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/json-parse-even-better-errors": { + "version": "3.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/json-stringify-nice": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/jsonparse": { + "version": "1.3.1", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff": { + "version": "6.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff-apply": { + "version": "5.5.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/libnpmaccess": { + "version": "8.0.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-package-arg": "^11.0.2", + "npm-registry-fetch": "^17.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmdiff": { + "version": "6.1.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^7.5.4", + "@npmcli/installed-package-contents": "^2.1.0", + "binary-extensions": "^2.3.0", + "diff": "^5.1.0", + "minimatch": "^9.0.4", + "npm-package-arg": "^11.0.2", + "pacote": "^18.0.6", + "tar": "^6.2.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmexec": { + "version": "8.1.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^7.5.4", + "@npmcli/run-script": "^8.1.0", + "ci-info": "^4.0.0", + "npm-package-arg": "^11.0.2", + "pacote": "^18.0.6", + "proc-log": "^4.2.0", + "read": "^3.0.1", + "read-package-json-fast": "^3.0.2", + "semver": "^7.3.7", + "walk-up-path": "^3.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmfund": { + "version": "5.0.12", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^7.5.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmhook": { + "version": "10.0.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^17.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmorg": { + "version": "6.0.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^17.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmpack": { + "version": "7.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^7.5.4", + "@npmcli/run-script": "^8.1.0", + "npm-package-arg": "^11.0.2", + "pacote": "^18.0.6" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmpublish": { + "version": "9.0.9", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "ci-info": "^4.0.0", + "normalize-package-data": "^6.0.1", + "npm-package-arg": "^11.0.2", + "npm-registry-fetch": "^17.0.1", + "proc-log": "^4.2.0", + "semver": "^7.3.7", + "sigstore": "^2.2.0", + "ssri": "^10.0.6" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmsearch": { + "version": "7.0.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^17.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmteam": { + "version": "6.0.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^17.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmversion": { + "version": "6.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^5.0.7", + "@npmcli/run-script": "^8.1.0", + "json-parse-even-better-errors": "^3.0.2", + "proc-log": "^4.2.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/lru-cache": { + "version": "10.2.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/npm/node_modules/make-fetch-happen": { + "version": "13.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^2.0.0", + "cacache": "^18.0.0", + "http-cache-semantics": "^4.1.1", + "is-lambda": "^1.0.1", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/minimatch": { + "version": "9.0.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/minipass": { + "version": "7.1.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-collect": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-fetch": { + "version": "3.0.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/npm/node_modules/minipass-flush": { + "version": "1.0.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline": { + "version": "1.2.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized": { + "version": "1.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minizlib": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/mkdirp": { + "version": "1.0.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/mute-stream": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/negotiator": { + "version": "0.6.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm/node_modules/node-gyp": { + "version": "10.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^10.3.10", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^13.0.0", + "nopt": "^7.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^4.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/proc-log": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/nopt": { + "version": "7.2.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/normalize-package-data": { + "version": "6.0.2", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-audit-report": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-bundled": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-install-checks": { + "version": "6.3.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-normalize-package-bin": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-package-arg": { + "version": "11.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^7.0.0", + "proc-log": "^4.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-packlist": { + "version": "8.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "ignore-walk": "^6.0.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-pick-manifest": { + "version": "9.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "npm-package-arg": "^11.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-profile": { + "version": "10.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^17.0.1", + "proc-log": "^4.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-registry-fetch": { + "version": "17.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/redact": "^2.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^13.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minizlib": "^2.1.2", + "npm-package-arg": "^11.0.0", + "proc-log": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-user-validate": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/p-map": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/package-json-from-dist": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/npm/node_modules/pacote": { + "version": "18.0.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^5.0.0", + "@npmcli/installed-package-contents": "^2.0.1", + "@npmcli/package-json": "^5.1.0", + "@npmcli/promise-spawn": "^7.0.0", + "@npmcli/run-script": "^8.0.0", + "cacache": "^18.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^11.0.0", + "npm-packlist": "^8.0.0", + "npm-pick-manifest": "^9.0.0", + "npm-registry-fetch": "^17.0.0", + "proc-log": "^4.0.0", + "promise-retry": "^2.0.1", + "sigstore": "^2.2.0", + "ssri": "^10.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/parse-conflict-json": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^3.0.0", + "just-diff": "^6.0.0", + "just-diff-apply": "^5.2.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/path-scurry": { + "version": "1.11.1", + "dev": true, + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/postcss-selector-parser": { + "version": "6.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/proc-log": { + "version": "4.2.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/proggy": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/promise-all-reject-late": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-call-limit": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-inflight": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/promise-retry": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/promzard": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "read": "^3.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/qrcode-terminal": { + "version": "0.12.0", + "dev": true, + "inBundle": true, + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" + } + }, + "node_modules/npm/node_modules/read": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "mute-stream": "^1.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/read-cmd-shim": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/read-package-json-fast": { + "version": "3.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/retry": { + "version": "0.12.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm/node_modules/safer-buffer": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true + }, + "node_modules/npm/node_modules/semver": { + "version": "7.6.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/signal-exit": { + "version": "4.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/sigstore": { + "version": "2.3.1", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "@sigstore/sign": "^2.3.2", + "@sigstore/tuf": "^2.3.4", + "@sigstore/verify": "^1.2.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/smart-buffer": { + "version": "4.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks": { + "version": "2.8.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks-proxy-agent": { + "version": "8.0.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.1", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/spdx-correct": { + "version": "3.2.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-correct/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-exceptions": { + "version": "2.5.0", + "dev": true, + "inBundle": true, + "license": "CC-BY-3.0" + }, + "node_modules/npm/node_modules/spdx-expression-parse": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-license-ids": { + "version": "3.0.18", + "dev": true, + "inBundle": true, + "license": "CC0-1.0" + }, + "node_modules/npm/node_modules/sprintf-js": { + "version": "1.1.3", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause" + }, + "node_modules/npm/node_modules/ssri": { + "version": "10.0.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/supports-color": { + "version": "9.4.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/npm/node_modules/tar": { + "version": "6.2.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass": { + "version": "2.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/text-table": { + "version": "0.2.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/tiny-relative-date": { + "version": "1.3.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/treeverse": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/tuf-js": { + "version": "2.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/models": "2.0.1", + "debug": "^4.3.4", + "make-fetch-happen": "^13.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/unique-filename": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/unique-slug": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/util-deprecate": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/validate-npm-package-license": { + "version": "3.0.4", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-name": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/walk-up-path": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/which": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/which/node_modules/isexe": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/npm/node_modules/wrap-ansi": { + "version": "8.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/write-file-atomic": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-each-series": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-3.0.0.tgz", + "integrity": "sha512-lastgtAdoH9YaLyDa5i5z64q+kzOcQHsQ5SsZJD3q0VEyI8mq872S3geuNbRUQLVAE9siMfgKrpj7MloKFHruw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-filter": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-4.1.0.tgz", + "integrity": "sha512-37/tPdZ3oJwHaS3gNJdenCDB3Tz26i9sjhnguBtvN0vYlRIiDNnvTWkuh+0hETV9rLPdJ3rlL3yVOYPIAnM8rw==", + "dev": true, + "dependencies": { + "p-map": "^7.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-is-promise": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", + "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-map": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.2.tgz", + "integrity": "sha512-z4cYYMMdKHzw4O5UkWJImbZynVIo0lSGTXc7bzB1e/rrDqkgGUNysK/o4bTr+0+xKvvLoTyGqYC4Fgljy9qe1Q==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-reduce": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-3.0.0.tgz", + "integrity": "sha512-xsrIUgI0Kn6iyDYm9StOpOeK29XM1aboGji26+QEortiFST1hGZaUQOLhtEbqHErPpGW/aSz6allwK2qcptp0Q==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "dev": true + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dev": true, + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-conf": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", + "integrity": "sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==", + "dev": true, + "dependencies": { + "find-up": "^2.0.0", + "load-json-file": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pretty-ms": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.0.0.tgz", + "integrity": "sha512-E9e9HJ9R9NasGOgPaPE8VMeiPKAyWR5jcFpNnwIejslIhWqdqOrb2wShBsncMPUb+BcCd2OPYfh7p2W6oemTng==", + "dev": true, + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/read-package-up": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-11.0.0.tgz", + "integrity": "sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ==", + "dev": true, + "dependencies": { + "find-up-simple": "^1.0.0", + "read-pkg": "^9.0.0", + "type-fest": "^4.6.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", + "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.3", + "normalize-package-data": "^6.0.0", + "parse-json": "^8.0.0", + "type-fest": "^4.6.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/parse-json": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.1.0.tgz", + "integrity": "sha512-rum1bPifK5SSar35Z6EKZuYPJx85pkNaFrxBK3mwdfSJ1/WKbYrjoW/zTPSjRRamfmVX1ACBIdFAO0VRErW/EA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "index-to-position": "^0.1.2", + "type-fest": "^4.7.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/registry-auth-token": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.2.tgz", + "integrity": "sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==", + "dev": true, + "dependencies": { + "@pnpm/npm-conf": "^2.1.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/semantic-release": { + "version": "24.0.0", + "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-24.0.0.tgz", + "integrity": "sha512-v46CRPw+9eI3ZuYGF2oAjqPqsfbnfFTwLBgQsv/lch4goD09ytwOTESMN4QIrx/wPLxUGey60/NMx+ANQtWRsA==", + "dev": true, + "dependencies": { + "@semantic-release/commit-analyzer": "^13.0.0-beta.1", + "@semantic-release/error": "^4.0.0", + "@semantic-release/github": "^10.0.0", + "@semantic-release/npm": "^12.0.0", + "@semantic-release/release-notes-generator": "^14.0.0-beta.1", + "aggregate-error": "^5.0.0", + "cosmiconfig": "^9.0.0", + "debug": "^4.0.0", + "env-ci": "^11.0.0", + "execa": "^9.0.0", + "figures": "^6.0.0", + "find-versions": "^6.0.0", + "get-stream": "^6.0.0", + "git-log-parser": "^1.2.0", + "hook-std": "^3.0.0", + "hosted-git-info": "^7.0.0", + "import-from-esm": "^1.3.1", + "lodash-es": "^4.17.21", + "marked": "^12.0.0", + "marked-terminal": "^7.0.0", + "micromatch": "^4.0.2", + "p-each-series": "^3.0.0", + "p-reduce": "^3.0.0", + "read-package-up": "^11.0.0", + "resolve-from": "^5.0.0", + "semver": "^7.3.2", + "semver-diff": "^4.0.0", + "signale": "^1.2.1", + "yargs": "^17.5.1" + }, + "bin": { + "semantic-release": "bin/semantic-release.js" + }, + "engines": { + "node": ">=20.8.1" + } + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", + "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", + "dev": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semver-regex": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-4.0.5.tgz", + "integrity": "sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/signale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/signale/-/signale-1.4.0.tgz", + "integrity": "sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==", + "dev": true, + "dependencies": { + "chalk": "^2.3.2", + "figures": "^2.0.0", + "pkg-conf": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/signale/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/signale/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/signale/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/signale/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/signale/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/signale/node_modules/figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/signale/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/signale/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/skin-tone": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", + "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==", + "dev": true, + "dependencies": { + "unicode-emoji-modifier-base": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spawn-error-forwarder": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz", + "integrity": "sha512-gRjMgK5uFjbCvdibeGJuy3I5OYz6VLoVdsOJdA6wV0WlfQVLFueoqMxwwYD9RODdgb6oUIvlRlsyFSiQkMKu0g==", + "dev": true + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.18", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.18.tgz", + "integrity": "sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==", + "dev": true + }, + "node_modules/split2": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-1.0.0.tgz", + "integrity": "sha512-NKywug4u4pX/AZBB1FCPzZ6/7O+Xhz1qMVbzTvvKvikjO99oPN87SkK08mEY9P63/5lWjK+wgOOgApnTg5r6qg==", + "dev": true, + "dependencies": { + "through2": "~2.0.0" + } + }, + "node_modules/stream-combiner2": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "integrity": "sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==", + "dev": true, + "dependencies": { + "duplexer2": "~0.1.0", + "readable-stream": "^2.0.2" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-final-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/super-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/super-regex/-/super-regex-1.0.0.tgz", + "integrity": "sha512-CY8u7DtbvucKuquCmOFEKhr9Besln7n9uN8eFbwcoGYWXOMW07u2o8njWaiXt11ylS3qoGF55pILjRmPlbodyg==", + "dev": true, + "dependencies": { + "function-timeout": "^1.0.1", + "time-span": "^5.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.0.0.tgz", + "integrity": "sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + } + }, + "node_modules/temp-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", + "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", + "dev": true, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/tempy": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-3.1.0.tgz", + "integrity": "sha512-7jDLIdD2Zp0bDe5r3D2qtkd1QOCacylBuL7oa4udvN6v2pqr4+LcCr67C8DR1zkpaZ8XosF5m1yQSabKAW6f2g==", + "dev": true, + "dependencies": { + "is-stream": "^3.0.0", + "temp-dir": "^3.0.0", + "type-fest": "^2.12.2", + "unique-string": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/text-extensions": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz", + "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/time-span": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/time-span/-/time-span-5.1.0.tgz", + "integrity": "sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==", + "dev": true, + "dependencies": { + "convert-hrtime": "^5.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/traverse": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.8.tgz", + "integrity": "sha512-aXJDbk6SnumuaZSANd21XAo15ucCDE38H4fkqiGsc3MhCK+wOlZvLP9cB/TvpHT0mOyWgC4Z8EwRlzqYSUzdsA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/type-fest": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.23.0.tgz", + "integrity": "sha512-ZiBujro2ohr5+Z/hZWHESLz3g08BBdrdLMieYFULJO+tWc437sn8kQsWLJoZErY8alNhxre9K4p3GURAG11n+w==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "dev": true, + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uglify-js": { + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.0.tgz", + "integrity": "sha512-wNKHUY2hYYkf6oSFfhwwiHo4WCHzHmzcXsqXYTN9ja3iApYIFbb2U6ics9hBcYLHcYGQoAlwnZlTrf3oF+BL/Q==", + "dev": true, + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/unicode-emoji-modifier-base": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz", + "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unique-string": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", + "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", + "dev": true, + "dependencies": { + "crypto-random-string": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universal-user-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz", + "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==", + "dev": true + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/url-join": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz", + "integrity": "sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", + "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.1.tgz", + "integrity": "sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..b7913f8f --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "ssi-agent", + "version": "0.1.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/impierce/ssi-agent.git" + }, + "author": "", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/impierce/ssi-agent/issues" + }, + "homepage": "https://impierce.com", + "devDependencies": { + "@commitlint/cli": "^19.3.0", + "@commitlint/config-angular": "^19.3.0", + "semantic-release": "^24.0.0" + }, + "type": "module" +} From 966ce9703ff60927b487db44dd0012ac72dec492 Mon Sep 17 00:00:00 2001 From: Daniel Mader Date: Wed, 24 Jul 2024 09:36:30 +0200 Subject: [PATCH 31/37] feat: check PR title with `commitlint` --- .github/workflows/format-lint-test.yaml | 7 +++++++ commitlint.config.js | 4 +++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/format-lint-test.yaml b/.github/workflows/format-lint-test.yaml index 5f48f5f9..fe1532fc 100644 --- a/.github/workflows/format-lint-test.yaml +++ b/.github/workflows/format-lint-test.yaml @@ -21,5 +21,12 @@ jobs: - name: Lint run: cargo clippy --all-targets --all-features -- -D warnings + - name: Check PR title + env: + # Security: We mitigate script injections by using an intermediate environment variable + # https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable + TITLE: ${{ github.event.pull_request.title }} + run: echo $TITLE | npx commitlint + - name: Test run: cargo test --workspace diff --git a/commitlint.config.js b/commitlint.config.js index 5d2cf86a..ba5c66fb 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -1 +1,3 @@ -export default { extends: ["@commitlint/config-angular"] }; +export default { + extends: ["@commitlint/config-angular"], +}; From f8581c8339ceeb91165bbf3b43b32b08ee106f75 Mon Sep 17 00:00:00 2001 From: Daniel Mader Date: Wed, 24 Jul 2024 09:42:38 +0200 Subject: [PATCH 32/37] refactor: move `commitlint` to separate workflow --- .github/workflows/format-lint-test.yaml | 7 ------- .github/workflows/lint-pr-title.yaml | 23 +++++++++++++++++++++++ 2 files changed, 23 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/lint-pr-title.yaml diff --git a/.github/workflows/format-lint-test.yaml b/.github/workflows/format-lint-test.yaml index fe1532fc..5f48f5f9 100644 --- a/.github/workflows/format-lint-test.yaml +++ b/.github/workflows/format-lint-test.yaml @@ -21,12 +21,5 @@ jobs: - name: Lint run: cargo clippy --all-targets --all-features -- -D warnings - - name: Check PR title - env: - # Security: We mitigate script injections by using an intermediate environment variable - # https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable - TITLE: ${{ github.event.pull_request.title }} - run: echo $TITLE | npx commitlint - - name: Test run: cargo test --workspace diff --git a/.github/workflows/lint-pr-title.yaml b/.github/workflows/lint-pr-title.yaml new file mode 100644 index 00000000..308d3293 --- /dev/null +++ b/.github/workflows/lint-pr-title.yaml @@ -0,0 +1,23 @@ +name: Lint PR title + +on: + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: "lts/*" + + - run: npm ci + + - run: echo $TITLE | npx commitlint + env: + # Security: we mitigate script injections by using an intermediate environment variable + # https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable + TITLE: ${{ github.event.pull_request.title }} From 056d18c7e851615ccbce1b837ec7d59051dfc8b4 Mon Sep 17 00:00:00 2001 From: Daniel Mader Date: Wed, 24 Jul 2024 10:58:30 +0200 Subject: [PATCH 33/37] refactor: only check PR title for `dev` --- .github/workflows/lint-pr-title.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/lint-pr-title.yaml b/.github/workflows/lint-pr-title.yaml index 308d3293..0ced8f9c 100644 --- a/.github/workflows/lint-pr-title.yaml +++ b/.github/workflows/lint-pr-title.yaml @@ -2,6 +2,8 @@ name: Lint PR title on: pull_request: + branches: ["dev"] + types: [opened, synchronize, reopened, edited] jobs: build: From 3d04109a577878f330cad08dfcdbb8519e96bbc6 Mon Sep 17 00:00:00 2001 From: Daniel Mader Date: Wed, 24 Jul 2024 11:58:48 +0200 Subject: [PATCH 34/37] chore: disable `types` (temporarily) --- .github/workflows/lint-pr-title.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/lint-pr-title.yaml b/.github/workflows/lint-pr-title.yaml index 0ced8f9c..7a8ce530 100644 --- a/.github/workflows/lint-pr-title.yaml +++ b/.github/workflows/lint-pr-title.yaml @@ -3,7 +3,8 @@ name: Lint PR title on: pull_request: branches: ["dev"] - types: [opened, synchronize, reopened, edited] + # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request + # types: [opened, synchronize, reopened, edited] jobs: build: From a07f38a729ff3c34d75dad31b5191fc18468aa42 Mon Sep 17 00:00:00 2001 From: Daniel Mader Date: Wed, 24 Jul 2024 12:08:15 +0200 Subject: [PATCH 35/37] ci: add default `semantic-release` workflow (for Node projects) --- .github/workflows/release.yaml | 39 ++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/workflows/release.yaml diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 00000000..468ebe63 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,39 @@ +# https://github.com/semantic-release/semantic-release/blob/master/docs/recipes/ci-configurations/github-actions.md +name: Release + +on: + workflow_dispatch: +# push: +# branches: +# - main + +permissions: + contents: read # for checkout + +jobs: + release: + name: Release + runs-on: ubuntu-latest + permissions: + contents: write # to be able to publish a GitHub release + issues: write # to be able to comment on released issues + pull-requests: write # to be able to comment on released pull requests + id-token: write # to enable use of OIDC for npm provenance + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: "lts/*" + - name: Install dependencies + run: npm clean-install + - name: Verify the integrity of provenance attestations and registry signatures for installed dependencies + run: npm audit signatures + - name: Release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + run: npx semantic-release From 54a9bd321ae13f0608c1aa7e4ae2b56f2674ac1b Mon Sep 17 00:00:00 2001 From: Daniel Mader Date: Wed, 24 Jul 2024 12:50:39 +0200 Subject: [PATCH 36/37] docs: add reference to "Twelve-Factor App" --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index bd4d7eb9..3a37bfe3 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,10 @@ [![semantic-release: angular](https://img.shields.io/badge/semantic--release-angular-e10079?logo=semantic-release)](https://github.com/semantic-release/semantic-release) [![License](https://img.shields.io/github/license/impierce/ssi-agent.svg)](https://github.com/impierce/ssi-agent/blob/HEAD/LICENSE) + + +[![twelve-factor-app](https://img.shields.io/badge/factors-twelve-blue)](https://12factor.net) + --- ## API specification From af1ce933dac07742a3474663980fee0481c29e58 Mon Sep 17 00:00:00 2001 From: Daniel Mader Date: Wed, 24 Jul 2024 12:58:38 +0200 Subject: [PATCH 37/37] docs: add badge for DockerHub pulls --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3a37bfe3..ea5bc737 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # SSI Agent [![semantic-release: angular](https://img.shields.io/badge/semantic--release-angular-e10079?logo=semantic-release)](https://github.com/semantic-release/semantic-release) -[![License](https://img.shields.io/github/license/impierce/ssi-agent.svg)](https://github.com/impierce/ssi-agent/blob/HEAD/LICENSE) +[![GitHub License](https://img.shields.io/github/license/impierce/ssi-agent)](https://github.com/impierce/ssi-agent/blob/HEAD/LICENSE) +[![Docker Pulls](https://img.shields.io/docker/pulls/impiercetechnologies/ssi-agent)](https://hub.docker.com/r/impiercetechnologies/ssi-agent)