From 61f5db432b022c10dfabb95f4c523d25e9bbd8e1 Mon Sep 17 00:00:00 2001 From: luisgomez29 Date: Thu, 6 Aug 2026 09:15:49 -0500 Subject: [PATCH 1/3] feat: add configurable event suffix for Kafka consumers --- .github/workflows/docs.yml | 6 +- .github/workflows/main.yml | 4 +- .github/workflows/release.yml | 2 +- .github/workflows/updater.yml | 4 +- async/async-commons/async-commons.gradle | 2 +- async/async-kafka/async-kafka.gradle | 2 +- .../listeners/ApplicationEventListener.java | 4 +- .../ApplicationEventListenerTest.java | 31 +- async/async-rabbit/async-rabbit.gradle | 2 +- .../cloudevents-json-jackson.gradle | 4 +- .../configuration_properties/1-rabbitmq.md | 94 +- .../configuration_properties/2-kafka.md | 198 ++- docs/package-lock.json | 1385 +++++++++-------- docs/package.json | 14 +- domain/domain-events/domain-events-api.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 4 +- gradlew.bat | 4 +- main.gradle | 2 +- .../async-kafka-sender-client.gradle | 2 +- .../eda-async-receiver-sample.gradle | 2 +- .../eda-async-sender-client-domain-a.gradle | 2 +- .../eda-async-sender-client.gradle | 2 +- .../GenericAsyncPropsDomainProperties.java | 31 +- ...GenericAsyncPropsDomainPropertiesTest.java | 34 + .../async/kafka/KafkaBrokerProvider.java | 2 +- .../kafka/config/props/AsyncKafkaProps.java | 15 + .../config/props/AsyncKafkaPropsDomain.java | 4 +- .../AsyncKafkaPropsDomainProperties.java | 5 + .../async/kafka/config/props/DomainProps.java | 10 +- .../async/kafka/config/props/EventsProps.java | 23 + .../config/spring/KafkaPropertiesBase.java | 59 +- .../async/kafka/KafkaBrokerProviderTest.java | 39 +- .../AsyncKafkaPropsCustomizerMergeTest.java | 60 + .../AsyncKafkaPropsDomainPropertiesTest.java | 55 + .../AsyncKafkaPropsEventsGroupIdTest.java | 53 + .../props/AsyncKafkaPropsGroupIdTest.java | 86 + .../AsyncRabbitPropsDomainProperties.java | 5 + .../AsyncRabbitPropsDomainPropertiesTest.java | 34 + 39 files changed, 1459 insertions(+), 830 deletions(-) create mode 100644 starters/async-commons-starter/src/test/java/org/reactivecommons/async/starter/props/GenericAsyncPropsDomainPropertiesTest.java create mode 100644 starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/EventsProps.java create mode 100644 starters/async-kafka-starter/src/test/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsCustomizerMergeTest.java create mode 100644 starters/async-kafka-starter/src/test/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsDomainPropertiesTest.java create mode 100644 starters/async-kafka-starter/src/test/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsEventsGroupIdTest.java create mode 100644 starters/async-kafka-starter/src/test/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsGroupIdTest.java create mode 100644 starters/async-rabbit-starter/src/test/java/org/reactivecommons/async/rabbit/config/props/AsyncRabbitPropsDomainPropertiesTest.java diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 39c74074..32681138 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -19,9 +19,9 @@ jobs: url: ${{ steps.deployment.outputs.page_url }} steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@v7 with: node-version-file: 'docs/.nvmrc' - name: Install dependencies @@ -31,7 +31,7 @@ jobs: run: npm run build working-directory: docs - name: Upload artifact - uses: actions/upload-pages-artifact@v4 + uses: actions/upload-pages-artifact@v5 with: path: 'docs/build/' - name: Deploy to GitHub Pages diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 386f2772..8631e960 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,7 +15,7 @@ jobs: pull-requests: write runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Verify Conventional Commits uses: amannn/action-semantic-pull-request@v5 if: github.event_name == 'pull_request' || github.event_name == 'pull_request_target' @@ -23,7 +23,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Set up NodeJS if: github.ref == 'refs/heads/master' - uses: actions/setup-node@v6 + uses: actions/setup-node@v7 with: node-version-file: '.nvmrc' - name: Set up Semantic Release diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ee526344..ea5caa9d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,7 +6,7 @@ jobs: release: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Set env run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/v}" >> $GITHUB_ENV - name: Upgrade Gradle.properties diff --git a/.github/workflows/updater.yml b/.github/workflows/updater.yml index 2125af8d..1a19ff47 100644 --- a/.github/workflows/updater.yml +++ b/.github/workflows/updater.yml @@ -7,7 +7,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Set up JDK 25 uses: actions/setup-java@v5 with: @@ -15,6 +15,8 @@ jobs: java-version: 25 - name: Check for updates run: ./gradlew internalTask --action UPDATE_DEPENDENCIES + - name: Gradle Wrapper + run: ./gradlew wrapper - name: Check for changes id: git_changes run: | diff --git a/async/async-commons/async-commons.gradle b/async/async-commons/async-commons.gradle index 9bb6d0a2..a57cc7f4 100644 --- a/async/async-commons/async-commons.gradle +++ b/async/async-commons/async-commons.gradle @@ -11,7 +11,7 @@ dependencies { compileOnly 'io.projectreactor:reactor-core' api 'tools.jackson.core:jackson-databind' implementation 'commons-io:commons-io:2.22.0' - implementation 'io.cloudevents:cloudevents-core:4.1.1' + implementation 'io.cloudevents:cloudevents-core:5.0.0' testImplementation 'io.projectreactor:reactor-test' } \ No newline at end of file diff --git a/async/async-kafka/async-kafka.gradle b/async/async-kafka/async-kafka.gradle index eee44024..74be7159 100644 --- a/async/async-kafka/async-kafka.gradle +++ b/async/async-kafka/async-kafka.gradle @@ -9,5 +9,5 @@ dependencies { api project(':async-commons') api project(':cloudevents-json-jackson') api 'io.projectreactor.kafka:reactor-kafka:1.3.25' - implementation 'io.cloudevents:cloudevents-core:4.1.1' + implementation 'io.cloudevents:cloudevents-core:5.0.0' } \ No newline at end of file diff --git a/async/async-kafka/src/main/java/org/reactivecommons/async/kafka/listeners/ApplicationEventListener.java b/async/async-kafka/src/main/java/org/reactivecommons/async/kafka/listeners/ApplicationEventListener.java index 11ca9548..4c185818 100644 --- a/async/async-kafka/src/main/java/org/reactivecommons/async/kafka/listeners/ApplicationEventListener.java +++ b/async/async-kafka/src/main/java/org/reactivecommons/async/kafka/listeners/ApplicationEventListener.java @@ -32,9 +32,9 @@ public ApplicationEventListener(ReactiveMessageListener receiver, int retryDelay, DiscardNotifier discardNotifier, CustomReporter errorReporter, - String appName) { + String groupId) { super(receiver, withDLQRetry, createTopology, maxRetries, retryDelay, discardNotifier, - "event", errorReporter, appName + "-events", resolver.getEventNames()); + "event", errorReporter, groupId, resolver.getEventNames()); this.resolver = resolver; this.messageConverter = messageConverter; } diff --git a/async/async-kafka/src/test/java/org/reactivecommons/async/kafka/listeners/ApplicationEventListenerTest.java b/async/async-kafka/src/test/java/org/reactivecommons/async/kafka/listeners/ApplicationEventListenerTest.java index 2f014db2..c6de4d9b 100644 --- a/async/async-kafka/src/test/java/org/reactivecommons/async/kafka/listeners/ApplicationEventListenerTest.java +++ b/async/async-kafka/src/test/java/org/reactivecommons/async/kafka/listeners/ApplicationEventListenerTest.java @@ -15,10 +15,13 @@ import org.reactivecommons.async.commons.communications.Message; import org.reactivecommons.async.commons.converters.MessageConverter; import org.reactivecommons.async.kafka.communications.ReactiveMessageListener; +import org.reactivecommons.async.kafka.communications.topology.TopologyCreator; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -52,7 +55,7 @@ void setup() { 1000, null, null, - "testApp" + "testApp-events" ); } @@ -96,4 +99,30 @@ void shouldHandleRawMessageSuccessfullyWhenCloudEvent() { verify(resolver, times(1)).getEventListener(anyString()); verify(messageConverter, times(1)).readCloudEvent(any(Message.class)); } + + @Test + void shouldUseTheGivenGroupId() { + ApplicationEventListener listener = buildListener("dummy.consumer-group"); + when(receiver.getMaxConcurrency()).thenReturn(1); + when(receiver.listen(anyString(), any())).thenReturn(Flux.never()); + + listener.startListener(mock(TopologyCreator.class)); + + verify(receiver, times(1)).listen(eq("dummy.consumer-group"), any()); + } + + private ApplicationEventListener buildListener(String groupId) { + return new ApplicationEventListener( + receiver, + resolver, + messageConverter, + true, + false, + 3, + 1000, + null, + null, + groupId + ); + } } \ No newline at end of file diff --git a/async/async-rabbit/async-rabbit.gradle b/async/async-rabbit/async-rabbit.gradle index ea90b042..cb695d8f 100644 --- a/async/async-rabbit/async-rabbit.gradle +++ b/async/async-rabbit/async-rabbit.gradle @@ -15,6 +15,6 @@ dependencies { api 'com.rabbitmq:amqp-client' api 'tools.jackson.core:jackson-databind' - implementation 'io.cloudevents:cloudevents-core:4.1.1' + implementation 'io.cloudevents:cloudevents-core:5.0.0' testImplementation 'io.projectreactor:reactor-test' } \ No newline at end of file diff --git a/async/cloudevents-json-jackson/cloudevents-json-jackson.gradle b/async/cloudevents-json-jackson/cloudevents-json-jackson.gradle index 2c9012af..74c4d2f6 100644 --- a/async/cloudevents-json-jackson/cloudevents-json-jackson.gradle +++ b/async/cloudevents-json-jackson/cloudevents-json-jackson.gradle @@ -5,6 +5,6 @@ ext { dependencies { api 'tools.jackson.core:jackson-databind' - implementation 'io.cloudevents:cloudevents-api:4.1.1' - implementation 'io.cloudevents:cloudevents-core:4.1.1' + implementation 'io.cloudevents:cloudevents-api:5.0.0' + implementation 'io.cloudevents:cloudevents-core:5.0.0' } \ No newline at end of file diff --git a/docs/docs/reactive-commons/configuration_properties/1-rabbitmq.md b/docs/docs/reactive-commons/configuration_properties/1-rabbitmq.md index d4a136ad..a82df0a2 100644 --- a/docs/docs/reactive-commons/configuration_properties/1-rabbitmq.md +++ b/docs/docs/reactive-commons/configuration_properties/1-rabbitmq.md @@ -4,16 +4,16 @@ sidebar_position: 1 # RabbitMQ Configuration -This page describes how to configure RabbitMQ connection and messaging properties for each **domain** in -Reactive Commons. A domain represents an independent connection to a RabbitMQ broker. Your application can work -with a single domain (one broker) or multiple domains (several independent brokers), each with its own properties. -See [Communication Scenarios](/reactive-commons-java/docs/category/communication-scenarios) for guidance on when -to use multiple domains. +This page describes how to configure RabbitMQ connection and messaging properties for each **domain** in Reactive +Commons. A domain represents an independent connection to a RabbitMQ broker. Your application can work with a single +domain (one broker) or multiple domains (several independent brokers), each with its own properties. +See [Communication Scenarios](/reactive-commons-java/docs/category/communication-scenarios) for guidance on when to use +multiple domains. All available properties are defined in the [AsyncProps](https://github.com/reactive-commons/reactive-commons-java/blob/master/starters/async-rabbit-starter/src/main/java/org/reactivecommons/async/rabbit/config/props/AsyncProps.java) -class. There are two ways to provide these values via `application.yaml` or a combination of YAML and -programmatic configuration, as described in the [Configuration approaches](#configuration-approaches) section below. +class. There are two ways to provide these values via `application.yaml` or a combination of YAML and programmatic +configuration, as described in the [Configuration approaches](#configuration-approaches) section below. ```yaml title="application.yaml" app: @@ -79,12 +79,12 @@ properties do not depend on runtime values such as secrets. ### Approach 2: Hybrid YAML + `RabbitPropsCustomizer` -Use this approach when you want to define the domain structure in YAML (topology, retry settings, etc.) but need to -set some properties at runtime for example, loading connection credentials from a secrets manager. +Use this approach when you want to define the domain structure in YAML (topology, retry settings, etc.) but need to set +some properties at runtime for example, loading connection credentials from a secrets manager. Declare your domains in `application.yaml` as usual, then define a `RabbitPropsCustomizer` bean to override specific -properties after the YAML is loaded. The customizer receives the full map of configured domains and can modify -any property on any domain. +properties after the YAML is loaded. The customizer receives the full map of configured domains and can modify any +property on any domain. :::caution[YAML domains are optional] The `RabbitPropsCustomizer` can work with or without pre-existing YAML domains. If no domains are defined in your @@ -95,7 +95,7 @@ executes, otherwise an `InvalidConfigurationException` is thrown. You have two options: -**Option A: Define domains in YAML, then override with customizer** +**Option A: Define domains in YAML, then merge overrides with the customizer** Declare your domains in `application.yaml` as usual, then use the customizer to override or extend them. @@ -114,7 +114,6 @@ app: package sample; import org.reactivecommons.async.rabbit.config.RabbitProperties; -import org.reactivecommons.async.rabbit.config.props.AsyncProps; import org.reactivecommons.async.rabbit.config.props.AsyncPropsDomain; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -132,27 +131,31 @@ public class RabbitMQConfig { @Bean public AsyncPropsDomain.RabbitPropsCustomizer rabbitPropsCustomizer() { return domainProperties -> { - // Customize the "app" domain — overrides take precedence over YAML values - AsyncProps app = domainProperties.get("app"); - if (app != null) { - app.setConnectionProperties(loadFromSecret("secret-app-rabbit")); - } + // Customize the "app" domain — YAML values are kept, only these fields are overridden + domainProperties.customize("app", app -> app.setConnectionProperties( + loadFromSecret("secret-app-rabbit") + )); // Customize the "accounts" domain independently - AsyncProps accounts = domainProperties.get("accounts"); - if (accounts != null) { - accounts.setConnectionProperties(loadFromSecret("secret-accounts-rabbit")); - } + domainProperties.customize("accounts", accounts -> accounts.setConnectionProperties( + loadFromSecret("secret-accounts-rabbit") + )); }; } } ``` +:::danger[`put` replaces the domain, `customize` merges it] +`domainProperties` is a map of domains, so `domainProperties.put("app", AsyncProps.builder()...build())` +**replaces the whole domain** and every value bound from `application.yaml` for that domain is lost, going back to its +defaults. Use `customize(domain, props -> ...)` to merge, and `put(domain, props)` only when you intend to define the +whole domain programmatically. +::: + **Option B: Define all domains in the customizer (no YAML domains)** If you prefer full programmatic control, **omit the `app.async` section entirely from your `application.yaml`** and -define all domains -inside the customizer: +define all domains inside the customizer: ```java package sample; @@ -194,7 +197,11 @@ public class RabbitMQConfig { **Key rules for the hybrid approach:** - Properties set in the customizer **take precedence** over YAML values. -- YAML values not touched by the customizer are **preserved**. +- YAML values not touched by the customizer are **preserved**, as long as you use + `domainProperties.customize("domain", props -> ...)` or mutate the instance returned by + `domainProperties.get("domain")`. +- `domainProperties.put("domain", props)` **replaces** the whole domain: use it to define domains programmatically, not + to override a few properties of a domain declared in YAML. - The customizer can also **add new domains** by calling `domainProperties.put("newDomain", asyncProps)`. ## Loading properties from a secret @@ -202,13 +209,13 @@ public class RabbitMQConfig { :::danger[Deprecated] Using `AsyncPropsDomain.RabbitSecretFiller` to load secrets is **deprecated** and will be removed in a future version. Use **[Approach 2: Hybrid YAML + `RabbitPropsCustomizer`](#approach-2-hybrid-yaml--rabbitpropscustomizer)** instead, -which provides full control over all domain properties at runtime and is the recommended way to integrate with a -secrets manager. +which provides full control over all domain properties at runtime and is the recommended way to integrate with a secrets +manager. ::: The recommended way to load connection properties from a secrets manager is to use the `RabbitPropsCustomizer` (see -[Approach 2](#approach-2-hybrid-yaml--rabbitpropscustomizer)). This gives you full control over all domain properties -at runtime. The example below uses the [Secrets Manager](https://github.com/bancolombia/secrets-manager) library. +[Approach 2](#approach-2-hybrid-yaml--rabbitpropscustomizer)). This gives you full control over all domain properties at +runtime. The example below uses the [Secrets Manager](https://github.com/bancolombia/secrets-manager) library. 1. Create a `@ConfigurationProperties` record to map the secret fields: @@ -330,8 +337,8 @@ public class RabbitMQConfig { ## Customizing the connection For advanced control over the RabbitMQ connection, you can define a `ConnectionFactoryCustomizer` bean. This allows you -to configure options that are not exposed through standard properties, such as custom timeouts, SSL/TLS settings, -or automatic recovery strategies: +to configure options that are not exposed through standard properties, such as custom timeouts, SSL/TLS settings, or +automatic recovery strategies: ```java @@ -373,7 +380,7 @@ two brokers, which means the described scenarios are limited to a maximum of two ### 1. Sending messages (single domain) > In this scenario we only use annotations to enable message sending only, along with different configurations for the -`listenReplies` property: +> `listenReplies` property: | Enabled annotations | listenReplies | Broker | Connections | Channels | |----------------------------|---------------|------------|-------------|----------| @@ -383,7 +390,7 @@ two brokers, which means the described scenarios are limited to a maximum of two ### 2. Sending messages (multiple domains) > In this scenario, we only send messages to two brokers, using one or all of the annotations and configurations for the -`listenReplies` property: +> `listenReplies` property: | Enabled annotations | listenReplies | Broker | Connections | Channels | |----------------------------|---------------|-------------------|-------------|----------| @@ -527,9 +534,8 @@ app: ``` Now we configure the return handler to manage messages that could not be delivered correctly. By default, these messages -are displayed in a log. -To customize this behavior, a class that implements the `UnroutableMessageHandler` interface is created and registered -as a Spring bean: +are displayed in a log. To customize this behavior, a class that implements the `UnroutableMessageHandler` interface is +created and registered as a Spring bean: ```java package sample; @@ -574,8 +580,8 @@ To send the unrouted message to a queue, we use the `@EnableDomainEventBus` anno for [commands](/reactive-commons-java/docs/reactive-commons/sending-a-command) and [asynchronous queries](/reactive-commons-java/docs/reactive-commons/making-an-async-query), as appropriate. -It is important to ensure that the queue exists before sending the message, as it will otherwise be lost. -Therefore, it is recommended to verify or create the queue beforehand to ensure successful delivery. +It is important to ensure that the queue exists before sending the message, as it will otherwise be lost. Therefore, it +is recommended to verify or create the queue beforehand to ensure successful delivery. ```java package sample; @@ -730,8 +736,7 @@ public class RabbitMQConfig { ### PRECONDITION_FAILED - inequivalent arg 'x-dead-letter-exchange' This error occurs when there is a mismatch between the queue properties defined in your application and the properties -of the queue that already exists in the RabbitMQ broker. -It commonly happens when you try to: +of the queue that already exists in the RabbitMQ broker. It commonly happens when you try to: - Change the name of a domain. - Enable or disable DLQ (Dead Letter Queue) functionality for a queue that has already been created. @@ -748,13 +753,10 @@ of type 'longstr', class-id=50, method-id=10) **Cause:** RabbitMQ does not allow changing certain durable properties of a queue after it has been declared, such as the -`x-dead-letter-exchange` argument. -When your application starts, it tries to declare the queue with the new properties, but the broker rejects the -declaration because it conflicts -with the existing queue. +`x-dead-letter-exchange` argument. When your application starts, it tries to declare the queue with the new properties, +but the broker rejects the declaration because it conflicts with the existing queue. **Solution:** To resolve this issue, you must manually delete the conflicting queues from the RabbitMQ broker. Once the queues are -deleted, -you can restart the microservice to recreate them with the correct, updated properties. +deleted, you can restart the microservice to recreate them with the correct, updated properties. diff --git a/docs/docs/reactive-commons/configuration_properties/2-kafka.md b/docs/docs/reactive-commons/configuration_properties/2-kafka.md index c244a3ef..b85db817 100644 --- a/docs/docs/reactive-commons/configuration_properties/2-kafka.md +++ b/docs/docs/reactive-commons/configuration_properties/2-kafka.md @@ -4,16 +4,16 @@ sidebar_position: 2 # Kafka Configuration -This page describes how to configure Kafka connection and messaging properties for each **domain** in -Reactive Commons. A domain represents an independent connection to a Kafka cluster. Your application can work -with a single domain (one cluster) or multiple domains (several independent clusters), each with its own properties. -See [Communication Scenarios](/reactive-commons-java/docs/category/communication-scenarios) for guidance on when -to use multiple domains. +This page describes how to configure Kafka connection and messaging properties for each **domain** in Reactive Commons. +A domain represents an independent connection to a Kafka cluster. Your application can work with a single domain (one +cluster) or multiple domains (several independent clusters), each with its own properties. +See [Communication Scenarios](/reactive-commons-java/docs/category/communication-scenarios) for guidance on when to use +multiple domains. All available properties are defined in the [AsyncKafkaProps](https://github.com/reactive-commons/reactive-commons-java/blob/master/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaProps.java) -class. There are two ways to provide these values via `application.yaml` or a combination of YAML and -programmatic configuration, as described in the [Configuration approaches](#configuration-approaches) section below. +class. There are two ways to provide these values via `application.yaml` or a combination of YAML and programmatic +configuration, as described in the [Configuration approaches](#configuration-approaches) section below. ```yaml title="application.yaml" reactive: @@ -30,43 +30,148 @@ reactive: brokerType: "kafka" # please don't change this value domain: ignoreThisListener: false # Allows you to disable event listener for this specific domain + events: + eventsSuffix: events # consumer group suffix used only when consumer.group-id is not set, group id will be like ${spring.application.name}-${eventsSuffix} connectionProperties: # you can override the connection properties of each domain bootstrap-servers: localhost:9092 + consumer: + group-id: # optional. When set, it is used as the consumer group id of the domain events listener, exactly as provided # Another domain can be configured with same properties structure that app accounts: # this is a second domain name and can have another independent setup connectionProperties: # you can override the connection properties of each domain bootstrap-servers: localhost:9093 ``` +## Connection properties + +`connectionProperties` of each domain is a +[KafkaProperties](https://github.com/reactive-commons/reactive-commons-java/blob/master/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/spring/KafkaPropertiesBase.java) +instance, which has the same structure as the well known `spring.kafka.*` properties of Spring Boot. It can be defined +**in YAML, programmatically, or both** — YAML is bound first and a `KafkaPropsCustomizer` can override it afterwards +(see [Approach 2](#approach-2-hybrid-yaml--kafkapropscustomizer)). + +The class exposes typed sections (`consumer`, `producer`, `admin`, `ssl`, `security`) plus free-form `properties` +maps for any Kafka client property that is not modeled as a field. Relaxed binding applies, so +`bootstrapServers`, `bootstrap-servers` and `BOOTSTRAP_SERVERS` are equivalent in YAML. + +```yaml title="application.yaml" +reactive: + commons: + kafka: + app: + connectionProperties: + bootstrap-servers: broker1:9094,broker2:9094 # list, can also be written as a YAML sequence + consumer: + group-id: dummy.consumer-group # consumer group used by the domain events listener + auto-offset-reset: earliest + max-poll-records: 250 + security: + protocol: SASL_SSL + ssl: + trust-store-type: PEM + trust-store-location: file:/etc/certs/kafka.pem + properties: # raw Kafka client properties, common to consumer, producer and admin + sasl.mechanism: SCRAM-SHA-512 + sasl.jaas.config: org.apache.kafka.common.security.scram.ScramLoginModule required username="user" password="pass"; +``` + +Equivalent programmatic definition: + +```java +var propertiesApp = new KafkaProperties(); +propertiesApp. + +setBootstrapServers(List.of("broker1:9094", "broker2:9094")); + propertiesApp. + +getConsumer(). + +setGroupId("dummy.consumer-group"); +propertiesApp. + +getSecurity(). + +setProtocol("SASL_SSL"); +propertiesApp. + +getProperties(). + +put("sasl.mechanism","SCRAM-SHA-512"); +``` + +Everything under `connectionProperties` is translated into plain Kafka client properties when the consumer, producer and +admin clients are created (`buildConsumerProperties()`, `buildProducerProperties()`, `buildAdminProperties()`). Within +the consumer, precedence is: `connectionProperties.properties` (common),`connectionProperties.consumer.*` +typed fields, `connectionProperties.consumer.properties` (raw consumer-specific). + +## Consumer group for domain events + +The consumer group of the domain events listener is taken from the Kafka connection properties, so you configure it +where the rest of the technical connection settings live: + +```yaml title="application.yaml" +reactive: + commons: + kafka: + app: + connectionProperties: + bootstrap-servers: broker1:9094 + consumer: + group-id: dummy.consumer-group +``` + +or programmatically, through the customizer: + +```java + +@Bean +public AsyncKafkaPropsDomain.KafkaPropsCustomizer kafkaPropsCustomizer() { + return domainProperties -> domainProperties.customize("app", app -> { + app.getConnectionProperties().setBootstrapServers(List.of("localhost:9092")); + app.getConnectionProperties().getConsumer().setGroupId("dummy.consumer-group"); + } + ); +} +``` + +Resolution order for the events consumer group id: + +1. The `group.id` present in the consumer connection properties, no matter how it was provided: + `connectionProperties.consumer.group-id`, `connectionProperties.consumer.properties."group.id"` or + `connectionProperties.properties."group.id"`. It is used **exactly as configured**, without appending any suffix. +2. Otherwise, the value defined by the `domain.events.eventsSuffix` properties is used, preserving the default + configuration. + +Each domain resolves its own group id, so you can authorize a different consumer group per Kafka cluster. + ## Configuration approaches There are two ways to supply domain properties. Choose the one that best fits your use case. ### Approach 1: YAML only -Define all domains directly in `application.yaml` as shown above. No additional Java configuration is needed. -This is the simplest approach and works well when properties do not depend on runtime values such as secrets. +Define all domains directly in `application.yaml` as shown above. No additional Java configuration is needed. This is +the simplest approach and works well when properties do not depend on runtime values such as secrets. ### Approach 2: Hybrid YAML + `KafkaPropsCustomizer` -Use this approach when you want to define the domain structure in YAML (topology, retry settings, etc.) but need to -set some properties at runtime — for example, loading bootstrap servers or credentials from a secrets manager. +Use this approach when you want to define the domain structure in YAML (topology, retry settings, etc.) but need to set +some properties at runtime — for example, loading bootstrap servers or credentials from a secrets manager. Declare your domains in `application.yaml` as usual, then define a `KafkaPropsCustomizer` bean to override specific -properties after the YAML is loaded. The customizer receives the full map of configured domains and can modify -any property on any domain. +properties after the YAML is loaded. The customizer receives the full map of configured domains and can modify any +property on any domain. :::caution[YAML domains are optional] The `KafkaPropsCustomizer` can work with or without pre-existing YAML domains. If no domains are defined in your `application.yaml` under `reactive.commons.kafka`, you can define all domains directly inside the customizer using `domainProperties.put("", AsyncKafkaProps.builder()...build())`. At least one domain must exist after the -customizer -executes, otherwise an `InvalidConfigurationException` is thrown. +customizer executes, otherwise an `InvalidConfigurationException` is thrown. ::: You have two options: -**Option A: Define domains in YAML, then override with customizer** +**Option A: Define domains in YAML, then merge overrides with the customizer** Declare your domains in `application.yaml` as usual, then use the customizer to override or extend them. @@ -85,7 +190,6 @@ reactive: package sample; import org.reactivecommons.async.kafka.config.KafkaProperties; -import org.reactivecommons.async.kafka.config.props.AsyncKafkaProps; import org.reactivecommons.async.kafka.config.props.AsyncKafkaPropsDomain; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -103,27 +207,39 @@ public class KafkaConfig { @Bean public AsyncKafkaPropsDomain.KafkaPropsCustomizer kafkaPropsCustomizer() { return domainProperties -> { - // Customize the "app" domain — overrides take precedence over YAML values - AsyncKafkaProps app = domainProperties.get("app"); - if (app != null) { - app.setConnectionProperties(loadFromSecret("secret-app-kafka")); - } + // Customize the "app" domain — YAML values are kept, only these fields are overridden + domainProperties.customize("app", app -> + app.setConnectionProperties(loadFromSecret("secret-app-kafka")) + ); // Customize the "accounts" domain independently - AsyncKafkaProps accounts = domainProperties.get("accounts"); - if (accounts != null) { - accounts.setConnectionProperties(loadFromSecret("secret-accounts-kafka")); - } + domainProperties.customize("accounts", accounts -> + accounts.setConnectionProperties(loadFromSecret("secret-accounts-kafka")) + ); }; } } ``` +:::danger[`put` replaces the domain, `customize` merges it] +`domainProperties` is a map of domains, so `domainProperties.put("app", AsyncKafkaProps.builder()...build())` +**replaces the whole domain** and every value bound from `application.yaml` for that domain is lost, including +`connectionProperties`, which goes back to its defaults (`localhost:9092`, no `group.id`, hence a consumer group +resolved as `domain.events.eventsSuffix`). + +The same happens with `app.setConnectionProperties(newKafkaProperties)`: it replaces the whole connection properties +object, so anything configured in YAML under `connectionProperties` (for example `consumer.group-id`) is discarded. +Prefer mutating the existing instance (`app.getConnectionProperties().setBootstrapServers(...)`) when you want to keep +the YAML values. + +Use `customize(domain, props -> ...)` to merge, and `put(domain, props)` only when you intend to define the whole domain +programmatically. +::: + **Option B: Define all domains in the customizer (no YAML domains)** -If you prefer full programmatic control, **omit the `reactive.commons.kafka` section entirely from -your `application.yaml`** and define all domains -inside the customizer: +If you prefer full programmatic control, **omit the `reactive.commons.kafka` section entirely from your +`application.yaml`** and define all domains inside the customizer: ```java package sample; @@ -164,10 +280,12 @@ public class KafkaConfig { **Key rules for the hybrid approach:** - Properties set in the customizer **take precedence** over YAML values. -- YAML values not touched by the customizer are **preserved**. +- YAML values not touched by the customizer are **preserved**, as long as you use + `domainProperties.customize("domain", props -> ...)` or mutate the instance returned by + `domainProperties.get("domain")`. +- `domainProperties.put("domain", props)` **replaces** the whole domain: use it to define domains programmatically, not + to override a few properties of a domain declared in YAML. - The customizer can also **add new domains** by calling `domainProperties.put("newDomain", asyncKafkaProps)`. -- The first domain declared in YAML becomes the **default domain** and automatically resolves handlers registered - without an explicit domain (e.g., via `HandlerRegistry.register().listenEvent(...)`). ## Loading properties from a secret @@ -179,8 +297,8 @@ a secrets manager. ::: The recommended way to load connection properties from a secrets manager is to use the `KafkaPropsCustomizer` (see -[Approach 2](#approach-2-hybrid-yaml--kafkapropscustomizer)). This gives you full control over all domain properties -at runtime. The example below uses the [Secrets Manager](https://github.com/bancolombia/secrets-manager) library. +[Approach 2](#approach-2-hybrid-yaml--kafkapropscustomizer)). This gives you full control over all domain properties at +runtime. The example below uses the [Secrets Manager](https://github.com/bancolombia/secrets-manager) library. 1. Create a `@ConfigurationProperties` record to map the secret fields: @@ -267,11 +385,12 @@ public class SecretsConfig { ```java import lombok.RequiredArgsConstructor; -import org.reactivecommons.async.kafka.config.props.AsyncKafkaProps; import org.reactivecommons.async.kafka.config.props.AsyncKafkaPropsDomain; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import java.util.List; + @Configuration @RequiredArgsConstructor public class KafkaConfig { @@ -280,12 +399,9 @@ public class KafkaConfig { @Bean public AsyncKafkaPropsDomain.KafkaPropsCustomizer kafkaPropsCustomizer() { - return domainProperties -> { - AsyncKafkaProps app = domainProperties.get("app"); - if (app != null) { - app.setConnectionProperties(kafkaConnectionProperties.toKafkaProperties()); - } - }; + return domainProperties -> domainProperties.customize("app", app -> + app.setConnectionProperties(kafkaConnectionProperties.toKafkaProperties()) + ); } } ``` diff --git a/docs/package-lock.json b/docs/package-lock.json index 0a040212..c545b7c3 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -8,64 +8,79 @@ "name": "docs", "version": "0.0.0", "dependencies": { - "@docusaurus/core": "^3.10.1", - "@docusaurus/preset-classic": "^3.10.1", - "@docusaurus/theme-common": "^3.10.1", + "@docusaurus/core": "^3.10.2", + "@docusaurus/preset-classic": "^3.10.2", + "@docusaurus/theme-common": "^3.10.2", "@mdx-js/react": "^3.1.1", "clsx": "^2.1.1", "prism-react-renderer": "^2.4.1", - "react": "^19.2.7", - "react-dom": "^19.2.7" + "react": "^19.2.8", + "react-dom": "^19.2.8" }, "devDependencies": { - "@docusaurus/module-type-aliases": "^3.10.1", - "@docusaurus/types": "^3.10.1" + "@docusaurus/module-type-aliases": "^3.10.2", + "@docusaurus/types": "^3.10.2" }, "engines": { "node": ">=24.0.0" } }, + "node_modules/@11ty/gray-matter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@11ty/gray-matter/-/gray-matter-1.0.0.tgz", + "integrity": "sha512-7mJJl+wf1AByoT0PknQiQfOPnVNT4fevGrUBVWO4HXsnYn1aQPyRyrELYrNUFleUBM++KzMKN6QaxHPk0t/6/g==", + "license": "MIT", + "dependencies": { + "js-yaml": "^4.1.0", + "kind-of": "^6.0.3", + "section-matter": "^1.0.0", + "strip-bom-string": "^1.0.0" + }, + "engines": { + "node": ">=11" + } + }, "node_modules/@algolia/abtesting": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.20.0.tgz", - "integrity": "sha512-5CqkS592H3+24b6H6CQ2RVpphdmAuIElZzv0Hngqo/ZEZpUZJ+KGLcueBhx33fv2wYBXyuuvskG5aQ7Ti+lR0g==", + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.22.0.tgz", + "integrity": "sha512-BFR6zNowNKcY7Ou7TaJc9QWexES4YKPbmf/OTFofpdsdhz4x6q0lbxp3duO0EHnyrN7rE4ba/TSXuY+BDGu4+g==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.54.0", - "@algolia/requester-browser-xhr": "5.54.0", - "@algolia/requester-fetch": "5.54.0", - "@algolia/requester-node-http": "5.54.0" + "@algolia/client-common": "5.56.0", + "@algolia/requester-browser-xhr": "5.56.0", + "@algolia/requester-fetch": "5.56.0", + "@algolia/requester-node-http": "5.56.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/autocomplete-core": { - "version": "1.19.8", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.19.8.tgz", - "integrity": "sha512-3YEorYg44niXcm7gkft3nXYItHd44e8tmh4D33CTszPgP0QWkaLEaFywiNyJBo7UL/mqObA/G9RYuU7R8tN1IA==", + "version": "1.19.9", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.19.9.tgz", + "integrity": "sha512-4U2JKLMWlDu0CotYyUkWakDxr8AIav3QtIUXXRpfavYN29aVWfzlwJp9T0rPKEf/dO2QCPAUc0Kq1Tj1GJxo2A==", "license": "MIT", "dependencies": { - "@algolia/autocomplete-plugin-algolia-insights": "1.19.8", - "@algolia/autocomplete-shared": "1.19.8" + "@algolia/autocomplete-plugin-algolia-insights": "1.19.9", + "@algolia/autocomplete-shared": "1.19.9" } }, "node_modules/@algolia/autocomplete-plugin-algolia-insights": { - "version": "1.19.8", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.19.8.tgz", - "integrity": "sha512-ZvJWO8ZZJDpc1LNM2TTBdmQsZBLMR4rU5iNR2OYvEeFBiaf/0ESnRSSLQbryarJY4SVxtoz6A2ZtDMNM+iQEAA==", + "version": "1.19.9", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.19.9.tgz", + "integrity": "sha512-6mExC6X7762s2SV3eJy3QOkB8bdMmnUhQ2agvGVDuzwoGyr3PquGSY/0vPQXCfiAiCaXUz1rXn+lwghgSi0l0w==", "license": "MIT", "dependencies": { - "@algolia/autocomplete-shared": "1.19.8" + "@algolia/autocomplete-shared": "1.19.9" }, "peerDependencies": { "search-insights": ">= 1 < 3" } }, "node_modules/@algolia/autocomplete-shared": { - "version": "1.19.8", - "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.19.8.tgz", - "integrity": "sha512-h5hf2t8ejF6vlOgvLaZzQbWs5SyH2z4PAWygNAvvD/2RI29hdQ54ldUGwqVuj9Srs+n8XUKTPUqb7fvhBhQrnQ==", + "version": "1.19.9", + "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.19.9.tgz", + "integrity": "sha512-YosP9Uoek6y/Ur1r1qeogk4biMe/hzkyNcgMCciw0//3XpCM7VlYLSHnyt/vOnEOGhCCc0+3v+unEiH6zz+Z1A==", "license": "MIT", "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", @@ -73,99 +88,99 @@ } }, "node_modules/@algolia/client-abtesting": { - "version": "5.54.0", - "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.54.0.tgz", - "integrity": "sha512-IXnH1x3DBsPQA4/FRO0Pvkfy79tKy5Qr+ugAV9jdcGkpzHc476D0WV1MFJ+pZxtbts0xh2JqzUVmYEqA6LkOpA==", + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.56.0.tgz", + "integrity": "sha512-7r4Z3NC7yU1oAQVWJNA2HX7tX481F3pJvCGyLIXiTdBcthz4Q/o21jwcMYDFkuI92UWTNBQQmHYgwHo1zS5dzg==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.54.0", - "@algolia/requester-browser-xhr": "5.54.0", - "@algolia/requester-fetch": "5.54.0", - "@algolia/requester-node-http": "5.54.0" + "@algolia/client-common": "5.56.0", + "@algolia/requester-browser-xhr": "5.56.0", + "@algolia/requester-fetch": "5.56.0", + "@algolia/requester-node-http": "5.56.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-analytics": { - "version": "5.54.0", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.54.0.tgz", - "integrity": "sha512-pBpRqm1wpE0GnGy2rNLk9rjDn0Le4iywNRtnAWblLeqfjxpWKg8lWnk7nmSoTShFO31sz2jatXXzxK2lz8ipbA==", + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.56.0.tgz", + "integrity": "sha512-avmjXQSq+jadFO8Xl2em05/uQdQnEmHsJyOAdVbZkmVgpMfxL12aJwVVfGNwYr9nulcpuJN1X0lTaQ5wxuNGcA==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.54.0", - "@algolia/requester-browser-xhr": "5.54.0", - "@algolia/requester-fetch": "5.54.0", - "@algolia/requester-node-http": "5.54.0" + "@algolia/client-common": "5.56.0", + "@algolia/requester-browser-xhr": "5.56.0", + "@algolia/requester-fetch": "5.56.0", + "@algolia/requester-node-http": "5.56.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-common": { - "version": "5.54.0", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.54.0.tgz", - "integrity": "sha512-WbuwRUlFvSOsuxqTDjSSmgusuF5KFt+oFPzobvPDvodra6EWnVwUXjz0elkNSsnsIlZGtcXlX3LhxkO7rF90jw==", + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.56.0.tgz", + "integrity": "sha512-v2TPStUhY//ripPjIVclZ8AWc7DEGooXULZGFlFu37zNatgHjw34oZZ+OSbbc/YHO+xZwPl62I1k8xH1m4S2eg==", "license": "MIT", "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-insights": { - "version": "5.54.0", - "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.54.0.tgz", - "integrity": "sha512-/HNLVi3kPI+JhO59WbglLjPM2c4uECU+x4gk1iADseKtE5eYqJ/RJ+FIwM8xzPFKhFJaw+8hVq4lkd0nn8HDDg==", + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.56.0.tgz", + "integrity": "sha512-P0ehROpM4Sem3Sqo5x2cKPgj67D3G3jy0rh1Amwkcvsfr6tkvIcdCmerieanqTF7NxUMPNFLkpIFeMO8Rpa50w==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.54.0", - "@algolia/requester-browser-xhr": "5.54.0", - "@algolia/requester-fetch": "5.54.0", - "@algolia/requester-node-http": "5.54.0" + "@algolia/client-common": "5.56.0", + "@algolia/requester-browser-xhr": "5.56.0", + "@algolia/requester-fetch": "5.56.0", + "@algolia/requester-node-http": "5.56.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-personalization": { - "version": "5.54.0", - "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.54.0.tgz", - "integrity": "sha512-6TolyyDRumIKeLGBGSFAZsSIJ8hrm6NFCGR1jO7pQTiOtSWgAIxcFE5/JRpZ3g+unG4OkNOFy+I0dUEquIDbig==", + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.56.0.tgz", + "integrity": "sha512-SXK3Vn3WVxyzbm31oePZBJkp1wpOyuWdd4B/Pv7n0aXDxmeSWhC1R1FC1517mMrFAIaPH4Rt0x6RUe7ZNjz8FA==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.54.0", - "@algolia/requester-browser-xhr": "5.54.0", - "@algolia/requester-fetch": "5.54.0", - "@algolia/requester-node-http": "5.54.0" + "@algolia/client-common": "5.56.0", + "@algolia/requester-browser-xhr": "5.56.0", + "@algolia/requester-fetch": "5.56.0", + "@algolia/requester-node-http": "5.56.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-query-suggestions": { - "version": "5.54.0", - "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.54.0.tgz", - "integrity": "sha512-AxW2MLBhjBtGX5kIZrVLO2SP2vRkZxJw5qHGxnQJfLcYhA04mFNP0fWRtHshlcVnDk9M8L9lwfr2lGpf3Er+hw==", + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.56.0.tgz", + "integrity": "sha512-5+ZdX8garFnmycnZgKhtXHePEaLj5zqDxI/0lkhhluzCcvTn0/PvvTirTg8hHYetQHvn7GDyeAiqTAieMvMW4A==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.54.0", - "@algolia/requester-browser-xhr": "5.54.0", - "@algolia/requester-fetch": "5.54.0", - "@algolia/requester-node-http": "5.54.0" + "@algolia/client-common": "5.56.0", + "@algolia/requester-browser-xhr": "5.56.0", + "@algolia/requester-fetch": "5.56.0", + "@algolia/requester-node-http": "5.56.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-search": { - "version": "5.54.0", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.54.0.tgz", - "integrity": "sha512-ngdgVGp05lJzUyA+sUzr0MDZ7AMtANcJpwIzq4ZsfpZL5B3S7A4XYfMcU2sECZc3bx3ysOhYcdbbaTjc3ve0WQ==", + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.56.0.tgz", + "integrity": "sha512-+mKUdYvqOi0BcvpAEyCEw49vSBptufIcfibtHz2bdr1pI789M46Yt0uQEk/sxtK3teh71OQvVFHaTDzShUWewQ==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.54.0", - "@algolia/requester-browser-xhr": "5.54.0", - "@algolia/requester-fetch": "5.54.0", - "@algolia/requester-node-http": "5.54.0" + "@algolia/client-common": "5.56.0", + "@algolia/requester-browser-xhr": "5.56.0", + "@algolia/requester-fetch": "5.56.0", + "@algolia/requester-node-http": "5.56.0" }, "engines": { "node": ">= 14.0.0" @@ -178,81 +193,81 @@ "license": "MIT" }, "node_modules/@algolia/ingestion": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.54.0.tgz", - "integrity": "sha512-hee59Z7FgZ6/13FYL05ANPwRJY1pfvIlrwC8eBZYdiRFXTJVvf94IyTWOxLqRVpbseDP6eQQTW+PT7/DxTZIng==", + "version": "1.56.0", + "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.56.0.tgz", + "integrity": "sha512-9g/zj+AZx5moFcdFIrYQoVrueXivjUcc3MQHtCYT8WhIuk1lUh1AyEhvJCS0XBZld09cLvd1AZ3BvDBpVpX2UA==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.54.0", - "@algolia/requester-browser-xhr": "5.54.0", - "@algolia/requester-fetch": "5.54.0", - "@algolia/requester-node-http": "5.54.0" + "@algolia/client-common": "5.56.0", + "@algolia/requester-browser-xhr": "5.56.0", + "@algolia/requester-fetch": "5.56.0", + "@algolia/requester-node-http": "5.56.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/monitoring": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.54.0.tgz", - "integrity": "sha512-diCjZVbIO7Pzw98tEKqLWIAgmQBI3Zt1sHsXyAPNGZgn32derpIXTnjjpJbZl+uAhSSznd6SfxFGdC9uYdN1TA==", + "version": "1.56.0", + "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.56.0.tgz", + "integrity": "sha512-Qf3Sr6f9A9uxCZUf3MXS0d2b877uYzEB5yxqpVGXAhcJnBCQjrRRon0KvefpGkxy+BshrIJs96OUoMtGqXTFDA==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.54.0", - "@algolia/requester-browser-xhr": "5.54.0", - "@algolia/requester-fetch": "5.54.0", - "@algolia/requester-node-http": "5.54.0" + "@algolia/client-common": "5.56.0", + "@algolia/requester-browser-xhr": "5.56.0", + "@algolia/requester-fetch": "5.56.0", + "@algolia/requester-node-http": "5.56.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/recommend": { - "version": "5.54.0", - "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.54.0.tgz", - "integrity": "sha512-6zeslypRAGWDgVJEJYAPuqmyquHvnw4MQwG+XXdrw5dTNDjXYIcCJdQQcCY06xPF9tUvmzy/1vHiH9QxhgwuOQ==", + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.56.0.tgz", + "integrity": "sha512-GXWG1rWc5wu8hY4N33Y3b6ernY6sAdAvmKWN/zHAiACOx40WnpG0TVX5YazCAr/9gOYGInSiM2A0y2jy2xbiDA==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.54.0", - "@algolia/requester-browser-xhr": "5.54.0", - "@algolia/requester-fetch": "5.54.0", - "@algolia/requester-node-http": "5.54.0" + "@algolia/client-common": "5.56.0", + "@algolia/requester-browser-xhr": "5.56.0", + "@algolia/requester-fetch": "5.56.0", + "@algolia/requester-node-http": "5.56.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-browser-xhr": { - "version": "5.54.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.54.0.tgz", - "integrity": "sha512-iHZax214LPXd7XizQ4BNnTsegl8f3IeKm8JcrmSNZ/5x1rZ5xLkbG/anltAWtLpoRNnfpr4Z80YdYeVVPpx6wQ==", + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.56.0.tgz", + "integrity": "sha512-7t24cBxaInS3mZb7ddEaZT/tp6q+/aR4YttsQVyP1/i+LmwPR34atO35KjaLFCcRVrlP7sYOAqkCfg6lIRB+ew==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.54.0" + "@algolia/client-common": "5.56.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-fetch": { - "version": "5.54.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.54.0.tgz", - "integrity": "sha512-YKtuG5YwPxZ+kkfd4HmUO7Z9aICPUSMlHslzKTmtMMhxGnetxEqGj9T/v2r/PdcuOUC5oW0CHe8akJk8cpS3gQ==", + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.56.0.tgz", + "integrity": "sha512-R7ePHgVYmDFjZpvrsVAfbDz/d4RxKAYZ5/vgLfIsCVRZRryjWl/3INOxpOICzitehQ5FjNtNjcLQTrmHPTcHBQ==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.54.0" + "@algolia/client-common": "5.56.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-node-http": { - "version": "5.54.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.54.0.tgz", - "integrity": "sha512-zyZDJ4WS5TnjZZ5pqywTBFO9olW7QMtY2kf2dbLnu+UTzfc9ri/HGf27jRN2NTbX9FcRPxSqPqzUhF/BZIx0VA==", + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.56.0.tgz", + "integrity": "sha512-PIOUXlSnrqM0S+WOgDRb4RzotydJH7ZoT6tOyL7tAO7qJOfvX5wsEW8Pe+PMKMwvuI4/gIyK9cg2H7lJXqnc4Q==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.54.0" + "@algolia/client-common": "5.56.0" }, "engines": { "node": ">= 14.0.0" @@ -321,13 +336,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", - "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.29.7", - "@babel/types": "^7.29.7", + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -620,12 +635,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", - "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", "license": "MIT", "dependencies": { - "@babel/types": "^7.29.7" + "@babel/types": "^7.29.8" }, "bin": { "parser": "bin/babel-parser.js" @@ -1226,15 +1241,15 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.7.tgz", - "integrity": "sha512-TM2ZcQLoG2/y4HODiStCo10DibYhWhGWAwVv+EQKmG/7GFl0N+AAmUiXOMKM+aiJ9XBJ9AHVZBvTzMnJ2sM3cQ==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.8.tgz", + "integrity": "sha512-6iSnEK0zlkLKU4heofK/AdmRD4e2SHVpJMtrwnTCzhnaM98ria4rTrOXBBi45BTTYnJtO8txnPsX4fChYXkmeA==", "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7", - "@babel/traverse": "^7.29.7" + "@babel/traverse": "^7.29.8" }, "engines": { "node": ">=6.9.0" @@ -1530,9 +1545,9 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.7.tgz", - "integrity": "sha512-rNNFV0DBAJp988xW2DOntfDoYn1eR8GGF5AT5vYc+rjyfaQkM242c9tZUHHPe7KYaiJizXPWhQTzzdbXySyhBw==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.8.tgz", + "integrity": "sha512-0UpIXPtdDtMXfnV2OJAVMLpj3H/92vmkA6lpSRakmycJvj3VUy6Xs1dM8tXRugupykr5WB+LpiVl0J8LMVg2mg==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" @@ -1620,9 +1635,9 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.29.7.tgz", - "integrity": "sha512-/u5K1QWada7tbYNqTjMh96718g9NTwh9tfPJMsSmVsQwGT447FskV+KcfeXkXq2GWki4EM/MuTdmBec+hOuVTQ==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.29.8.tgz", + "integrity": "sha512-4S9ksMGVWUshvgK0mKfvZky7leuG5/uoFVwMpAomJ8bMoDJiNHRVmc1EglwW/CmGVSqqWpEbXm9FmbRit22qoA==", "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", @@ -1946,17 +1961,17 @@ } }, "node_modules/@babel/traverse": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", - "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", + "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.29.7", - "@babel/generator": "^7.29.7", + "@babel/generator": "^7.29.8", "@babel/helper-globals": "^7.29.7", - "@babel/parser": "^7.29.7", + "@babel/parser": "^7.29.8", "@babel/template": "^7.29.7", - "@babel/types": "^7.29.7", + "@babel/types": "^7.29.8", "debug": "^4.3.1" }, "engines": { @@ -1964,9 +1979,9 @@ } }, "node_modules/@babel/types": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", - "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.29.7", @@ -3312,9 +3327,9 @@ } }, "node_modules/@docsearch/core": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/@docsearch/core/-/core-4.6.3.tgz", - "integrity": "sha512-rUOujwIpxJRgD7+kicVsI3D5sqBvdiRTquzWBpTEXZs8ZXfGbfzpus5HqumaNYTppN2HvH8E2yNuRwYdHJeOlA==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@docsearch/core/-/core-4.7.0.tgz", + "integrity": "sha512-p/9xVKmPDj3FPvMfPf5naVO3Ej8SCbcUugGvx1+8GgkuBNbqxqN2Irx3WLBv8VY0jH7XpRwKWdlmjXLZsmTLsg==", "license": "MIT", "peerDependencies": { "@types/react": ">= 16.8.0 < 20.0.0", @@ -3334,20 +3349,20 @@ } }, "node_modules/@docsearch/css": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-4.6.3.tgz", - "integrity": "sha512-nlOwcXcsNAptQl4vlL4MA78qNJKO0Qlds5GuBjCoePgkebTXLSf8Qt1oyZ3YBshYupKXG9VRGEsk1zr23d+bzQ==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-4.7.0.tgz", + "integrity": "sha512-Sk5xkdRFeE7PeWjG9l4AfTwdvMfr9wHiwNNCpHXT4v4SNyNMKdHGvEILc31BgaVFGDDNbv5u/a73tofRiwbEZw==", "license": "MIT" }, "node_modules/@docsearch/react": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-4.6.3.tgz", - "integrity": "sha512-Bg2wdDsoQVlNCcEKuEJAU04tvHCqgx8rIu+uIoM4pRtcx3TBKJuXutJik3LTA8LRc9YEyHkrYUrmcC0D7BYf+g==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-4.7.0.tgz", + "integrity": "sha512-x6oedjJ8O8/pIDBsMo5Orca3/6cQCz616/CwthVe68l43mqnj2lrJ9kFQITBqy8hMsS3nWeBWFoVO5dJ1DCFKA==", "license": "MIT", "dependencies": { "@algolia/autocomplete-core": "1.19.2", - "@docsearch/core": "4.6.3", - "@docsearch/css": "4.6.3" + "@docsearch/core": "4.7.0", + "@docsearch/css": "4.7.0" }, "peerDependencies": { "@types/react": ">= 16.8.0 < 20.0.0", @@ -3403,9 +3418,9 @@ } }, "node_modules/@docusaurus/babel": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@docusaurus/babel/-/babel-3.10.1.tgz", - "integrity": "sha512-DZzFO1K3v/GoEt1fx1DiYHF4en+PuhtQf1AkQJa5zu3CoeKSpr5cpQRUlz3jr0m44wyzmSXu9bVpfir+N4+8bg==", + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/@docusaurus/babel/-/babel-3.10.2.tgz", + "integrity": "sha512-aJ1hpGyvfkte3dDAfNbWM4biW4yWZBVz7TIGLZP+v+tWOBgxX3e0N5ZIXHIvmfNNXTI77pcHUx3KmtOk05Ze3Q==", "license": "MIT", "dependencies": { "@babel/core": "^7.25.9", @@ -3417,8 +3432,8 @@ "@babel/preset-typescript": "^7.25.9", "@babel/runtime": "^7.25.9", "@babel/traverse": "^7.25.9", - "@docusaurus/logger": "3.10.1", - "@docusaurus/utils": "3.10.1", + "@docusaurus/logger": "3.10.2", + "@docusaurus/utils": "3.10.2", "babel-plugin-dynamic-import-node": "^2.3.3", "fs-extra": "^11.1.1", "tslib": "^2.6.0" @@ -3428,17 +3443,17 @@ } }, "node_modules/@docusaurus/bundler": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@docusaurus/bundler/-/bundler-3.10.1.tgz", - "integrity": "sha512-HIqQPvbqnnQRe4NsBd1774KRarjXqS6wHsWELtyuSs1gCfvixJO2jUGH/OEBtr1Gvzpw+ze5CjGMvSJ8UE1KUw==", + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/@docusaurus/bundler/-/bundler-3.10.2.tgz", + "integrity": "sha512-i0ZNcy0f0WhaOlYVgzLsWhIoEXO9kS3HRoKPtgE6vQtZUq7arKZaYdNBudr3mqCmd+TyOkwtwfHgs1ENj07r5g==", "license": "MIT", "dependencies": { "@babel/core": "^7.25.9", - "@docusaurus/babel": "3.10.1", - "@docusaurus/cssnano-preset": "3.10.1", - "@docusaurus/logger": "3.10.1", - "@docusaurus/types": "3.10.1", - "@docusaurus/utils": "3.10.1", + "@docusaurus/babel": "3.10.2", + "@docusaurus/cssnano-preset": "3.10.2", + "@docusaurus/logger": "3.10.2", + "@docusaurus/types": "3.10.2", + "@docusaurus/utils": "3.10.2", "babel-loader": "^9.2.1", "clean-css": "^5.3.3", "copy-webpack-plugin": "^11.0.0", @@ -3471,18 +3486,18 @@ } }, "node_modules/@docusaurus/core": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.10.1.tgz", - "integrity": "sha512-3pf2fXXw0eVk8WnC3T4LIigRDupcpvngpKo9Vy7mYyBhuddc0klDUuZAIfzMoK6z05pdlk6EFC/vBSX43+1O5w==", - "license": "MIT", - "dependencies": { - "@docusaurus/babel": "3.10.1", - "@docusaurus/bundler": "3.10.1", - "@docusaurus/logger": "3.10.1", - "@docusaurus/mdx-loader": "3.10.1", - "@docusaurus/utils": "3.10.1", - "@docusaurus/utils-common": "3.10.1", - "@docusaurus/utils-validation": "3.10.1", + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.10.2.tgz", + "integrity": "sha512-EYByj6nk+aD9KeVxV6Hmo2/nAAT79P21Y82ycTBOBtrmqilloIbIEhgL2/8Xpt2Jz/pgNqHAwyusOGwmbKeJmA==", + "license": "MIT", + "dependencies": { + "@docusaurus/babel": "3.10.2", + "@docusaurus/bundler": "3.10.2", + "@docusaurus/logger": "3.10.2", + "@docusaurus/mdx-loader": "3.10.2", + "@docusaurus/utils": "3.10.2", + "@docusaurus/utils-common": "3.10.2", + "@docusaurus/utils-validation": "3.10.2", "boxen": "^6.2.1", "chalk": "^4.1.2", "chokidar": "^3.5.3", @@ -3490,7 +3505,7 @@ "combine-promises": "^1.1.0", "commander": "^5.1.0", "core-js": "^3.31.1", - "detect-port": "^1.5.1", + "detect-port": "^2.1.0", "escape-html": "^1.0.3", "eta": "^2.2.0", "eval": "^0.1.8", @@ -3538,9 +3553,9 @@ } }, "node_modules/@docusaurus/cssnano-preset": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.10.1.tgz", - "integrity": "sha512-eNfHGcTKCSq6xmcavAkX3RRclHaE2xRCMParlDXLdXVP01/a2e/jKXMj/0ULnLFQSNwwuI62L0Ge8J+nZsR7UQ==", + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.10.2.tgz", + "integrity": "sha512-4gCnHRbJLTloiwfvFAa92tgb2gI4KYhvjfQVYnEaiMO/EgvWfCo1LwytHXen+1oZAN0VAlS0JAPxp3MsvKDa3A==", "license": "MIT", "dependencies": { "cssnano-preset-advanced": "^6.1.2", @@ -3553,9 +3568,9 @@ } }, "node_modules/@docusaurus/logger": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.10.1.tgz", - "integrity": "sha512-oPjNFnfJsRCkePVjkGrxWGq4MvJKRQT0r9jOP0eRBTZ7Wr9FAbzdP/Gjs0I2Ss6YRkPoEgygKG112OkE6skvJw==", + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.10.2.tgz", + "integrity": "sha512-gSEwqtPfCAnC3ZSJY6xL7tcIfgg0vFD39jbv93eakuweyvO2864xR0K+kmKwBhkTCtWRNjuGGnb5rdmkD/ndqw==", "license": "MIT", "dependencies": { "chalk": "^4.1.2", @@ -3566,14 +3581,14 @@ } }, "node_modules/@docusaurus/mdx-loader": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.10.1.tgz", - "integrity": "sha512-GRmeb/wQ+iXRrFwcHBfgQhrJxGElgCsoTWZYDhccjsZVne1p8MK/EpQVIloXttz76TCe78kKD5AEG9n1xc1oxQ==", + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.10.2.tgz", + "integrity": "sha512-9Fd4V/SFjfrVQ0JH5EN0+iPWyFunvTeQE3gfyFeetqPaXMP0OylIjOw16dCuXG4NZJrYdBqwzjh18/h3gRi47w==", "license": "MIT", "dependencies": { - "@docusaurus/logger": "3.10.1", - "@docusaurus/utils": "3.10.1", - "@docusaurus/utils-validation": "3.10.1", + "@docusaurus/logger": "3.10.2", + "@docusaurus/utils": "3.10.2", + "@docusaurus/utils-validation": "3.10.2", "@mdx-js/mdx": "^3.0.0", "@slorber/remark-comment": "^1.0.0", "escape-html": "^1.0.3", @@ -3605,12 +3620,12 @@ } }, "node_modules/@docusaurus/module-type-aliases": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.10.1.tgz", - "integrity": "sha512-YoOZKUdGlp8xSYhuAkGdSo5Ydkbq4V4eK3sD8v0a2hloxCWdQbNBhkc+Ko9QyjpESc0BYcIGM5iHVAy5hdFV6w==", + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.10.2.tgz", + "integrity": "sha512-h/I5e4jaAhDHW4vaLENi1i2hnOEnXY1t9R+nnRTbgUl7ymVRzN/HF7dDfj8rKYGj8gfIge+Ef+iYRAMtbGvsrQ==", "license": "MIT", "dependencies": { - "@docusaurus/types": "3.10.1", + "@docusaurus/types": "3.10.2", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -3624,19 +3639,19 @@ } }, "node_modules/@docusaurus/plugin-content-blog": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.10.1.tgz", - "integrity": "sha512-mmkgE6Q2+K74tnkou7tXlpDLvoCU/qkSa2GSQ3XUiHWvcebCoDQzS670RR3tO8PmaWlIyWWISYWzZLuMfxunRA==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.10.1", - "@docusaurus/logger": "3.10.1", - "@docusaurus/mdx-loader": "3.10.1", - "@docusaurus/theme-common": "3.10.1", - "@docusaurus/types": "3.10.1", - "@docusaurus/utils": "3.10.1", - "@docusaurus/utils-common": "3.10.1", - "@docusaurus/utils-validation": "3.10.1", + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.10.2.tgz", + "integrity": "sha512-0cbEnNKf0InmLkhj/+nVRmqEnWEoOE8Mh+2x1qOXI0qYpCnphq4RXknVJ8BvybKRXqYVvbmdMfiJSup+k4tm5w==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.10.2", + "@docusaurus/logger": "3.10.2", + "@docusaurus/mdx-loader": "3.10.2", + "@docusaurus/theme-common": "3.10.2", + "@docusaurus/types": "3.10.2", + "@docusaurus/utils": "3.10.2", + "@docusaurus/utils-common": "3.10.2", + "@docusaurus/utils-validation": "3.10.2", "cheerio": "1.0.0-rc.12", "combine-promises": "^1.1.0", "feed": "^4.2.2", @@ -3659,20 +3674,20 @@ } }, "node_modules/@docusaurus/plugin-content-docs": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.10.1.tgz", - "integrity": "sha512-2jRVrtzjf8LClGTHQlwlwuD3wQXRx3WEoF7XUarJ8Ou+0onV+SLtejsyfY9JLpfUh9hPhXM4pbBGkyAY4Bi3HQ==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.10.1", - "@docusaurus/logger": "3.10.1", - "@docusaurus/mdx-loader": "3.10.1", - "@docusaurus/module-type-aliases": "3.10.1", - "@docusaurus/theme-common": "3.10.1", - "@docusaurus/types": "3.10.1", - "@docusaurus/utils": "3.10.1", - "@docusaurus/utils-common": "3.10.1", - "@docusaurus/utils-validation": "3.10.1", + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.10.2.tgz", + "integrity": "sha512-Sqwl4FPoZBDrlY8I2VU2H8O0M91CHp9T8ToMSkTZmjvHCif+1laqfXi6sTk8IfyVS/trN5yNjcWd1bFsGB6W5Q==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.10.2", + "@docusaurus/logger": "3.10.2", + "@docusaurus/mdx-loader": "3.10.2", + "@docusaurus/module-type-aliases": "3.10.2", + "@docusaurus/theme-common": "3.10.2", + "@docusaurus/types": "3.10.2", + "@docusaurus/utils": "3.10.2", + "@docusaurus/utils-common": "3.10.2", + "@docusaurus/utils-validation": "3.10.2", "@types/react-router-config": "^5.0.7", "combine-promises": "^1.1.0", "fs-extra": "^11.1.1", @@ -3692,16 +3707,16 @@ } }, "node_modules/@docusaurus/plugin-content-pages": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.10.1.tgz", - "integrity": "sha512-huJpaRPMl42nsFwuCXvV8bVDj2MazuwRJIUylI/RSlmZeJssVoZXeCjVf1y+1Drtpa9SKcdGn8yoJ76IRJijtw==", + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.10.2.tgz", + "integrity": "sha512-h5R12sZ/vV9EPiVjvIl9YFCOwkpwXes7dQMYt3EvP6Pphu4amHxxTqWxf08Fl5DR8h+oZMbWpFTNw5vKEYfvzQ==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.10.1", - "@docusaurus/mdx-loader": "3.10.1", - "@docusaurus/types": "3.10.1", - "@docusaurus/utils": "3.10.1", - "@docusaurus/utils-validation": "3.10.1", + "@docusaurus/core": "3.10.2", + "@docusaurus/mdx-loader": "3.10.2", + "@docusaurus/types": "3.10.2", + "@docusaurus/utils": "3.10.2", + "@docusaurus/utils-validation": "3.10.2", "fs-extra": "^11.1.1", "tslib": "^2.6.0", "webpack": "^5.88.1" @@ -3715,15 +3730,15 @@ } }, "node_modules/@docusaurus/plugin-css-cascade-layers": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-css-cascade-layers/-/plugin-css-cascade-layers-3.10.1.tgz", - "integrity": "sha512-r//fn+MNHkE1wCof8T29VAQezt1enGCpsFxoziBbvLgBM4JfXN2P3rxrBaavHmvLvm7lYkpJeitcDthwnmWCTw==", + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-css-cascade-layers/-/plugin-css-cascade-layers-3.10.2.tgz", + "integrity": "sha512-UkdvQby5OQUKWrw3lLnSTJXQ6VETaUVTuPQX9AABtmFm5h+ifEBx1OQ+LN726Q4byuwBf2ElHkf4qU4hTxdvRg==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.10.1", - "@docusaurus/types": "3.10.1", - "@docusaurus/utils": "3.10.1", - "@docusaurus/utils-validation": "3.10.1", + "@docusaurus/core": "3.10.2", + "@docusaurus/types": "3.10.2", + "@docusaurus/utils": "3.10.2", + "@docusaurus/utils-validation": "3.10.2", "tslib": "^2.6.0" }, "engines": { @@ -3731,14 +3746,14 @@ } }, "node_modules/@docusaurus/plugin-debug": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.10.1.tgz", - "integrity": "sha512-9KqOpKNfAyqGZykRb9LhIT/vyRF6sm/ykhjj/39JvaJahDS+jZJE0Z1Wfz9q3DUNDTMNN0Q7u/kk4rKKU+IJuA==", + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.10.2.tgz", + "integrity": "sha512-8vbZNOSCpnsT57EY6CgN7sgRVmx3KTYwO8Uvo2pbxOyb8tbqAwtT9SslqaQ41HbA1v1hpn5RP7u5s2KvRwAFpQ==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.10.1", - "@docusaurus/types": "3.10.1", - "@docusaurus/utils": "3.10.1", + "@docusaurus/core": "3.10.2", + "@docusaurus/types": "3.10.2", + "@docusaurus/utils": "3.10.2", "fs-extra": "^11.1.1", "react-json-view-lite": "^2.3.0", "tslib": "^2.6.0" @@ -3752,14 +3767,14 @@ } }, "node_modules/@docusaurus/plugin-google-analytics": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.10.1.tgz", - "integrity": "sha512-8o0P1KtmgdYQHH+oInitPpRWI0Of5XednAX4+DMhQNSmGSRNrsEEHg1ebv35m9AgRClfAytCJ5jA9KvcASTyuA==", + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.10.2.tgz", + "integrity": "sha512-kMHMBK9j4VAtgd5owwrRLRIi0EjkrpXlX7ePj1+y68XfVZV9I1T4S+koPDm+Hfw2TtnyHvh0uNrDvjz+DjQGVA==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.10.1", - "@docusaurus/types": "3.10.1", - "@docusaurus/utils-validation": "3.10.1", + "@docusaurus/core": "3.10.2", + "@docusaurus/types": "3.10.2", + "@docusaurus/utils-validation": "3.10.2", "tslib": "^2.6.0" }, "engines": { @@ -3771,15 +3786,14 @@ } }, "node_modules/@docusaurus/plugin-google-gtag": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.10.1.tgz", - "integrity": "sha512-pu3xIUo5o/zCMLfUY9BO5KOwSH0zIsAGyFRPvXHayFSA5XIhCU/SFuB0g0ZNjFn9niZLCaNvoeAuOGFJZq0fdw==", + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.10.2.tgz", + "integrity": "sha512-Vt90nNFhtAChRe9+it1hcHFgFvETdSnOkL5Bma+p6E/yU2tAYrvvyk+gv+LJGM2ZUkyKuKXLRsZ2Lb0bO7+Vog==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.10.1", - "@docusaurus/types": "3.10.1", - "@docusaurus/utils-validation": "3.10.1", - "@types/gtag.js": "^0.0.20", + "@docusaurus/core": "3.10.2", + "@docusaurus/types": "3.10.2", + "@docusaurus/utils-validation": "3.10.2", "tslib": "^2.6.0" }, "engines": { @@ -3791,14 +3805,14 @@ } }, "node_modules/@docusaurus/plugin-google-tag-manager": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.10.1.tgz", - "integrity": "sha512-f6fyGHiCm7kJHBtAisGQS5oNBnpnMTYQZxDXeVrnw/3zWU+LMA22pr6UHGYkBKDbN+qPC5QHG3NuOfzQLq3+Lw==", + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.10.2.tgz", + "integrity": "sha512-MLCffCldysi/R0nzJQP7ZWd0xAoGNnSTiVOo6TTR6mKVGFhE+/XArGe67ZcaZv1uytgQXoXs92VJrgVDrz80rQ==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.10.1", - "@docusaurus/types": "3.10.1", - "@docusaurus/utils-validation": "3.10.1", + "@docusaurus/core": "3.10.2", + "@docusaurus/types": "3.10.2", + "@docusaurus/utils-validation": "3.10.2", "tslib": "^2.6.0" }, "engines": { @@ -3810,17 +3824,17 @@ } }, "node_modules/@docusaurus/plugin-sitemap": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.10.1.tgz", - "integrity": "sha512-C26MbmmqgdjkDq1htaZ3aD7LzEDKFWXfpyQpt0EOUThuq5nV77zDaedV20yHcVo9p+3ey9aZ4pbHA0D3QcZTzg==", + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.10.2.tgz", + "integrity": "sha512-PODkwg5XetLML3hU/3xpCKJUZ9cqExLaBnD/Fzzwj2VHogLeqnDisLIujae87zuze7T4mCm2A6KEqZkyiz07EQ==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.10.1", - "@docusaurus/logger": "3.10.1", - "@docusaurus/types": "3.10.1", - "@docusaurus/utils": "3.10.1", - "@docusaurus/utils-common": "3.10.1", - "@docusaurus/utils-validation": "3.10.1", + "@docusaurus/core": "3.10.2", + "@docusaurus/logger": "3.10.2", + "@docusaurus/types": "3.10.2", + "@docusaurus/utils": "3.10.2", + "@docusaurus/utils-common": "3.10.2", + "@docusaurus/utils-validation": "3.10.2", "fs-extra": "^11.1.1", "sitemap": "^7.1.1", "tslib": "^2.6.0" @@ -3834,15 +3848,15 @@ } }, "node_modules/@docusaurus/plugin-svgr": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-svgr/-/plugin-svgr-3.10.1.tgz", - "integrity": "sha512-6SFxsmjWFkVLDmBUvFK6i72QjUwqyQFe4Ovz+SUJophJjOyVG3ZZG5IQpBC/kX/Gfv1yWeU9nWauH6F6Q7QX/Q==", + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-svgr/-/plugin-svgr-3.10.2.tgz", + "integrity": "sha512-JgfT3jWM0TJ8Uw0cEcqxHpybngQY1vlBYpuuNO+gEh5iPh5Ar+vxq/u9CFrYsWeXy48BN7Db76Pzp2edNXUQ8A==", "license": "MIT", "dependencies": { - "@docusaurus/core": "3.10.1", - "@docusaurus/types": "3.10.1", - "@docusaurus/utils": "3.10.1", - "@docusaurus/utils-validation": "3.10.1", + "@docusaurus/core": "3.10.2", + "@docusaurus/types": "3.10.2", + "@docusaurus/utils": "3.10.2", + "@docusaurus/utils-validation": "3.10.2", "@svgr/core": "8.1.0", "@svgr/webpack": "^8.1.0", "tslib": "^2.6.0", @@ -3857,26 +3871,26 @@ } }, "node_modules/@docusaurus/preset-classic": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.10.1.tgz", - "integrity": "sha512-YO/FL8v1zmbxoTso6mjMz/RDjhaTJxb1UpFFTDdY5847LLDCeyYiYlrhyTbgN1RIN3xnkLKZ9Lj1x8hUzI4JOg==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.10.1", - "@docusaurus/plugin-content-blog": "3.10.1", - "@docusaurus/plugin-content-docs": "3.10.1", - "@docusaurus/plugin-content-pages": "3.10.1", - "@docusaurus/plugin-css-cascade-layers": "3.10.1", - "@docusaurus/plugin-debug": "3.10.1", - "@docusaurus/plugin-google-analytics": "3.10.1", - "@docusaurus/plugin-google-gtag": "3.10.1", - "@docusaurus/plugin-google-tag-manager": "3.10.1", - "@docusaurus/plugin-sitemap": "3.10.1", - "@docusaurus/plugin-svgr": "3.10.1", - "@docusaurus/theme-classic": "3.10.1", - "@docusaurus/theme-common": "3.10.1", - "@docusaurus/theme-search-algolia": "3.10.1", - "@docusaurus/types": "3.10.1" + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.10.2.tgz", + "integrity": "sha512-a4B3VczmDl99zK0EufDQYomdJ186WDingjmDXxhN2PNPS9Ty/Y2M5CLFX1KQMRKqRTLiRDKfutzG5IY1FC/ceg==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.10.2", + "@docusaurus/plugin-content-blog": "3.10.2", + "@docusaurus/plugin-content-docs": "3.10.2", + "@docusaurus/plugin-content-pages": "3.10.2", + "@docusaurus/plugin-css-cascade-layers": "3.10.2", + "@docusaurus/plugin-debug": "3.10.2", + "@docusaurus/plugin-google-analytics": "3.10.2", + "@docusaurus/plugin-google-gtag": "3.10.2", + "@docusaurus/plugin-google-tag-manager": "3.10.2", + "@docusaurus/plugin-sitemap": "3.10.2", + "@docusaurus/plugin-svgr": "3.10.2", + "@docusaurus/theme-classic": "3.10.2", + "@docusaurus/theme-common": "3.10.2", + "@docusaurus/theme-search-algolia": "3.10.2", + "@docusaurus/types": "3.10.2" }, "engines": { "node": ">=20.0" @@ -3887,24 +3901,24 @@ } }, "node_modules/@docusaurus/theme-classic": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.10.1.tgz", - "integrity": "sha512-VU1RK0qb2pab0si4r7HFK37cYco8VzqLj3u1PspVipSr/z/GPVKHO4/HXbnePqHoWDk8urjyGSeatH0NIMBM1A==", - "license": "MIT", - "dependencies": { - "@docusaurus/core": "3.10.1", - "@docusaurus/logger": "3.10.1", - "@docusaurus/mdx-loader": "3.10.1", - "@docusaurus/module-type-aliases": "3.10.1", - "@docusaurus/plugin-content-blog": "3.10.1", - "@docusaurus/plugin-content-docs": "3.10.1", - "@docusaurus/plugin-content-pages": "3.10.1", - "@docusaurus/theme-common": "3.10.1", - "@docusaurus/theme-translations": "3.10.1", - "@docusaurus/types": "3.10.1", - "@docusaurus/utils": "3.10.1", - "@docusaurus/utils-common": "3.10.1", - "@docusaurus/utils-validation": "3.10.1", + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.10.2.tgz", + "integrity": "sha512-JqTSLQmqmA9uKWZsD5iwBGJ4JyKB4/yTw6PsSXVPRJG/6GAm/u+add9Iip+hvwP12/AnPNztrdxsI14NJW4KeA==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.10.2", + "@docusaurus/logger": "3.10.2", + "@docusaurus/mdx-loader": "3.10.2", + "@docusaurus/module-type-aliases": "3.10.2", + "@docusaurus/plugin-content-blog": "3.10.2", + "@docusaurus/plugin-content-docs": "3.10.2", + "@docusaurus/plugin-content-pages": "3.10.2", + "@docusaurus/theme-common": "3.10.2", + "@docusaurus/theme-translations": "3.10.2", + "@docusaurus/types": "3.10.2", + "@docusaurus/utils": "3.10.2", + "@docusaurus/utils-common": "3.10.2", + "@docusaurus/utils-validation": "3.10.2", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "copy-text-to-clipboard": "^3.2.0", @@ -3928,15 +3942,15 @@ } }, "node_modules/@docusaurus/theme-common": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.10.1.tgz", - "integrity": "sha512-0YtmIeoNo1fIw65LO8+/1dPgmDV86UmhMkow37gzjytuiCSQm9xob6PJy0L4kuQEMTLfUOGvkXvZr7GPrHquMA==", + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.10.2.tgz", + "integrity": "sha512-R9b/vMpK1yye6hNZTA6x/ivRv+at6GhxnXcxkpzCGzO1R1RwiquqiFg2wMFh6aqlJTpWRFKpFD2TzCDQcyOU0A==", "license": "MIT", "dependencies": { - "@docusaurus/mdx-loader": "3.10.1", - "@docusaurus/module-type-aliases": "3.10.1", - "@docusaurus/utils": "3.10.1", - "@docusaurus/utils-common": "3.10.1", + "@docusaurus/mdx-loader": "3.10.2", + "@docusaurus/module-type-aliases": "3.10.2", + "@docusaurus/utils": "3.10.2", + "@docusaurus/utils-common": "3.10.2", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -3956,20 +3970,20 @@ } }, "node_modules/@docusaurus/theme-search-algolia": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.10.1.tgz", - "integrity": "sha512-OTaARARVZj2GvkJQjB+1jOIxntRaXea+G+fMsNqrZBAU1O1vJKDW22R7kECOHW27oJCLFN9HKaZeRrfAUyviug==", + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.10.2.tgz", + "integrity": "sha512-1msxllyhi/5m77JukXtp5UFnUAriwZIC1oJ7MTnpQpCwLTbclJi5BK5n28CTZuSXpQN2ewbbnqRgAhMM6c6ihg==", "license": "MIT", "dependencies": { "@algolia/autocomplete-core": "^1.19.2", "@docsearch/react": "^3.9.0 || ^4.3.2", - "@docusaurus/core": "3.10.1", - "@docusaurus/logger": "3.10.1", - "@docusaurus/plugin-content-docs": "3.10.1", - "@docusaurus/theme-common": "3.10.1", - "@docusaurus/theme-translations": "3.10.1", - "@docusaurus/utils": "3.10.1", - "@docusaurus/utils-validation": "3.10.1", + "@docusaurus/core": "3.10.2", + "@docusaurus/logger": "3.10.2", + "@docusaurus/plugin-content-docs": "3.10.2", + "@docusaurus/theme-common": "3.10.2", + "@docusaurus/theme-translations": "3.10.2", + "@docusaurus/utils": "3.10.2", + "@docusaurus/utils-validation": "3.10.2", "algoliasearch": "^5.37.0", "algoliasearch-helper": "^3.26.0", "clsx": "^2.0.0", @@ -3988,9 +4002,9 @@ } }, "node_modules/@docusaurus/theme-translations": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.10.1.tgz", - "integrity": "sha512-cLMyaKivjBVWKMJuWqyFVVgtqe8DPJNPkog0bn8W1MDVAKcPdxRFycBfC1We1RaNp7Rdk513bmtW78RR6OBxBw==", + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.10.2.tgz", + "integrity": "sha512-iv20wrxnyXkY89LM3TzRlzGlt5fIGO5UnaR6UL1ZVfB9RRFjxQFQ6awDrwAc6Km8Y5gD8pInuwYPF+6/TiCxXA==", "license": "MIT", "dependencies": { "fs-extra": "^11.1.1", @@ -4001,9 +4015,9 @@ } }, "node_modules/@docusaurus/types": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.10.1.tgz", - "integrity": "sha512-XYMK8k1szDCFMw2V+Xyen0g7Kee1sP3dtFnl7vkGkZOkeAJ/oPDQPL8iz4HBKOo/cwU8QeV6onVjMqtP+tFzsw==", + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.10.2.tgz", + "integrity": "sha512-B6rvfwIFSapUqUJjMriZswX13K8l5Z7AcmVE6uTEJpYddQieSTR12DsGaFtcZAIDsQd4p+0WTl0Vc6jmZK0Trw==", "license": "MIT", "dependencies": { "@mdx-js/mdx": "^3.0.0", @@ -4037,21 +4051,21 @@ } }, "node_modules/@docusaurus/utils": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.10.1.tgz", - "integrity": "sha512-3ojeJry9xBYdJO6qoyyzqeJFSJBVx2mXhyDzSdjwL2+URFQMf+h25gG38iswGImicK0ELjTd1EL2xzk8hf3QPw==", + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.10.2.tgz", + "integrity": "sha512-xx0W3eav2uW1NRIpuHJWNwLTC15xPNjU4Uxi9NSnd3swYC96BE3vFiT93SD8s24kmAAWNwgZwfZ2fghGZ01Lcw==", "license": "MIT", "dependencies": { - "@docusaurus/logger": "3.10.1", - "@docusaurus/types": "3.10.1", - "@docusaurus/utils-common": "3.10.1", + "@11ty/gray-matter": "^1.0.0", + "@docusaurus/logger": "3.10.2", + "@docusaurus/types": "3.10.2", + "@docusaurus/utils-common": "3.10.2", "escape-string-regexp": "^4.0.0", "execa": "^5.1.1", "file-loader": "^6.2.0", "fs-extra": "^11.1.1", "github-slugger": "^1.5.0", "globby": "^11.1.0", - "gray-matter": "^4.0.3", "jiti": "^1.20.0", "js-yaml": "^4.1.0", "lodash": "^4.17.21", @@ -4069,12 +4083,12 @@ } }, "node_modules/@docusaurus/utils-common": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.10.1.tgz", - "integrity": "sha512-5mFSgEADtnFxFH7RLw02QA5MpU5JVUCj0MPeIvi/aF4Fi45tQRIuTwXoXDqJ+1VfQJuYJGz3SI63wmGz4HvXzA==", + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.10.2.tgz", + "integrity": "sha512-x3Dz6jv6iQKBNjBmVTu8p57abMp/VNTUgKBMgRVXJc5444orBTsArv0+cdfrXTiz/VMmHfDRVkPbL7GH2B7T7w==", "license": "MIT", "dependencies": { - "@docusaurus/types": "3.10.1", + "@docusaurus/types": "3.10.2", "tslib": "^2.6.0" }, "engines": { @@ -4082,14 +4096,14 @@ } }, "node_modules/@docusaurus/utils-validation": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.10.1.tgz", - "integrity": "sha512-cRv1X69jwaWv47waglllgZVWzeBFLhl53XT/XED/83BerVBTC5FTP8WTcVl8Z6sZOegDSwitu/wpCSPCDOT6lg==", + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.10.2.tgz", + "integrity": "sha512-sn8unbDfUL585NtR3cwHefPicOyaHvPaX7VD0aOg/siIxUBoKyKKaGEqzJZDS64mM43TnxurkYDtmB1wsJlZsw==", "license": "MIT", "dependencies": { - "@docusaurus/logger": "3.10.1", - "@docusaurus/utils": "3.10.1", - "@docusaurus/utils-common": "3.10.1", + "@docusaurus/logger": "3.10.2", + "@docusaurus/utils": "3.10.2", + "@docusaurus/utils-common": "3.10.2", "fs-extra": "^11.2.0", "joi": "^17.9.2", "js-yaml": "^4.1.0", @@ -4248,13 +4262,13 @@ } }, "node_modules/@jsonjoy.com/fs-core": { - "version": "4.57.7", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-core/-/fs-core-4.57.7.tgz", - "integrity": "sha512-GDKuYHjP7vAI1kjBo73V+STKr9XIMZknW/xirpRW/EcShX0IKSev/ALafeRfC8Q331nodrXUFu04PugPB0MAhw==", + "version": "4.68.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-core/-/fs-core-4.68.0.tgz", + "integrity": "sha512-OAioDU3UGV34ECVTB4pt641OfUzuyDuyUmgrlVM+Fp9g4YyavZrB4xxWuupYrU66+81+um9D0DAzYhjXto5ysQ==", "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-node-builtins": "4.57.7", - "@jsonjoy.com/fs-node-utils": "4.57.7", + "@jsonjoy.com/fs-node-builtins": "4.68.0", + "@jsonjoy.com/fs-node-utils": "4.68.0", "thingies": "^2.5.0" }, "engines": { @@ -4269,14 +4283,14 @@ } }, "node_modules/@jsonjoy.com/fs-fsa": { - "version": "4.57.7", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-fsa/-/fs-fsa-4.57.7.tgz", - "integrity": "sha512-1rWsah2nZtRbNeP+c61QcfGfVrJXBmBD0Hm7Akvv4C9MKEasXnbiOS//iH3T3HwUSSBATGrfSp0Xi8nlNhATeQ==", + "version": "4.68.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-fsa/-/fs-fsa-4.68.0.tgz", + "integrity": "sha512-dC6VeW8uqXMF9Hkqb7qYST/t165XCHG+bNwwMzRetoNZRxJC8anr5XCJSAk5B+YLHGn6SP+noNltU6OFg/oa7A==", "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-core": "4.57.7", - "@jsonjoy.com/fs-node-builtins": "4.57.7", - "@jsonjoy.com/fs-node-utils": "4.57.7", + "@jsonjoy.com/fs-core": "4.68.0", + "@jsonjoy.com/fs-node-builtins": "4.68.0", + "@jsonjoy.com/fs-node-utils": "4.68.0", "thingies": "^2.5.0" }, "engines": { @@ -4291,16 +4305,16 @@ } }, "node_modules/@jsonjoy.com/fs-node": { - "version": "4.57.7", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node/-/fs-node-4.57.7.tgz", - "integrity": "sha512-xhnyeyEVTiIOibFvda/5n89nChMLCPKHHM2WQ+GGDf6+U/IrQBW3Qx6x+Uq1bkDbxBkybLOdIGoBtVBrE8Nngg==", + "version": "4.68.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node/-/fs-node-4.68.0.tgz", + "integrity": "sha512-xYDlpSk3UDHjcI0kiz6kNJ/oVTEzlS7kUzAoAsgJh2/+yOtYV/eZW7M8O9Fj+ED3BDONYKt7+8OiPlWl6xpFeA==", "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-core": "4.57.7", - "@jsonjoy.com/fs-node-builtins": "4.57.7", - "@jsonjoy.com/fs-node-utils": "4.57.7", - "@jsonjoy.com/fs-print": "4.57.7", - "@jsonjoy.com/fs-snapshot": "4.57.7", + "@jsonjoy.com/fs-core": "4.68.0", + "@jsonjoy.com/fs-node-builtins": "4.68.0", + "@jsonjoy.com/fs-node-utils": "4.68.0", + "@jsonjoy.com/fs-print": "4.68.0", + "@jsonjoy.com/fs-snapshot": "4.68.0", "glob-to-regex.js": "^1.0.0", "thingies": "^2.5.0" }, @@ -4316,9 +4330,9 @@ } }, "node_modules/@jsonjoy.com/fs-node-builtins": { - "version": "4.57.7", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-builtins/-/fs-node-builtins-4.57.7.tgz", - "integrity": "sha512-LWqfY1m+uAosjwM1RrKhMkUnP9jcq1RUczHsNO779ovm1E9v8I/pmj04eBAcoBjhC7ltcPbNFGyRJ5JqSJ7Jdg==", + "version": "4.68.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-builtins/-/fs-node-builtins-4.68.0.tgz", + "integrity": "sha512-j3bb+k4NuqpqXQzinCLeagS7LGLCUvPqA6TnpaBkBqhnPjbzETYiQFpD7pWaV8cGOhhZcAv9dmpzk5mxNb1qsg==", "license": "Apache-2.0", "engines": { "node": ">=10.0" @@ -4332,14 +4346,14 @@ } }, "node_modules/@jsonjoy.com/fs-node-to-fsa": { - "version": "4.57.7", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-to-fsa/-/fs-node-to-fsa-4.57.7.tgz", - "integrity": "sha512-9T0zC9LKcAWXDoTLRdLMoJ0seOvJ5bgDKq1tSBoQAFQpPDstQUeV1Oe7PLypdu7F2D3ddRstmwgeNUEN/VaZ4Q==", + "version": "4.68.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-to-fsa/-/fs-node-to-fsa-4.68.0.tgz", + "integrity": "sha512-dtLpmLQxw3IBcGxoK9tVvftwmQLl9qmQyivt8DZpeEwDUcQkxk2CLMZERXtmOlF1mWXfT9CqtjjMiswkb03rKw==", "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-fsa": "4.57.7", - "@jsonjoy.com/fs-node-builtins": "4.57.7", - "@jsonjoy.com/fs-node-utils": "4.57.7" + "@jsonjoy.com/fs-fsa": "4.68.0", + "@jsonjoy.com/fs-node-builtins": "4.68.0", + "@jsonjoy.com/fs-node-utils": "4.68.0" }, "engines": { "node": ">=10.0" @@ -4353,12 +4367,13 @@ } }, "node_modules/@jsonjoy.com/fs-node-utils": { - "version": "4.57.7", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-utils/-/fs-node-utils-4.57.7.tgz", - "integrity": "sha512-jjWSDOsfcog2cZnUCwX5AHmlIq6b6wx5Pz/2LAcNjJ62Rajwg89Fy7ubN+lDHew0/1reLDa9Z5urybYadhh37g==", + "version": "4.68.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-utils/-/fs-node-utils-4.68.0.tgz", + "integrity": "sha512-Q84pogSfRaz0WNrBBy+HzBmhNu4m6tWoM0po8bLwW4pKvrwrSadOgFpm1sku08sfg0QAtUubkWuZEBDpNI3toQ==", "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-node-builtins": "4.57.7" + "@jsonjoy.com/fs-node-builtins": "4.68.0", + "glob-to-regex.js": "^1.0.1" }, "engines": { "node": ">=10.0" @@ -4372,12 +4387,12 @@ } }, "node_modules/@jsonjoy.com/fs-print": { - "version": "4.57.7", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-print/-/fs-print-4.57.7.tgz", - "integrity": "sha512-mFM4P4Gjq0QQHkLnXzPYPEMFrAoe6a5Myedgb6+CmL+nGd3MKvTxYPuD7N1dLIH9RBy1fLdzxd80qvuK8xrx3Q==", + "version": "4.68.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-print/-/fs-print-4.68.0.tgz", + "integrity": "sha512-sdy9F6N9QEcyAcU8OxEQmB91mlgiyuMphTFeijQ+qFgigCOeT16lW3X1dCWNzD7wwKLOKXnnYqsFyeq4LWSlYQ==", "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-node-utils": "4.57.7", + "@jsonjoy.com/fs-node-utils": "4.68.0", "tree-dump": "^1.1.0" }, "engines": { @@ -4392,13 +4407,13 @@ } }, "node_modules/@jsonjoy.com/fs-snapshot": { - "version": "4.57.7", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-snapshot/-/fs-snapshot-4.57.7.tgz", - "integrity": "sha512-1GS3+plfm2giB3PqokiqyydyqYTPLcCQIKSkp0TdMNRh3KVk7rqRM6U785FLlVRG7XLmkc0KWr215OY+22K3QA==", + "version": "4.68.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-snapshot/-/fs-snapshot-4.68.0.tgz", + "integrity": "sha512-eV44t9KY9LH47aPR1a4Nv4xYxM1vQ+OKEw9Mx/3zkuxGOILwmtFddlh5HakdMFpFHwuFNPw1s+NGq2eVYFpQRA==", "license": "Apache-2.0", "dependencies": { "@jsonjoy.com/buffers": "^17.65.0", - "@jsonjoy.com/fs-node-utils": "4.57.7", + "@jsonjoy.com/fs-node-utils": "4.68.0", "@jsonjoy.com/json-pack": "^17.65.0", "@jsonjoy.com/util": "^17.65.0" }, @@ -4941,9 +4956,9 @@ "license": "BSD-3-Clause" }, "node_modules/@sinclair/typebox": { - "version": "0.27.10", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", - "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", + "version": "0.27.12", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.12.tgz", + "integrity": "sha512-hhyNJ+nbR6ZR7pToHvllEFun9TL0sbL+tk/ON75lo+Xas054uez98qRbsuNt7MBCyZKK4+8Yli/OAGZhmfBZ/g==", "license": "MIT" }, "node_modules/@sindresorhus/is": { @@ -5313,9 +5328,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "4.19.8", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.8.tgz", - "integrity": "sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA==", + "version": "4.19.9", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.9.tgz", + "integrity": "sha512-QP2ESEe/ImWY0HDwNAnK9PvEffUyhLTnWkk7KXzHfyeWAnlrDe1fN77bXl6ia8KT3wPlmA7t9/VPRpnf4Ex9sg==", "license": "MIT", "dependencies": { "@types/node": "*", @@ -5324,16 +5339,10 @@ "@types/send": "*" } }, - "node_modules/@types/gtag.js": { - "version": "0.0.20", - "resolved": "https://registry.npmjs.org/@types/gtag.js/-/gtag.js-0.0.20.tgz", - "integrity": "sha512-wwAbk3SA2QeU67unN7zPxjEHmPmlXwZXZvQEpbEUQuMCRGgKyE1m6XDuTUA9b6pCGb/GqJmdfMOY5LuDjJSbbg==", - "license": "MIT" - }, "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz", + "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==", "license": "MIT", "dependencies": { "@types/unist": "*" @@ -5430,12 +5439,12 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "25.9.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.3.tgz", - "integrity": "sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==", + "version": "26.1.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.2.tgz", + "integrity": "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==", "license": "MIT", "dependencies": { - "undici-types": ">=7.24.0 <7.24.7" + "undici-types": "~8.3.0" } }, "node_modules/@types/prismjs": { @@ -5457,9 +5466,9 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "19.2.17", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", - "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", + "version": "19.2.18", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz", + "integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==", "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -5591,9 +5600,9 @@ "license": "MIT" }, "node_modules/@ungap/structured-clone": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", - "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.3.tgz", + "integrity": "sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==", "license": "ISC" }, "node_modules/@webassemblyjs/ast": { @@ -5798,9 +5807,9 @@ } }, "node_modules/acorn": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", - "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -5809,18 +5818,6 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-import-phases": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", - "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", - "license": "MIT", - "engines": { - "node": ">=10.13.0" - }, - "peerDependencies": { - "acorn": "^8.14.0" - } - }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -5843,12 +5840,12 @@ } }, "node_modules/address": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz", - "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/address/-/address-2.0.3.tgz", + "integrity": "sha512-XNAb/a6TCqou+TufU8/u11HCu9x1gYvOoxLwtlXgIqmkrYQADVv6ljyW2zwiPhHz9R1gItAWpuDrdJMmrOBFEA==", "license": "MIT", "engines": { - "node": ">= 10.0.0" + "node": ">= 16.0.0" } }, "node_modules/aggregate-error": { @@ -5910,34 +5907,34 @@ } }, "node_modules/algoliasearch": { - "version": "5.54.0", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.54.0.tgz", - "integrity": "sha512-APAX4ajIOgsmYoUlGe++oNZkSTBgmXYM4maHC0OxC+Yo7xkaKQElV0ATZYCZA7jzrSJX1OBiqEs7mk+ZxXgYqA==", - "license": "MIT", - "dependencies": { - "@algolia/abtesting": "1.20.0", - "@algolia/client-abtesting": "5.54.0", - "@algolia/client-analytics": "5.54.0", - "@algolia/client-common": "5.54.0", - "@algolia/client-insights": "5.54.0", - "@algolia/client-personalization": "5.54.0", - "@algolia/client-query-suggestions": "5.54.0", - "@algolia/client-search": "5.54.0", - "@algolia/ingestion": "1.54.0", - "@algolia/monitoring": "1.54.0", - "@algolia/recommend": "5.54.0", - "@algolia/requester-browser-xhr": "5.54.0", - "@algolia/requester-fetch": "5.54.0", - "@algolia/requester-node-http": "5.54.0" + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.56.0.tgz", + "integrity": "sha512-PrqppUmhT4ENdas2pH9caE7efUcxy6EcSFhWzosiVuQBzu2tQ5yLTI6jwomT/1cuBnivzGfxiJCqDNN9FRRh+Q==", + "license": "MIT", + "dependencies": { + "@algolia/abtesting": "1.22.0", + "@algolia/client-abtesting": "5.56.0", + "@algolia/client-analytics": "5.56.0", + "@algolia/client-common": "5.56.0", + "@algolia/client-insights": "5.56.0", + "@algolia/client-personalization": "5.56.0", + "@algolia/client-query-suggestions": "5.56.0", + "@algolia/client-search": "5.56.0", + "@algolia/ingestion": "1.56.0", + "@algolia/monitoring": "1.56.0", + "@algolia/recommend": "5.56.0", + "@algolia/requester-browser-xhr": "5.56.0", + "@algolia/requester-fetch": "5.56.0", + "@algolia/requester-node-http": "5.56.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/algoliasearch-helper": { - "version": "3.29.1", - "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.29.1.tgz", - "integrity": "sha512-6ck2YFudF2Pje7szQoPBiRFTGfd+1I+0I/WfLPGn0bj1kvrFoOQmNyedNiDxTk3/r4IfSLDYk+RA4G7u8H6+yA==", + "version": "3.29.2", + "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.29.2.tgz", + "integrity": "sha512-SaV+rZM3drExb0punEYYjT+sNcH74YFwN8ocjya7IDOyQvKWeQpEaSMVG3+IGTVos+feuatj7ljQ4BXlXdUp3w==", "license": "MIT", "dependencies": { "@algolia/events": "^4.0.1" @@ -6084,9 +6081,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.0.tgz", - "integrity": "sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong==", + "version": "10.5.4", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.4.tgz", + "integrity": "sha512-MaU0U/za7N3r6brxD4YB/l4NSrFzLPlANv6wEuQVaIPlD3L4W9rFcQPbL/EilY9BHhHvhfcz3gInDLrEtWT4EA==", "funding": [ { "type": "opencollective", @@ -6103,8 +6100,8 @@ ], "license": "MIT", "dependencies": { - "browserslist": "^4.28.2", - "caniuse-lite": "^1.0.30001787", + "browserslist": "^4.28.6", + "caniuse-lite": "^1.0.30001806", "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" @@ -6210,9 +6207,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.10.36", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.36.tgz", - "integrity": "sha512-lVq/Df7LXlO79MVaaUHztSwWiG9oXoWHlgvNS51v8Dpd4+G4/VIy6qYePTw31nAVls33nUtnfezYeLkYAak9dg==", + "version": "2.11.12", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.12.tgz", + "integrity": "sha512-r7WnVImvVCeFpf2DOXfy41aPWzeNg3H/A2X4dKmy1QL0MSyyk/e7z8ihJ3N6Nn2PsdhkVlqnEfnUE4a05P2aTA==", "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.cjs" @@ -6249,9 +6246,9 @@ } }, "node_modules/body-parser": { - "version": "1.20.5", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", - "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==", + "version": "1.20.6", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.6.tgz", + "integrity": "sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==", "license": "MIT", "dependencies": { "bytes": "~3.1.2", @@ -6297,9 +6294,9 @@ "license": "MIT" }, "node_modules/bonjour-service": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.4.1.tgz", - "integrity": "sha512-9KM4QMPKnaJqaja1v7gYO/+TXZGLtzPA05NmUTqDAJjcsWeVoOXKMvU9g0gfuuoYTQqJZ924hivICd5R/bCJbA==", + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.4.4.tgz", + "integrity": "sha512-jCZcVv7eoc4QesRscwEZtSROBen+6LpKAmBIsQYQrsAeVHLyMXWX/t6eIV5KiRZYNUBl8eVqImEEMQ8L5+c/Kw==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -6335,9 +6332,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", - "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -6357,9 +6354,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", - "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "version": "4.28.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.7.tgz", + "integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==", "funding": [ { "type": "opencollective", @@ -6376,10 +6373,10 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.10.12", - "caniuse-lite": "^1.0.30001782", - "electron-to-chromium": "^1.5.328", - "node-releases": "^2.0.36", + "baseline-browser-mapping": "^2.10.44", + "caniuse-lite": "^1.0.30001806", + "electron-to-chromium": "^1.5.393", + "node-releases": "^2.0.51", "update-browserslist-db": "^1.2.3" }, "bin": { @@ -6546,9 +6543,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001799", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz", - "integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==", + "version": "1.0.30001806", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", + "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==", "funding": [ { "type": "opencollective", @@ -7141,23 +7138,29 @@ } }, "node_modules/core-js": { - "version": "3.49.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.49.0.tgz", - "integrity": "sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==", + "version": "3.50.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.50.0.tgz", + "integrity": "sha512-BRWgOLKkFeCgRudR6zrs8p9XJZcE14grzKMMssoYrk6krtuEZ7MTKPIY5RzOnqsEKIR9kst7wNzphttraT+Yqw==", "hasInstallScript": true, "license": "MIT", + "engines": { + "node": "*" + }, "funding": { "type": "opencollective", "url": "https://opencollective.com/core-js" } }, "node_modules/core-js-compat": { - "version": "3.49.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.49.0.tgz", - "integrity": "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==", + "version": "3.50.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.50.0.tgz", + "integrity": "sha512-XGpFGbMLHwSt74YLTKho7Ib242qi6O8MSX+sRokV4oz7iKXvQWGYZthjIhjRGMxjzVkAubBO512dKGYcefmX3Q==", "license": "MIT", "dependencies": { - "browserslist": "^4.28.1" + "browserslist": "^4.28.7" + }, + "engines": { + "node": ">=6.4.0" }, "funding": { "type": "opencollective", @@ -7851,20 +7854,19 @@ "license": "MIT" }, "node_modules/detect-port": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.6.1.tgz", - "integrity": "sha512-CmnVc+Hek2egPx1PeTFVta2W78xy2K/9Rkf6cC4T59S50tVnzKj+tnx5mmx5lwvCkujZ4uRrpRSuV+IVs3f90Q==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-2.1.0.tgz", + "integrity": "sha512-epZuWb/6Q62L+nDHJc/hQAqf8pylsqgk3BpZXVBx1CDnr3nkrVNn73Uu1rXcFzkNcc+hkP3whuOg7JZYaQB65Q==", "license": "MIT", "dependencies": { - "address": "^1.0.1", - "debug": "4" + "address": "^2.0.1" }, "bin": { - "detect": "bin/detect-port.js", - "detect-port": "bin/detect-port.js" + "detect": "dist/commonjs/bin/detect-port.js", + "detect-port": "dist/commonjs/bin/detect-port.js" }, "engines": { - "node": ">= 4.0.0" + "node": ">= 16.0.0" } }, "node_modules/devlop": { @@ -8035,9 +8037,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.372", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.372.tgz", - "integrity": "sha512-M3yhbAlilnwqC8D21t28UCDGHyitShTmmLRU/H+b74P6Ski16Nb9HONYEaVpMj/pwC7BEo5B95FpjODLCWbtfA==", + "version": "1.5.402", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.402.tgz", + "integrity": "sha512-/oOpMaPT6Yg+6/1XQhyIPlzgj7Ye9zf+nNM2Uh6OcE2G2oNptWazFa+qB2Pdqqbsc9KnIDzgAntoYN0dbwOXwA==", "license": "ISC" }, "node_modules/emoji-regex": { @@ -8081,9 +8083,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.24.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.0.tgz", - "integrity": "sha512-SkE2t82KlkkxQRVMVLAGKxLfORGQfrkx5dkj+vlgXRVNEdPc4eZcR+J/Fvj8C+yKSFH5L0q3NFlyufOVQnCcYQ==", + "version": "5.24.5", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.5.tgz", + "integrity": "sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==", "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -8133,9 +8135,9 @@ } }, "node_modules/es-module-lexer": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", - "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", + "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==", "license": "MIT" }, "node_modules/es-object-atoms": { @@ -8234,19 +8236,6 @@ "node": ">=8.0.0" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", @@ -8595,9 +8584,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", - "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz", + "integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==", "funding": [ { "type": "github", @@ -8880,9 +8869,9 @@ } }, "node_modules/fs-extra": { - "version": "11.3.5", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.5.tgz", - "integrity": "sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==", + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.4.0.tgz", + "integrity": "sha512-EQsFzMUJkCKGr1ePqlYADkIUmHW1s3ZXr5Yqy6wbGrfUCphpl2maM/kyOIRA2HpP3AaFQTZXD4ldjek+nccddA==", "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", @@ -9014,12 +9003,6 @@ "tslib": "2" } }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "license": "BSD-2-Clause" - }, "node_modules/global-dirs": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", @@ -9110,43 +9093,6 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, - "node_modules/gray-matter": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", - "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", - "license": "MIT", - "dependencies": { - "js-yaml": "^3.13.1", - "kind-of": "^6.0.2", - "section-matter": "^1.0.0", - "strip-bom-string": "^1.0.0" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/gray-matter/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/gray-matter/node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/gzip-size": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", @@ -9526,9 +9472,9 @@ } }, "node_modules/html-webpack-plugin": { - "version": "5.6.7", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.7.tgz", - "integrity": "sha512-md+vXtdCAe60s1k6AU3dUyMJnDxUyQAwfwPKoLisvgUF1IXjtlLsk2se54+qfL9Mdm26bbwvjJybpNx48NKRLw==", + "version": "5.6.8", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.8.tgz", + "integrity": "sha512-MZmKQcTnhEh1SPSyMiEytIeDZDUoBZVorNHivQGXMASHf/BSGGOrKa2xQ5bGx3TCe1n109ecCt+cpww7wwWhKA==", "license": "MIT", "dependencies": { "@types/html-minifier-terser": "^6.0.0", @@ -9545,7 +9491,7 @@ "url": "https://opencollective.com/html-webpack-plugin" }, "peerDependencies": { - "@rspack/core": "0.x || 1.x", + "@rspack/core": "0.x || 1.x || 2.x", "webpack": "^5.20.0" }, "peerDependenciesMeta": { @@ -9659,9 +9605,9 @@ } }, "node_modules/http-proxy-middleware": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz", - "integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==", + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.10.tgz", + "integrity": "sha512-RKzRWNPxUZqbuk3BC5mGVJbBnWgr+diEnjJexIOytFbBzDy88Fbh/YvBr3DsNrl1jYAfjWfpATEv0NO35FDuPQ==", "license": "MIT", "dependencies": { "@types/http-proxy": "^1.17.8", @@ -9853,9 +9799,9 @@ } }, "node_modules/ipaddr.js": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.4.0.tgz", - "integrity": "sha512-9VGk3HGanVE6JoZXHiCpnGy5X0jYDnN4EA4lntFPj+1vIWlFhIylq2CrrCOJH9EAhc5CYhq18F2Av2tgoAPsYQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.5.0.tgz", + "integrity": "sha512-aq+t5NAc+cS6rZQQVWC2x98CPqGtKKTMDd4Gaodv0wShnItdKg/51djkGJ1hqH+Oy0ivDftCbSLCQob8zso01w==", "license": "MIT", "engines": { "node": ">= 10" @@ -10273,9 +10219,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", - "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", "funding": [ { "type": "github", @@ -10427,19 +10373,6 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "license": "MIT" }, - "node_modules/loader-runner": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.2.tgz", - "integrity": "sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==", - "license": "MIT", - "engines": { - "node": ">=6.11.5" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/loader-utils": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", @@ -10994,19 +10927,19 @@ } }, "node_modules/memfs": { - "version": "4.57.7", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.57.7.tgz", - "integrity": "sha512-YZPphUQZSRGk6ddPlsNuMbztrLwsbUATFNZcqKscSbSJZ4g0+Y3vSZLJ/rfnGZaB1FFhC7SrywZXev6i8lnHgg==", + "version": "4.68.0", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.68.0.tgz", + "integrity": "sha512-qlU8XrIfXUuZcmXk/GODL47KXXnhSbbaWtW5z+L7cLFfIQbgAOWzL5lqUYn39kfJh+BHLaD/siaM94/UTweGow==", "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-core": "4.57.7", - "@jsonjoy.com/fs-fsa": "4.57.7", - "@jsonjoy.com/fs-node": "4.57.7", - "@jsonjoy.com/fs-node-builtins": "4.57.7", - "@jsonjoy.com/fs-node-to-fsa": "4.57.7", - "@jsonjoy.com/fs-node-utils": "4.57.7", - "@jsonjoy.com/fs-print": "4.57.7", - "@jsonjoy.com/fs-snapshot": "4.57.7", + "@jsonjoy.com/fs-core": "4.68.0", + "@jsonjoy.com/fs-fsa": "4.68.0", + "@jsonjoy.com/fs-node": "4.68.0", + "@jsonjoy.com/fs-node-builtins": "4.68.0", + "@jsonjoy.com/fs-node-to-fsa": "4.68.0", + "@jsonjoy.com/fs-node-utils": "4.68.0", + "@jsonjoy.com/fs-print": "4.68.0", + "@jsonjoy.com/fs-snapshot": "4.68.0", "@jsonjoy.com/json-pack": "^1.11.0", "@jsonjoy.com/util": "^1.9.0", "glob-to-regex.js": "^1.0.1", @@ -11017,9 +10950,6 @@ "funding": { "type": "github", "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" } }, "node_modules/merge-descriptors": { @@ -12953,6 +12883,95 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minimizer-webpack-plugin": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/minimizer-webpack-plugin/-/minimizer-webpack-plugin-5.6.1.tgz", + "integrity": "sha512-DoeAZz8Q1C1znwsUzej1fdoi4jCf7/+Em27ouLqfK/+3m8G+D7yDhUwrc3CNhjSzGUN1kn7Iv4sWmjflQHenpw==", + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@minify-html/node": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "@swc/css": { + "optional": true + }, + "@swc/html": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "cssnano": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "html-minifier-terser": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "postcss": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/minimizer-webpack-plugin/node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/minimizer-webpack-plugin/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/mrmime": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", @@ -12982,9 +13001,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", - "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "version": "3.3.17", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.17.tgz", + "integrity": "sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g==", "funding": [ { "type": "github", @@ -13040,9 +13059,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.47", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.47.tgz", - "integrity": "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==", + "version": "2.0.52", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.52.tgz", + "integrity": "sha512-MRlTqhAfoMx/4mhEbPo3Hi02g9LJZaJkka69V6h67Cb1gjrAG0jsTE4CZX1eptNx+VCAwJmfpnDIF4P0Nh1A7A==", "license": "MIT", "engines": { "node": ">=18" @@ -13638,9 +13657,9 @@ } }, "node_modules/postcss": { - "version": "8.5.15", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", - "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "version": "8.5.25", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz", + "integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==", "funding": [ { "type": "opencollective", @@ -13657,7 +13676,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.12", + "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -15243,21 +15262,22 @@ } }, "node_modules/pvutils": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.5.tgz", - "integrity": "sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.2.0.tgz", + "integrity": "sha512-BbubeCEyTuQjVMakvJQ/Sxbc93F2pwmbsxONT/ZRrwU7Ua38d8unYTwXpTVLAKJ4BDuH9IGztCjQcd/N/39Dvg==", "license": "MIT", "engines": { "node": ">=16.0.0" } }, "node_modules/qs": { - "version": "6.15.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", - "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", + "version": "6.15.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.1.0" + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" }, "engines": { "node": ">=0.6" @@ -15371,24 +15391,24 @@ } }, "node_modules/react": { - "version": "19.2.7", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", - "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==", + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", + "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "19.2.7", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz", - "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==", + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz", + "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==", "license": "MIT", "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^19.2.7" + "react": "^19.2.8" } }, "node_modules/react-fast-compare": { @@ -15681,9 +15701,9 @@ "license": "MIT" }, "node_modules/regjsparser": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.1.tgz", - "integrity": "sha512-dLsljMd9sqwRkby8zhO1gSg3PnJIBFid8f4CQj/sXx+7cKx+E7u0PKhZ+U4wmhx7EfmtvnA318oVaIkAB1lRJw==", + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.2.tgz", + "integrity": "sha512-NgRBy2Nx/bE+9F27nVHnqcN5HjyLmecqsqx2PJHu3/IEtADD4WuxuXIVExD5PoSDFVrl78dOonfcOe5O+5nbzQ==", "license": "BSD-2-Clause", "dependencies": { "jsesc": "~3.1.0" @@ -16138,9 +16158,9 @@ "license": "MIT" }, "node_modules/sax": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", - "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz", + "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==", "license": "BlueOak-1.0.0", "engines": { "node": ">=11.0.0" @@ -16217,9 +16237,9 @@ } }, "node_modules/semver": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", - "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -16491,9 +16511,9 @@ } }, "node_modules/shell-quote": { - "version": "1.8.4", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.4.tgz", - "integrity": "sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.10.0.tgz", + "integrity": "sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -16753,12 +16773,6 @@ "wbuf": "^1.7.3" } }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "license": "BSD-3-Clause" - }, "node_modules/srcset": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/srcset/-/srcset-4.0.0.tgz", @@ -16974,9 +16988,9 @@ "license": "MIT" }, "node_modules/svgo": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.3.tgz", - "integrity": "sha512-+wn7I4p7YgJhHs38k2TNjy1vCfPIfLIJWR5MnCStsN8WuuTcBnRKcMHQLMM2ijxGZmDoZwNv8ipl5aTTen62ng==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.4.tgz", + "integrity": "sha512-GsNRis4e8jxn2Y9ENz/8lbJ93CstG8svtMnuRaHbiF2LTJ5tK0/q3t/URPq9Zc7zVWBJnNnJMIp6bevK7bSmNg==", "license": "MIT", "dependencies": { "commander": "^7.2.0", @@ -17021,9 +17035,9 @@ } }, "node_modules/terser": { - "version": "5.48.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.48.0.tgz", - "integrity": "sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==", + "version": "5.49.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.49.2.tgz", + "integrity": "sha512-rGbJiKeQ4WDe3EXlDAIaQcwftVfv2Q8o1awFNfvXolJYKkb1AuZY1RTOmqx4LJXZENbWZA7eIsYGHuEzHsi1nQ==", "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -17134,9 +17148,9 @@ "license": "MIT" }, "node_modules/thingies": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/thingies/-/thingies-2.6.0.tgz", - "integrity": "sha512-rMHRjmlFLM1R96UYPvpmnc3LYtdFrT33JIB7L9hetGue1qAPfn1N2LJeEjxUSidu1Iku+haLZXDuEXUHNGO/lg==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/thingies/-/thingies-2.6.1.tgz", + "integrity": "sha512-cV/CMGTK3M4MlnJ/0At6ismOw/A0EEniDNScajjz/Br3c1sqE72YD01rGpPTKwd27wAxI5Pr+6+0w8yofzFRYw==", "license": "MIT", "engines": { "node": ">=10.18" @@ -17322,9 +17336,9 @@ } }, "node_modules/undici-types": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", - "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", + "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { @@ -17848,9 +17862,9 @@ } }, "node_modules/webpack": { - "version": "5.107.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.107.2.tgz", - "integrity": "sha512-v7RhXaJbpMlV0D7hC7lb2EbnxkoeUqf9qhKr6lozx3Q48pmFrqqNRmZFUEGmi7pSwm6fCQ2H1IjvCkHqdpVdjQ==", + "version": "5.109.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.109.2.tgz", + "integrity": "sha512-U9/cvLzxObKNEZ9+TtdqrHM5/9z3lgl2c+c4BzbqGxFQvQvBAq87yql5A8pQ+rrMbS496MZJeF5enVBndIy2hw==", "license": "MIT", "dependencies": { "@types/estree": "^1.0.8", @@ -17859,23 +17873,20 @@ "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", "acorn": "^8.16.0", - "acorn-import-phases": "^1.0.3", "browserslist": "^4.28.1", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.22.0", + "enhanced-resolve": "^5.24.4", "es-module-lexer": "^2.1.0", "eslint-scope": "5.1.1", "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.11", - "loader-runner": "^4.3.2", "mime-db": "^1.54.0", + "minimizer-webpack-plugin": "^5.6.1", "neo-async": "^2.6.2", "schema-utils": "^4.3.3", "tapable": "^2.3.0", - "terser-webpack-plugin": "^5.5.0", - "watchpack": "^2.5.1", - "webpack-sources": "^3.5.0" + "watchpack": "^2.5.2", + "webpack-sources": "^3.5.1" }, "bin": { "webpack": "bin/webpack.js" @@ -17983,18 +17994,22 @@ } }, "node_modules/webpack-dev-middleware/node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", + "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", "license": "MIT", "engines": { "node": ">= 0.6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/webpack-dev-server": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.4.tgz", - "integrity": "sha512-GqDPGZN9bRqKBTkp4aWkobDDHMsrXKoGSdOH56smIri8qR0JG8gfL8/v/f/OZR3/OKXjG8uwJbFVhKm/FNU/UA==", + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.6.tgz", + "integrity": "sha512-HNLRmamRvVavZQ+avceZifmv8hmdUjg43t6MI4SqJDwFdW7RPQwH5vzGhDRZSX59SgfbeHhLnq3g+uooWo7pVw==", "license": "MIT", "dependencies": { "@types/bonjour": "^3.5.13", @@ -18015,7 +18030,7 @@ "graceful-fs": "^4.2.6", "http-proxy-middleware": "^2.0.9", "ipaddr.js": "^2.1.0", - "launch-editor": "^2.6.1", + "launch-editor": "^2.14.1", "open": "^10.0.3", "p-retry": "^6.2.0", "schema-utils": "^4.2.0", @@ -18079,9 +18094,9 @@ } }, "node_modules/webpack-dev-server/node_modules/ws": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", - "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "version": "8.21.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.2.tgz", + "integrity": "sha512-54dMVAo4WIe6SKy3vBgN+9bJZqqQ8IMRevAkOLQALhi49qkkQDQfWdAZ8KQlXiEabw88ARXXdUrlvtbKQX+aKw==", "license": "MIT", "engines": { "node": ">=10.0.0" @@ -18114,9 +18129,9 @@ } }, "node_modules/webpack-sources": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.5.0.tgz", - "integrity": "sha512-HPuy+uuoTCaaoEoI1LQ3JN9+vrPBvEesnnX1jADHy728cHSMlq4wUc4afYqahq2B1mhQVZxCXOkNTnXltr+2vQ==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.5.1.tgz", + "integrity": "sha512-jyuiGJdtvY434z5bUZrjz67v76/ePNvFZTp9Mdz29IlH4+GPsgyGjiv0fKI+M7BdkU6ADjulUcKAd3tUK3WlEw==", "license": "MIT", "engines": { "node": ">=10.13.0" @@ -18286,9 +18301,9 @@ } }, "node_modules/ws": { - "version": "7.5.11", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.11.tgz", - "integrity": "sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA==", + "version": "7.5.13", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.13.tgz", + "integrity": "sha512-rsKI6xDBFVf4r/x8XyChGK04QR/XHroxs/jUcoWvtEZM8TPU/X/uIY9B1CsSzYws9ZJb/6bbBu7dPhFW00CAoA==", "license": "MIT", "engines": { "node": ">=8.3.0" diff --git a/docs/package.json b/docs/package.json index 16e14251..60ba7193 100644 --- a/docs/package.json +++ b/docs/package.json @@ -14,18 +14,18 @@ "write-heading-ids": "docusaurus write-heading-ids" }, "dependencies": { - "@docusaurus/core": "^3.10.1", - "@docusaurus/preset-classic": "^3.10.1", - "@docusaurus/theme-common": "^3.10.1", + "@docusaurus/core": "^3.10.2", + "@docusaurus/preset-classic": "^3.10.2", + "@docusaurus/theme-common": "^3.10.2", "@mdx-js/react": "^3.1.1", "clsx": "^2.1.1", "prism-react-renderer": "^2.4.1", - "react": "^19.2.7", - "react-dom": "^19.2.7" + "react": "^19.2.8", + "react-dom": "^19.2.8" }, "devDependencies": { - "@docusaurus/module-type-aliases": "^3.10.1", - "@docusaurus/types": "^3.10.1" + "@docusaurus/module-type-aliases": "^3.10.2", + "@docusaurus/types": "^3.10.2" }, "browserslist": { "production": [ diff --git a/domain/domain-events/domain-events-api.gradle b/domain/domain-events/domain-events-api.gradle index 61da0eed..2539fd96 100644 --- a/domain/domain-events/domain-events-api.gradle +++ b/domain/domain-events/domain-events-api.gradle @@ -5,5 +5,5 @@ ext { dependencies { api 'org.reactivestreams:reactive-streams:1.0.4' - api 'io.cloudevents:cloudevents-api:4.1.1' + api 'io.cloudevents:cloudevents-api:5.0.0' } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index df6a6ad7..a9db1155 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.1-bin.zip networkTimeout=10000 retries=0 retryBackOffMs=500 diff --git a/gradlew b/gradlew index b9bb139f..249efbb0 100755 --- a/gradlew +++ b/gradlew @@ -20,7 +20,7 @@ ############################################################################## # -# Gradle start up script for POSIX generated by Gradle. +# gradlew start up script for POSIX generated by Gradle. # # Important for running: # @@ -29,7 +29,7 @@ # bash, then to run this script, type that shell name before the whole # command line, like: # -# ksh Gradle +# ksh gradlew # # Busybox and similar reduced shells will NOT work, because this script # requires all of these POSIX shell features: diff --git a/gradlew.bat b/gradlew.bat index aa5f10b0..8508ef68 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -19,7 +19,7 @@ @if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem -@rem Gradle startup script for Windows +@rem gradlew startup script for Windows @rem @rem ########################################################################## @@ -72,7 +72,7 @@ echo location of your Java installation. 1>&2 -@rem Execute Gradle +@rem Execute gradlew @rem endlocal doesn't take effect until after the line is parsed and variables are expanded @rem which allows us to clear the local environment before executing the java command endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel diff --git a/main.gradle b/main.gradle index b174b5be..5e3918ae 100644 --- a/main.gradle +++ b/main.gradle @@ -183,6 +183,6 @@ tasks.register('generateMergedReport', JacocoReport) { } tasks.named('wrapper') { - gradleVersion = '9.5.1' + gradleVersion = '9.6.1' validateDistributionUrl = true } \ No newline at end of file diff --git a/samples/async/async-kafka-sender-client/async-kafka-sender-client.gradle b/samples/async/async-kafka-sender-client/async-kafka-sender-client.gradle index 9afc5b5e..db2432f3 100644 --- a/samples/async/async-kafka-sender-client/async-kafka-sender-client.gradle +++ b/samples/async/async-kafka-sender-client/async-kafka-sender-client.gradle @@ -4,7 +4,7 @@ dependencies { implementation project(':shared') implementation project(':async-kafka-starter') implementation project(':cloudevents-json-jackson') - implementation 'io.cloudevents:cloudevents-core:4.1.1' + implementation 'io.cloudevents:cloudevents-core:5.0.0' implementation 'org.springframework.boot:spring-boot-starter-webflux' implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'io.micrometer:micrometer-registry-prometheus' diff --git a/samples/async/eda-async-receiver-responder/eda-async-receiver-sample.gradle b/samples/async/eda-async-receiver-responder/eda-async-receiver-sample.gradle index 2e7221e4..c096bac5 100644 --- a/samples/async/eda-async-receiver-responder/eda-async-receiver-sample.gradle +++ b/samples/async/eda-async-receiver-responder/eda-async-receiver-sample.gradle @@ -4,5 +4,5 @@ dependencies { implementation project(":shared") implementation project(":async-commons-rabbit-starter") implementation 'org.springframework.boot:spring-boot-starter' - implementation 'io.cloudevents:cloudevents-core:4.1.1' + implementation 'io.cloudevents:cloudevents-core:5.0.0' } \ No newline at end of file diff --git a/samples/async/eda-async-sender-client-domain-a/eda-async-sender-client-domain-a.gradle b/samples/async/eda-async-sender-client-domain-a/eda-async-sender-client-domain-a.gradle index 94f7d779..fc2476b8 100644 --- a/samples/async/eda-async-sender-client-domain-a/eda-async-sender-client-domain-a.gradle +++ b/samples/async/eda-async-sender-client-domain-a/eda-async-sender-client-domain-a.gradle @@ -6,6 +6,6 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-webflux' implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'io.micrometer:micrometer-registry-prometheus' - implementation 'io.cloudevents:cloudevents-core:4.1.1' + implementation 'io.cloudevents:cloudevents-core:5.0.0' } \ No newline at end of file diff --git a/samples/async/eda-async-sender-client/eda-async-sender-client.gradle b/samples/async/eda-async-sender-client/eda-async-sender-client.gradle index 94f7d779..fc2476b8 100644 --- a/samples/async/eda-async-sender-client/eda-async-sender-client.gradle +++ b/samples/async/eda-async-sender-client/eda-async-sender-client.gradle @@ -6,6 +6,6 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-webflux' implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'io.micrometer:micrometer-registry-prometheus' - implementation 'io.cloudevents:cloudevents-core:4.1.1' + implementation 'io.cloudevents:cloudevents-core:5.0.0' } \ No newline at end of file diff --git a/starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/props/GenericAsyncPropsDomainProperties.java b/starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/props/GenericAsyncPropsDomainProperties.java index c9a784f5..5882b029 100644 --- a/starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/props/GenericAsyncPropsDomainProperties.java +++ b/starters/async-commons-starter/src/main/java/org/reactivecommons/async/starter/props/GenericAsyncPropsDomainProperties.java @@ -1,21 +1,50 @@ package org.reactivecommons.async.starter.props; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; import lombok.SneakyThrows; import java.util.HashMap; import java.util.Map; +import java.util.function.Consumer; @Getter @Setter +@NoArgsConstructor public class GenericAsyncPropsDomainProperties, P> extends HashMap { public GenericAsyncPropsDomainProperties(Map m) { super(m); } - public GenericAsyncPropsDomainProperties() { + /** + * Applies the given customizer over the properties of a domain, keeping every value already bound from + * your configuration files (application.yaml/properties), so YAML and programmatic configuration are merged. + * When the domain is not present yet, it is created with default values. + *

+ * Use this instead of {@link #put(Object, Object)} when you only want to override some properties, because + * put replaces the whole domain, discarding the values bound from your configuration files. + * + * @param domain domain name, for example 'app' + * @param customizer mutations to apply over the domain properties + * @return the customized domain properties + */ + public T customize(String domain, Consumer customizer) { + T props = computeIfAbsent(domain, key -> createProps()); + customizer.accept(props); + return props; + } + + /** + * Creates a domain properties instance with default values. Subclasses override it to support + * {@link #customize(String, Consumer)} for domains that are not present in the configuration files. + * + * @return a new domain properties instance with default values + */ + protected T createProps() { + throw new UnsupportedOperationException(getClass().getName() + " cannot create domain properties. Please " + + "declare the domain in your configuration file or define it with put(domain, props)"); } public static , diff --git a/starters/async-commons-starter/src/test/java/org/reactivecommons/async/starter/props/GenericAsyncPropsDomainPropertiesTest.java b/starters/async-commons-starter/src/test/java/org/reactivecommons/async/starter/props/GenericAsyncPropsDomainPropertiesTest.java new file mode 100644 index 00000000..01f6e9e2 --- /dev/null +++ b/starters/async-commons-starter/src/test/java/org/reactivecommons/async/starter/props/GenericAsyncPropsDomainPropertiesTest.java @@ -0,0 +1,34 @@ +package org.reactivecommons.async.starter.props; + +import org.junit.jupiter.api.Test; +import org.reactivecommons.async.starter.mybroker.props.AsyncMyBrokerPropsDomainProperties; +import org.reactivecommons.async.starter.mybroker.props.MyBrokerAsyncProps; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class GenericAsyncPropsDomainPropertiesTest { + + @Test + void shouldApplyCustomizationsOverExistingDomain() { + var properties = new AsyncMyBrokerPropsDomainProperties(); + MyBrokerAsyncProps configured = new MyBrokerAsyncProps(); + configured.setAppName("configured-app"); + properties.put("app", configured); + + MyBrokerAsyncProps result = properties.customize("app", props -> props.setSecret("secret-name")); + + assertThat(result).isSameAs(configured); + assertThat(result.getAppName()).isEqualTo("configured-app"); + assertThat(result.getSecret()).isEqualTo("secret-name"); + } + + @Test + void shouldFailWhenDomainIsAbsentAndSubclassCannotCreateProps() { + var properties = new AsyncMyBrokerPropsDomainProperties(); + + assertThatThrownBy(() -> properties.customize("app", props -> props.setSecret("secret-name"))) + .isInstanceOf(UnsupportedOperationException.class) + .hasMessageContaining("cannot create domain properties"); + } +} diff --git a/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/KafkaBrokerProvider.java b/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/KafkaBrokerProvider.java index d8ba74a7..45104b88 100644 --- a/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/KafkaBrokerProvider.java +++ b/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/KafkaBrokerProvider.java @@ -56,7 +56,7 @@ public void listenDomainEvents(HandlerResolver resolver) { props.getRetryDelay(), discardNotifier, errorReporter, - props.getAppName()); + props.resolveEventsGroupId()); eventListener.startListener(topologyCreator); } } diff --git a/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaProps.java b/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaProps.java index 26eefb03..ce3b9145 100644 --- a/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaProps.java +++ b/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaProps.java @@ -6,6 +6,7 @@ import lombok.NoArgsConstructor; import lombok.Setter; import lombok.experimental.SuperBuilder; +import org.apache.kafka.clients.consumer.ConsumerConfig; import org.reactivecommons.async.kafka.config.KafkaProperties; import org.reactivecommons.async.starter.props.GenericAsyncProps; import org.springframework.boot.context.properties.NestedConfigurationProperty; @@ -52,4 +53,18 @@ public class AsyncKafkaProps extends GenericAsyncProps { @Builder.Default private String brokerType = "kafka"; + + /** + * Resolves the consumer group id used by the domain events listener. + * + * @return the consumer group id for the domain events listener + */ + public String resolveEventsGroupId() { + Object configured = getConnectionProperties() == null ? null + : getConnectionProperties().buildConsumerProperties().get(ConsumerConfig.GROUP_ID_CONFIG); + if (configured instanceof String groupId && !groupId.isBlank()) { + return groupId; + } + return getAppName() + "-" + getDomain().getEvents().getEventsSuffix(); + } } diff --git a/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsDomain.java b/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsDomain.java index db676261..3a3bfc2c 100644 --- a/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsDomain.java +++ b/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsDomain.java @@ -57,8 +57,8 @@ private static AsyncKafkaPropsDomainProperties applyCustomizer( throw new InvalidConfigurationException(""" KafkaPropsCustomizer was applied but the default domain 'app' is not defined. \ When using KafkaPropsCustomizer, you must declare the 'app' domain in your \ - application.yaml (reactive.commons.kafka.app.*), or add the 'app' domain (or any other new domain) \ - directly inside the customizer using \ + application.yaml (reactive.commons.kafka.app.*), or add the 'app' domain \ + (or any other new domain) directly inside the customizer using \ domainProperties.put("app", AsyncKafkaProps.builder()...build())."""); } } diff --git a/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsDomainProperties.java b/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsDomainProperties.java index 04e0a7f5..c600c5ee 100644 --- a/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsDomainProperties.java +++ b/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsDomainProperties.java @@ -19,4 +19,9 @@ public AsyncKafkaPropsDomainProperties(Map m) AsyncKafkaPropsDomainProperties> builder() { return GenericAsyncPropsDomainProperties.builder(AsyncKafkaPropsDomainProperties.class); } + + @Override + protected AsyncKafkaProps createProps() { + return new AsyncKafkaProps(); + } } diff --git a/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/DomainProps.java b/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/DomainProps.java index da41f4f6..54d494f4 100644 --- a/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/DomainProps.java +++ b/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/DomainProps.java @@ -5,6 +5,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import org.springframework.boot.context.properties.NestedConfigurationProperty; @Getter @Setter @@ -12,10 +13,11 @@ @NoArgsConstructor @Builder public class DomainProps { - // -// @NestedConfigurationProperty -// @Builder.Default -// private EventsProps events = new EventsProps(); + + @NestedConfigurationProperty + @Builder.Default + private EventsProps events = new EventsProps(); + @Builder.Default private boolean ignoreThisListener = false; } diff --git a/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/EventsProps.java b/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/EventsProps.java new file mode 100644 index 00000000..f7ac9bd8 --- /dev/null +++ b/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/EventsProps.java @@ -0,0 +1,23 @@ +package org.reactivecommons.async.kafka.config.props; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class EventsProps { + + /** + * Suffix used to build the consumer group id of the domain events listener when no explicit + * group id is set in the connection properties (consumer.group-id). The resulting group id + * will be ${spring.application.name}-${eventsSuffix}. + */ + @Builder.Default + private String eventsSuffix = "events"; +} diff --git a/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/spring/KafkaPropertiesBase.java b/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/spring/KafkaPropertiesBase.java index f0d88bee..f62e7f7c 100644 --- a/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/spring/KafkaPropertiesBase.java +++ b/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/spring/KafkaPropertiesBase.java @@ -1,5 +1,5 @@ /* - * Copied from: https://github.com/spring-projects/spring-boot/blob/2639d1ead5223889738e85f5216a4b8989f2b9fb/module/spring-boot-kafka/src/main/java/org/springframework/boot/kafka/autoconfigure/KafkaProperties.java + * Copied from: https://github.com/spring-projects/spring-boot/blob/4.1.x/module/spring-boot-kafka/src/main/java/org/springframework/boot/kafka/autoconfigure/KafkaProperties.java * * Copyright 2012-present the original author or authors. * @@ -18,16 +18,6 @@ package org.reactivecommons.async.kafka.config.spring; -import java.io.IOException; -import java.time.Duration; -import java.time.temporal.ChronoUnit; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; - import org.apache.kafka.clients.CommonClientConfigs; import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.producer.ProducerConfig; @@ -35,8 +25,6 @@ import org.apache.kafka.common.serialization.StringDeserializer; import org.apache.kafka.common.serialization.StringSerializer; import org.jspecify.annotations.Nullable; - - import org.springframework.boot.context.properties.PropertyMapper; import org.springframework.boot.context.properties.source.MutuallyExclusiveConfigurationPropertiesException; import org.springframework.boot.convert.DurationUnit; @@ -47,6 +35,16 @@ import org.springframework.util.StringUtils; import org.springframework.util.unit.DataSize; +import java.io.IOException; +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + /** * Configuration properties for Spring for Apache Kafka. *

@@ -183,6 +181,7 @@ private Map buildCommonProperties() { *

* This allows you to add additional properties, if necessary, and override the * default {@code kafkaConsumerFactory} bean. + * * @return the consumer properties initialized with the customizations defined on this * instance */ @@ -197,6 +196,7 @@ public Map buildConsumerProperties() { *

* This allows you to add additional properties, if necessary, and override the * default {@code kafkaProducerFactory} bean. + * * @return the producer properties initialized with the customizations defined on this * instance */ @@ -211,6 +211,7 @@ public Map buildProducerProperties() { *

* This allows you to add additional properties, if necessary, and override the * default {@code kafkaAdmin} bean. + * * @return the admin properties initialized with the customizations defined on this * instance */ @@ -224,6 +225,7 @@ public Map buildAdminProperties() { * Create an initial map of streams properties from the state of this instance. *

* This allows you to add additional properties, if necessary. + * * @return the streams properties initialized with the customizations defined on this * instance */ @@ -919,6 +921,16 @@ public static class Template { */ private @Nullable String transactionIdPrefix; + /** + * Maximum time to wait when closing a producer. + */ + private Duration closeTimeout = Duration.ofSeconds(5); + + /** + * Whether to allow sending non-transactional messages. + */ + private boolean allowNonTransactional; + /** * Whether to enable observation. */ @@ -940,6 +952,22 @@ public void setTransactionIdPrefix(@Nullable String transactionIdPrefix) { this.transactionIdPrefix = transactionIdPrefix; } + public Duration getCloseTimeout() { + return this.closeTimeout; + } + + public void setCloseTimeout(Duration closeTimeout) { + this.closeTimeout = closeTimeout; + } + + public boolean isAllowNonTransactional() { + return this.allowNonTransactional; + } + + public void setAllowNonTransactional(boolean allowNonTransactional) { + this.allowNonTransactional = allowNonTransactional; + } + public boolean isObservationEnabled() { return this.observationEnabled; } @@ -1457,8 +1485,7 @@ private boolean hasValue(@Nullable Object value) { private String resourceToPath(Resource resource) { try { return resource.getFile().getAbsolutePath(); - } - catch (IOException ex) { + } catch (IOException ex) { throw new IllegalStateException("Resource '" + resource + "' must be on a file system", ex); } } @@ -1719,7 +1746,7 @@ public byte id() { private static final class Properties extends HashMap { java.util.function.Consumer in(String key) { - return (value) -> put(key, value); + return value -> put(key, value); } Properties with(Ssl ssl, Security security, Map properties) { diff --git a/starters/async-kafka-starter/src/test/java/org/reactivecommons/async/kafka/KafkaBrokerProviderTest.java b/starters/async-kafka-starter/src/test/java/org/reactivecommons/async/kafka/KafkaBrokerProviderTest.java index 41f5ae8e..bcc77141 100644 --- a/starters/async-kafka-starter/src/test/java/org/reactivecommons/async/kafka/KafkaBrokerProviderTest.java +++ b/starters/async-kafka-starter/src/test/java/org/reactivecommons/async/kafka/KafkaBrokerProviderTest.java @@ -17,6 +17,8 @@ import org.reactivecommons.async.kafka.communications.topology.KafkaCustomizations; import org.reactivecommons.async.kafka.communications.topology.TopologyCreator; import org.reactivecommons.async.kafka.config.props.AsyncKafkaProps; +import org.reactivecommons.async.kafka.config.props.DomainProps; +import org.reactivecommons.async.kafka.config.props.EventsProps; import org.reactivecommons.async.kafka.converters.json.KafkaJacksonMessageConverter; import org.reactivecommons.async.kafka.health.KafkaReactiveHealthIndicator; import org.reactivecommons.async.starter.broker.BrokerProvider; @@ -32,6 +34,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -116,7 +119,41 @@ void shouldListenDomainEvents() { // Act brokerProvider.listenDomainEvents(handlerResolver); // Assert - verify(listener, times(1)).listen(any(String.class), any()); + verify(listener, times(1)).listen(eq("test-events"), any()); + } + + @Test + @SuppressWarnings({"rawtypes", "unchecked"}) + void shouldListenDomainEventsWithCustomEventsSuffix() { + props.setDomain(DomainProps.builder() + .events(EventsProps.builder().eventsSuffix("subsEvents").build()) + .build()); + List mockedListeners = spy(List.of()); + when(mockedListeners.isEmpty()).thenReturn(false); + when(handlerResolver.getEventListeners()).thenReturn(mockedListeners); + when(creator.createTopics(any())).thenReturn(Mono.empty()); + when(listener.getMaxConcurrency()).thenReturn(1); + when(listener.listen(any(String.class), any())).thenReturn(Flux.never()); + // Act + brokerProvider.listenDomainEvents(handlerResolver); + // Assert + verify(listener, times(1)).listen(eq("test-subsEvents"), any()); + } + + @Test + @SuppressWarnings({"rawtypes", "unchecked"}) + void shouldListenDomainEventsWithExplicitGroupIdFromConnectionProperties() { + props.getConnectionProperties().getConsumer().setGroupId("dummy.consumer-group"); + List mockedListeners = spy(List.of()); + when(mockedListeners.isEmpty()).thenReturn(false); + when(handlerResolver.getEventListeners()).thenReturn(mockedListeners); + when(creator.createTopics(any())).thenReturn(Mono.empty()); + when(listener.getMaxConcurrency()).thenReturn(1); + when(listener.listen(any(String.class), any())).thenReturn(Flux.never()); + // Act + brokerProvider.listenDomainEvents(handlerResolver); + // Assert + verify(listener, times(1)).listen(eq("dummy.consumer-group"), any()); } @Test diff --git a/starters/async-kafka-starter/src/test/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsCustomizerMergeTest.java b/starters/async-kafka-starter/src/test/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsCustomizerMergeTest.java new file mode 100644 index 00000000..fd0ebc3e --- /dev/null +++ b/starters/async-kafka-starter/src/test/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsCustomizerMergeTest.java @@ -0,0 +1,60 @@ +package org.reactivecommons.async.kafka.config.props; + +import org.junit.jupiter.api.Test; +import org.reactivecommons.async.kafka.KafkaBrokerProviderFactory; +import org.reactivecommons.async.starter.config.ReactiveCommonsConfig; +import org.reactivecommons.async.starter.impl.common.kafka.RCKafkaConfig; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Verifies that YAML/properties values and programmatic values provided through a KafkaPropsCustomizer are merged + * when the customizer uses customize(), instead of replacing the whole domain as put() does. + */ +@SpringBootTest(classes = { + RCKafkaConfig.class, + AsyncKafkaPropsDomain.class, + KafkaBrokerProviderFactory.class, + ReactiveCommonsConfig.class, + AsyncKafkaPropsCustomizerMergeTest.CustomizerConfig.class +}, properties = { + "reactive.commons.kafka.app.connectionProperties.consumer.group-id=dummy.consumer-group", + "reactive.commons.kafka.app.retryDelay=1000" +}) +class AsyncKafkaPropsCustomizerMergeTest { + + @Autowired + private AsyncKafkaPropsDomain propsDomain; + + @Test + void shouldKeepYamlValuesAndApplyProgrammaticOnes() { + AsyncKafkaProps app = propsDomain.getProps("app"); + // Values coming from the customizer + assertThat(app.getMaxRetries()).isEqualTo(2); + assertThat(app.getRetryDelay()).isEqualTo(30000); + assertThat(app.getConnectionProperties().getBootstrapServers()).containsExactly("localhost:9099"); + // Values coming from configuration properties are preserved + assertThat(app.getConnectionProperties().getConsumer().getGroupId()) + .isEqualTo("dummy.consumer-group"); + assertThat(app.resolveEventsGroupId()).isEqualTo("dummy.consumer-group"); + } + + @TestConfiguration + static class CustomizerConfig { + + @Bean + public AsyncKafkaPropsDomain.KafkaPropsCustomizer kafkaPropsCustomizer() { + return domainProperties -> domainProperties.customize("app", app -> { + app.setMaxRetries(2); + app.setRetryDelay(30000); + app.getConnectionProperties().setBootstrapServers(List.of("localhost:9099")); + }); + } + } +} diff --git a/starters/async-kafka-starter/src/test/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsDomainPropertiesTest.java b/starters/async-kafka-starter/src/test/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsDomainPropertiesTest.java new file mode 100644 index 00000000..aa42dd2d --- /dev/null +++ b/starters/async-kafka-starter/src/test/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsDomainPropertiesTest.java @@ -0,0 +1,55 @@ +package org.reactivecommons.async.kafka.config.props; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.reactivecommons.async.api.HandlerRegistry.DEFAULT_DOMAIN; + +class AsyncKafkaPropsDomainPropertiesTest { + + private AsyncKafkaPropsDomainProperties withYamlDomain() { + var properties = new AsyncKafkaPropsDomainProperties(); + var fromYaml = new AsyncKafkaProps(); + fromYaml.setRetryDelay(1000); + fromYaml.getConnectionProperties().getConsumer().setGroupId("dummy.consumer-group"); + properties.put(DEFAULT_DOMAIN, fromYaml); + return properties; + } + + @Test + void shouldMergeCustomizationsKeepingConfiguredValues() { + AsyncKafkaPropsDomainProperties properties = withYamlDomain(); + + AsyncKafkaProps result = properties.customize(DEFAULT_DOMAIN, app -> { + app.setMaxRetries(2); + app.setRetryDelay(30000); + }); + + assertThat(result).isSameAs(properties.get(DEFAULT_DOMAIN)); + assertThat(result.getMaxRetries()).isEqualTo(2); + assertThat(result.getRetryDelay()).isEqualTo(30000); + assertThat(result.getConnectionProperties().getConsumer().getGroupId()) + .isEqualTo("dummy.consumer-group"); + } + + @Test + void shouldCreateDomainWithDefaultsWhenItDoesNotExist() { + var properties = new AsyncKafkaPropsDomainProperties(); + + AsyncKafkaProps result = properties.customize(DEFAULT_DOMAIN, app -> app.setMaxRetries(5)); + + assertThat(properties).containsKey(DEFAULT_DOMAIN); + assertThat(result.getMaxRetries()).isEqualTo(5); + assertThat(result.getBrokerType()).isEqualTo("kafka"); + assertThat(result.getCreateTopology()).isTrue(); + } + + @Test + void shouldDiscardConfiguredValuesWhenDomainIsReplacedWithPut() { + AsyncKafkaPropsDomainProperties properties = withYamlDomain(); + + properties.put(DEFAULT_DOMAIN, AsyncKafkaProps.builder().maxRetries(2).build()); + + assertThat(properties.get(DEFAULT_DOMAIN).getConnectionProperties().getConsumer().getGroupId()).isNull(); + } +} diff --git a/starters/async-kafka-starter/src/test/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsEventsGroupIdTest.java b/starters/async-kafka-starter/src/test/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsEventsGroupIdTest.java new file mode 100644 index 00000000..a23161b9 --- /dev/null +++ b/starters/async-kafka-starter/src/test/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsEventsGroupIdTest.java @@ -0,0 +1,53 @@ +package org.reactivecommons.async.kafka.config.props; + +import org.junit.jupiter.api.Test; +import org.reactivecommons.async.kafka.KafkaBrokerProviderFactory; +import org.reactivecommons.async.starter.config.ReactiveCommonsConfig; +import org.reactivecommons.async.starter.impl.common.kafka.RCKafkaConfig; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest(classes = { + RCKafkaConfig.class, + AsyncKafkaPropsDomain.class, + KafkaBrokerProviderFactory.class, + ReactiveCommonsConfig.class +}, properties = { + "reactive.commons.kafka.app.connectionProperties.consumer.group-id=dummy.consumer-group", + "reactive.commons.kafka.accounts.checkExistingTopics=false", + "reactive.commons.kafka.accounts.connectionProperties.bootstrap-servers=localhost:9093", + "reactive.commons.kafka.accounts.domain.events.events-suffix=subsEvents", + "reactive.commons.kafka.notifications.checkExistingTopics=false", + "reactive.commons.kafka.notifications.connectionProperties.properties.group.id=raw.consumer-group" +}) +class AsyncKafkaPropsEventsGroupIdTest { + + @Autowired + private AsyncKafkaPropsDomain propsDomain; + + @Test + void shouldBindGroupIdFromConnectionPropertiesConsumer() { + AsyncKafkaProps app = propsDomain.getProps("app"); + + assertThat(app.getConnectionProperties().getConsumer().getGroupId()) + .isEqualTo("dummy.consumer-group"); + assertThat(app.resolveEventsGroupId()).isEqualTo("dummy.consumer-group"); + } + + @Test + void shouldBindGroupIdFromRawConnectionProperties() { + AsyncKafkaProps notifications = propsDomain.getProps("notifications"); + + assertThat(notifications.resolveEventsGroupId()).isEqualTo("raw.consumer-group"); + } + + @Test + void shouldBindEventsSuffixPerDomainWhenNoGroupIdIsConfigured() { + AsyncKafkaProps accounts = propsDomain.getProps("accounts"); + + assertThat(accounts.getConnectionProperties().getConsumer().getGroupId()).isNull(); + assertThat(accounts.resolveEventsGroupId()).isEqualTo("async-kafka-starter-subsEvents"); + } +} diff --git a/starters/async-kafka-starter/src/test/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsGroupIdTest.java b/starters/async-kafka-starter/src/test/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsGroupIdTest.java new file mode 100644 index 00000000..9099e95f --- /dev/null +++ b/starters/async-kafka-starter/src/test/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsGroupIdTest.java @@ -0,0 +1,86 @@ +package org.reactivecommons.async.kafka.config.props; + +import org.junit.jupiter.api.Test; +import org.reactivecommons.async.kafka.config.KafkaProperties; + +import static org.assertj.core.api.Assertions.assertThat; + +class AsyncKafkaPropsGroupIdTest { + + private AsyncKafkaProps propsWith(KafkaProperties connectionProperties) { + var props = new AsyncKafkaProps(); + props.setAppName("sample-app"); + props.setConnectionProperties(connectionProperties); + return props; + } + + @Test + void shouldResolveDefaultGroupIdFromAppNameWhenNoGroupIdConfigured() { + AsyncKafkaProps props = propsWith(new KafkaProperties()); + + assertThat(props.getDomain().getEvents().getEventsSuffix()).isEqualTo("events"); + assertThat(props.resolveEventsGroupId()).isEqualTo("sample-app-events"); + } + + @Test + void shouldResolveDefaultGroupIdWithCustomEventsSuffix() { + AsyncKafkaProps props = propsWith(new KafkaProperties()); + props.setDomain(DomainProps.builder() + .events(EventsProps.builder().eventsSuffix("subsEvents").build()) + .build()); + + assertThat(props.resolveEventsGroupId()).isEqualTo("sample-app-subsEvents"); + } + + @Test + void shouldResolveGroupIdFromConsumerProperty() { + var connectionProperties = new KafkaProperties(); + connectionProperties.getConsumer().setGroupId("dummy.consumer-group"); + + assertThat(propsWith(connectionProperties).resolveEventsGroupId()) + .isEqualTo("dummy.consumer-group"); + } + + @Test + void shouldResolveGroupIdFromCommonRawProperties() { + var connectionProperties = new KafkaProperties(); + connectionProperties.getProperties().put("group.id", "dummy.consumer-group"); + + assertThat(propsWith(connectionProperties).resolveEventsGroupId()) + .isEqualTo("dummy.consumer-group"); + } + + @Test + void shouldResolveGroupIdFromConsumerRawProperties() { + var connectionProperties = new KafkaProperties(); + connectionProperties.getConsumer().getProperties().put("group.id", "raw.consumer-group"); + + assertThat(propsWith(connectionProperties).resolveEventsGroupId()).isEqualTo("raw.consumer-group"); + } + + @Test + void shouldPreferConsumerGroupIdOverCommonRawProperties() { + var connectionProperties = new KafkaProperties(); + connectionProperties.getProperties().put("group.id", "common.consumer-group"); + connectionProperties.getConsumer().setGroupId("consumer.consumer-group"); + + assertThat(propsWith(connectionProperties).resolveEventsGroupId()).isEqualTo("consumer.consumer-group"); + } + + @Test + void shouldIgnoreBlankGroupId() { + var connectionProperties = new KafkaProperties(); + connectionProperties.getConsumer().setGroupId(" "); + + assertThat(propsWith(connectionProperties).resolveEventsGroupId()).isEqualTo("sample-app-events"); + } + + @Test + void shouldResolveDefaultGroupIdWhenConnectionPropertiesAreNotSet() { + var props = new AsyncKafkaProps(); + props.setAppName("sample-app"); + props.setConnectionProperties(null); + + assertThat(props.resolveEventsGroupId()).isEqualTo("sample-app-events"); + } +} diff --git a/starters/async-rabbit-starter/src/main/java/org/reactivecommons/async/rabbit/config/props/AsyncRabbitPropsDomainProperties.java b/starters/async-rabbit-starter/src/main/java/org/reactivecommons/async/rabbit/config/props/AsyncRabbitPropsDomainProperties.java index 44fe7f0a..890296ba 100644 --- a/starters/async-rabbit-starter/src/main/java/org/reactivecommons/async/rabbit/config/props/AsyncRabbitPropsDomainProperties.java +++ b/starters/async-rabbit-starter/src/main/java/org/reactivecommons/async/rabbit/config/props/AsyncRabbitPropsDomainProperties.java @@ -19,4 +19,9 @@ public AsyncRabbitPropsDomainProperties(Map m) { AsyncRabbitPropsDomainProperties> builder() { return GenericAsyncPropsDomainProperties.builder(AsyncRabbitPropsDomainProperties.class); } + + @Override + protected AsyncProps createProps() { + return new AsyncProps(); + } } diff --git a/starters/async-rabbit-starter/src/test/java/org/reactivecommons/async/rabbit/config/props/AsyncRabbitPropsDomainPropertiesTest.java b/starters/async-rabbit-starter/src/test/java/org/reactivecommons/async/rabbit/config/props/AsyncRabbitPropsDomainPropertiesTest.java new file mode 100644 index 00000000..af440c02 --- /dev/null +++ b/starters/async-rabbit-starter/src/test/java/org/reactivecommons/async/rabbit/config/props/AsyncRabbitPropsDomainPropertiesTest.java @@ -0,0 +1,34 @@ +package org.reactivecommons.async.rabbit.config.props; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.reactivecommons.async.api.HandlerRegistry.DEFAULT_DOMAIN; + +class AsyncRabbitPropsDomainPropertiesTest { + + @Test + void shouldMergeCustomizationsKeepingConfiguredValues() { + var properties = new AsyncRabbitPropsDomainProperties(); + var fromYaml = new AsyncProps(); + fromYaml.getDomain().getEvents().setEventsSuffix("subsEvents"); + properties.put(DEFAULT_DOMAIN, fromYaml); + + AsyncProps result = properties.customize(DEFAULT_DOMAIN, app -> app.setRetryDelay(30000)); + + assertThat(result).isSameAs(fromYaml); + assertThat(result.getRetryDelay()).isEqualTo(30000); + assertThat(result.getDomain().getEvents().getEventsSuffix()).isEqualTo("subsEvents"); + } + + @Test + void shouldCreateDomainWithDefaultsWhenItDoesNotExist() { + var properties = new AsyncRabbitPropsDomainProperties(); + + AsyncProps result = properties.customize(DEFAULT_DOMAIN, app -> app.setRetryDelay(30000)); + + assertThat(properties).containsKey(DEFAULT_DOMAIN); + assertThat(result.getRetryDelay()).isEqualTo(30000); + assertThat(result.getDomain().getEvents().getEventsSuffix()).isEqualTo("subsEvents"); + } +} From 3bb65444d99f9f003308fe340fb9177712780e17 Mon Sep 17 00:00:00 2001 From: luisgomez29 Date: Thu, 6 Aug 2026 10:00:41 -0500 Subject: [PATCH 2/3] refactor: streamline KafkaProperties configuration for clarity --- .../configuration_properties/2-kafka.md | 22 ++++--------------- 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/docs/docs/reactive-commons/configuration_properties/2-kafka.md b/docs/docs/reactive-commons/configuration_properties/2-kafka.md index b85db817..7f8df4a7 100644 --- a/docs/docs/reactive-commons/configuration_properties/2-kafka.md +++ b/docs/docs/reactive-commons/configuration_properties/2-kafka.md @@ -79,24 +79,10 @@ Equivalent programmatic definition: ```java var propertiesApp = new KafkaProperties(); -propertiesApp. - -setBootstrapServers(List.of("broker1:9094", "broker2:9094")); - propertiesApp. - -getConsumer(). - -setGroupId("dummy.consumer-group"); -propertiesApp. - -getSecurity(). - -setProtocol("SASL_SSL"); -propertiesApp. - -getProperties(). - -put("sasl.mechanism","SCRAM-SHA-512"); +propertiesApp.setBootstrapServers(List.of("broker1:9094", "broker2:9094")); +propertiesApp.getConsumer().setGroupId("dummy.consumer-group"); +propertiesApp.getSecurity().setProtocol("SASL_SSL"); +propertiesApp.getProperties().put("sasl.mechanism","SCRAM-SHA-512"); ``` Everything under `connectionProperties` is translated into plain Kafka client properties when the consumer, producer and From 8c0f9ac45931cdc2c796cac5f8f98c50931d8786 Mon Sep 17 00:00:00 2001 From: luisgomez29 Date: Thu, 6 Aug 2026 11:10:29 -0500 Subject: [PATCH 3/3] refactor: simplify consumer group ID resolution by removing events suffix configuration --- .../configuration_properties/2-kafka.md | 9 +++----- .../kafka/config/props/AsyncKafkaProps.java | 7 +++++- .../async/kafka/config/props/DomainProps.java | 7 +++--- .../async/kafka/config/props/EventsProps.java | 23 ------------------- .../async/kafka/KafkaBrokerProviderTest.java | 20 ---------------- .../AsyncKafkaPropsEventsGroupIdTest.java | 5 ++-- .../props/AsyncKafkaPropsGroupIdTest.java | 11 --------- 7 files changed, 14 insertions(+), 68 deletions(-) delete mode 100644 starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/EventsProps.java diff --git a/docs/docs/reactive-commons/configuration_properties/2-kafka.md b/docs/docs/reactive-commons/configuration_properties/2-kafka.md index 7f8df4a7..84e30c3a 100644 --- a/docs/docs/reactive-commons/configuration_properties/2-kafka.md +++ b/docs/docs/reactive-commons/configuration_properties/2-kafka.md @@ -30,8 +30,6 @@ reactive: brokerType: "kafka" # please don't change this value domain: ignoreThisListener: false # Allows you to disable event listener for this specific domain - events: - eventsSuffix: events # consumer group suffix used only when consumer.group-id is not set, group id will be like ${spring.application.name}-${eventsSuffix} connectionProperties: # you can override the connection properties of each domain bootstrap-servers: localhost:9092 consumer: @@ -87,7 +85,7 @@ propertiesApp.getProperties().put("sasl.mechanism","SCRAM-SHA-512"); Everything under `connectionProperties` is translated into plain Kafka client properties when the consumer, producer and admin clients are created (`buildConsumerProperties()`, `buildProducerProperties()`, `buildAdminProperties()`). Within -the consumer, precedence is: `connectionProperties.properties` (common),`connectionProperties.consumer.*` +the consumer, precedence is: `connectionProperties.properties` (common), `connectionProperties.consumer.*` typed fields, `connectionProperties.consumer.properties` (raw consumer-specific). ## Consumer group for domain events @@ -125,8 +123,7 @@ Resolution order for the events consumer group id: 1. The `group.id` present in the consumer connection properties, no matter how it was provided: `connectionProperties.consumer.group-id`, `connectionProperties.consumer.properties."group.id"` or `connectionProperties.properties."group.id"`. It is used **exactly as configured**, without appending any suffix. -2. Otherwise, the value defined by the `domain.events.eventsSuffix` properties is used, preserving the default - configuration. +2. Otherwise `${spring.application.name}-events`, the default convention. Each domain resolves its own group id, so you can authorize a different consumer group per Kafka cluster. @@ -211,7 +208,7 @@ public class KafkaConfig { `domainProperties` is a map of domains, so `domainProperties.put("app", AsyncKafkaProps.builder()...build())` **replaces the whole domain** and every value bound from `application.yaml` for that domain is lost, including `connectionProperties`, which goes back to its defaults (`localhost:9092`, no `group.id`, hence a consumer group -resolved as `domain.events.eventsSuffix`). +resolved as `${spring.application.name}-events`). The same happens with `app.setConnectionProperties(newKafkaProperties)`: it replaces the whole connection properties object, so anything configured in YAML under `connectionProperties` (for example `consumer.group-id`) is discarded. diff --git a/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaProps.java b/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaProps.java index ce3b9145..5b66c3fe 100644 --- a/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaProps.java +++ b/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaProps.java @@ -54,8 +54,13 @@ public class AsyncKafkaProps extends GenericAsyncProps { @Builder.Default private String brokerType = "kafka"; + private static final String EVENTS_GROUP_ID_SUFFIX = "-events"; + /** * Resolves the consumer group id used by the domain events listener. + *

+ * It takes the group.id configured in the consumer connection properties, and falls back to + * appName + "-events" when it is not present. * * @return the consumer group id for the domain events listener */ @@ -65,6 +70,6 @@ public String resolveEventsGroupId() { if (configured instanceof String groupId && !groupId.isBlank()) { return groupId; } - return getAppName() + "-" + getDomain().getEvents().getEventsSuffix(); + return getAppName() + EVENTS_GROUP_ID_SUFFIX; } } diff --git a/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/DomainProps.java b/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/DomainProps.java index 54d494f4..67f517f6 100644 --- a/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/DomainProps.java +++ b/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/DomainProps.java @@ -5,7 +5,6 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import org.springframework.boot.context.properties.NestedConfigurationProperty; @Getter @Setter @@ -14,9 +13,9 @@ @Builder public class DomainProps { - @NestedConfigurationProperty - @Builder.Default - private EventsProps events = new EventsProps(); +// @NestedConfigurationProperty +// @Builder.Default +// private EventsProps events = new EventsProps(); @Builder.Default private boolean ignoreThisListener = false; diff --git a/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/EventsProps.java b/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/EventsProps.java deleted file mode 100644 index f7ac9bd8..00000000 --- a/starters/async-kafka-starter/src/main/java/org/reactivecommons/async/kafka/config/props/EventsProps.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.reactivecommons.async.kafka.config.props; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -@Getter -@Setter -@AllArgsConstructor -@NoArgsConstructor -@Builder -public class EventsProps { - - /** - * Suffix used to build the consumer group id of the domain events listener when no explicit - * group id is set in the connection properties (consumer.group-id). The resulting group id - * will be ${spring.application.name}-${eventsSuffix}. - */ - @Builder.Default - private String eventsSuffix = "events"; -} diff --git a/starters/async-kafka-starter/src/test/java/org/reactivecommons/async/kafka/KafkaBrokerProviderTest.java b/starters/async-kafka-starter/src/test/java/org/reactivecommons/async/kafka/KafkaBrokerProviderTest.java index bcc77141..e1f173a2 100644 --- a/starters/async-kafka-starter/src/test/java/org/reactivecommons/async/kafka/KafkaBrokerProviderTest.java +++ b/starters/async-kafka-starter/src/test/java/org/reactivecommons/async/kafka/KafkaBrokerProviderTest.java @@ -17,8 +17,6 @@ import org.reactivecommons.async.kafka.communications.topology.KafkaCustomizations; import org.reactivecommons.async.kafka.communications.topology.TopologyCreator; import org.reactivecommons.async.kafka.config.props.AsyncKafkaProps; -import org.reactivecommons.async.kafka.config.props.DomainProps; -import org.reactivecommons.async.kafka.config.props.EventsProps; import org.reactivecommons.async.kafka.converters.json.KafkaJacksonMessageConverter; import org.reactivecommons.async.kafka.health.KafkaReactiveHealthIndicator; import org.reactivecommons.async.starter.broker.BrokerProvider; @@ -122,24 +120,6 @@ void shouldListenDomainEvents() { verify(listener, times(1)).listen(eq("test-events"), any()); } - @Test - @SuppressWarnings({"rawtypes", "unchecked"}) - void shouldListenDomainEventsWithCustomEventsSuffix() { - props.setDomain(DomainProps.builder() - .events(EventsProps.builder().eventsSuffix("subsEvents").build()) - .build()); - List mockedListeners = spy(List.of()); - when(mockedListeners.isEmpty()).thenReturn(false); - when(handlerResolver.getEventListeners()).thenReturn(mockedListeners); - when(creator.createTopics(any())).thenReturn(Mono.empty()); - when(listener.getMaxConcurrency()).thenReturn(1); - when(listener.listen(any(String.class), any())).thenReturn(Flux.never()); - // Act - brokerProvider.listenDomainEvents(handlerResolver); - // Assert - verify(listener, times(1)).listen(eq("test-subsEvents"), any()); - } - @Test @SuppressWarnings({"rawtypes", "unchecked"}) void shouldListenDomainEventsWithExplicitGroupIdFromConnectionProperties() { diff --git a/starters/async-kafka-starter/src/test/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsEventsGroupIdTest.java b/starters/async-kafka-starter/src/test/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsEventsGroupIdTest.java index a23161b9..aed049b9 100644 --- a/starters/async-kafka-starter/src/test/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsEventsGroupIdTest.java +++ b/starters/async-kafka-starter/src/test/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsEventsGroupIdTest.java @@ -18,7 +18,6 @@ "reactive.commons.kafka.app.connectionProperties.consumer.group-id=dummy.consumer-group", "reactive.commons.kafka.accounts.checkExistingTopics=false", "reactive.commons.kafka.accounts.connectionProperties.bootstrap-servers=localhost:9093", - "reactive.commons.kafka.accounts.domain.events.events-suffix=subsEvents", "reactive.commons.kafka.notifications.checkExistingTopics=false", "reactive.commons.kafka.notifications.connectionProperties.properties.group.id=raw.consumer-group" }) @@ -44,10 +43,10 @@ void shouldBindGroupIdFromRawConnectionProperties() { } @Test - void shouldBindEventsSuffixPerDomainWhenNoGroupIdIsConfigured() { + void shouldFallBackToAppNameConventionWhenNoGroupIdIsConfigured() { AsyncKafkaProps accounts = propsDomain.getProps("accounts"); assertThat(accounts.getConnectionProperties().getConsumer().getGroupId()).isNull(); - assertThat(accounts.resolveEventsGroupId()).isEqualTo("async-kafka-starter-subsEvents"); + assertThat(accounts.resolveEventsGroupId()).isEqualTo("async-kafka-starter-events"); } } diff --git a/starters/async-kafka-starter/src/test/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsGroupIdTest.java b/starters/async-kafka-starter/src/test/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsGroupIdTest.java index 9099e95f..558c6e36 100644 --- a/starters/async-kafka-starter/src/test/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsGroupIdTest.java +++ b/starters/async-kafka-starter/src/test/java/org/reactivecommons/async/kafka/config/props/AsyncKafkaPropsGroupIdTest.java @@ -18,20 +18,9 @@ private AsyncKafkaProps propsWith(KafkaProperties connectionProperties) { void shouldResolveDefaultGroupIdFromAppNameWhenNoGroupIdConfigured() { AsyncKafkaProps props = propsWith(new KafkaProperties()); - assertThat(props.getDomain().getEvents().getEventsSuffix()).isEqualTo("events"); assertThat(props.resolveEventsGroupId()).isEqualTo("sample-app-events"); } - @Test - void shouldResolveDefaultGroupIdWithCustomEventsSuffix() { - AsyncKafkaProps props = propsWith(new KafkaProperties()); - props.setDomain(DomainProps.builder() - .events(EventsProps.builder().eventsSuffix("subsEvents").build()) - .build()); - - assertThat(props.resolveEventsGroupId()).isEqualTo("sample-app-subsEvents"); - } - @Test void shouldResolveGroupIdFromConsumerProperty() { var connectionProperties = new KafkaProperties();