diff --git a/CHANGELOG.md b/CHANGELOG.md index 80c0c5a02..41c58e00f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## v2.4.0 - (2026-01-11) + +### New features: + +- Added cron trigger support + - Schedule tasks using cron expressions + - Suspend and resume cron jobs via API and UI + - New cron jobs management page in UI +- Updated UI dependencies + - Updated React to 19.2.3 + - Updated all npm packages to latest versions + - Fixed security vulnerabilities (CVE-2025-62522, CVE-2025-64718) + ## v2.3.1 - (2026-01-11) ### UI Improvements: diff --git a/README.md b/README.md index c3389b7ac..0a2150dd3 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,8 @@ Secondary goal is to support [Poor mans Workflow](https://github.com/sterlp/pmw) 1. **JPA-Powered Persistence** - Automatically persists tasks to your database 1. **Spring Boot Auto-Configuration** - Simple setup with @EnableSpringPersistentTasks -1. **Clustering Support** - Built-in lock management for distributed environments +1. **Clustering Support for tasks** - Built-in lock management for distributed environments and tasks +1. **Clustering Support for cron tasks** - Support for cron tasks in a cluster - run on just one node 1. **Type-Safe Tasks** - Generic task definitions with state objects 1. **Retry Mechanisms** - Automatic retry policies for failed executions 1. **Transactional Integration** - Works with Spring's transaction management @@ -158,15 +159,15 @@ public class ExampleApplication { ## Schedulers -![Schedulers](screenshots/schedulers-screen.png) +![Schedulers](doc/docs/assets/dashboard.png) ## Triggers -![Triggers](screenshots/triggers-screen.png) +![Triggers](doc/docs/assets/triggers.png) ## History -![History](screenshots/history-screen.png) +![Triggers](doc/docs/assets/history-page.png) # Alternatives diff --git a/core/code-style.md b/core/code-style.md new file mode 100644 index 000000000..ac0716ef6 --- /dev/null +++ b/core/code-style.md @@ -0,0 +1,88 @@ +## General rules + +- Clean code apply +- Uncle Bob SOLID principle should be considered where useful +- if a method has more than 4 parameters consider an value/ entity class +- the zalando REST API rules apply +- run maven clean insall before a commit / end of work + +## Testing + +Each major functionality should be tested each test should be structured with. One test for each class with is called `subject` in the test to make clear this is the tested component. +Test should try to work with random data, so a clear shoud in general not be required. + +// GIVEN +here is all the setup code and mybe any required special clear code for this test. + +// WHEN +here is alle the action / modification code calling the service + +// THEN +here comes all the assert code + +## Component architecture + +Each component is in its own package. Any access to a component has to be through the *Services classes - which are in the root directory of the folder. + +All classes despite Model/Entity classes or converter are considered private and should not be used by any other component. +Each component is itself implemented by the help of + +### Services classes e.g. PersonService +Position: Root Packge of the component + +Represent the public internal API between components. Is usually annotated with @Service + +- Implementiert den „Workflow“ +- High Level Logik +- Aspekte wie + - Transaktions-management + - Autorisierung + - Caching + - Policies + +Has usually no private methods. Private method are implemented by components. +A service should have a short description in the java doc - where its responsibility is state clearly to ensure no other component or services does the same. + +Method names are more generic as the one used in repositories or components. +A Service may call other services. +A Service may call other components or repositories in their module/component. + +### Component classes e.g. DeletePersonComponent + +Position: sub package "component" + +Implementiert den „Use-Case/ Step“. Usually named like the method itself. In general has only one responsibility and usually only one "execute" method. May have serveral methods. but always to access this one logic in different ways. +Otherwise the method names are very specific. + +Low Level Logik +Wie eine private Methode im Service +Sollte alleine testbar sein + +- A component may call other components in their module/component. +- As a component is a low lever building part of the application it is not allowed to delegate the work up to the services of its own component. +- But a component may call other functionality from other components through foreign Services classes. + +### Repository classes e.g. PersonRepository +Position: sub package "repository" + +- Implementiert die „Persistenz“ +- Abstraktion der Datenbank +- Queries + +### Resource class e.g. PersonResource + +Position: sub package "api" + +Implements the public interface and delegates to the service. It it the anty corruption layer to the ourside world, it has no business logic and only converts from the external API to the internal entity classes. + +### Entity class e.g. PersonEntity +Position: sub package "model" + +Represent the "DTO" or "DB Entities" of the components. They are suffixed with "Entity" to ensure no name collision with the API classes, which has no suffix. +These classes can be shared between components but any modification or creation to this class has to be done by the Service owning this entity. + +### Timers e.g. PersonTimer + +Position: either root package with the service or sub package "timer" + +A Timer should only be an internal way to call the service. Itself has no logic despite logging. Any logic sould be in the Service or if it is more complex in an Component class, or several, which is/are used by the Service. \ No newline at end of file diff --git a/core/pom.xml b/core/pom.xml index a6f8a0c30..5653222f1 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -6,7 +6,7 @@ org.sterl.spring spring-persistent-tasks-root - 2.3.2-SNAPSHOT + 2.4.0-SNAPSHOT ../pom.xml @@ -152,6 +152,12 @@ java.io.Serializable org.sterl.spring.persistent_tasks.api.TaskId org.sterl.spring.persistent_tasks.api.RetryStrategy + org.sterl.spring.persistent_tasks.api.CronSchedule + org.sterl.spring.persistent_tasks.api.Schedule + org.sterl.spring.persistent_tasks.api.CronTriggerBuilder + org.sterl.spring.persistent_tasks.api.TriggerBuilder + org.sterl.spring.persistent_tasks.api.IntervalSchedule + TemporalUnit ../ui/spt-ui-lib/lib/server-api.ts module diff --git a/core/src/main/java/org/sterl/spring/persistent_tasks/PersistentTaskService.java b/core/src/main/java/org/sterl/spring/persistent_tasks/PersistentTaskService.java index 90cec913c..8181e1b37 100644 --- a/core/src/main/java/org/sterl/spring/persistent_tasks/PersistentTaskService.java +++ b/core/src/main/java/org/sterl/spring/persistent_tasks/PersistentTaskService.java @@ -25,6 +25,7 @@ import org.sterl.spring.persistent_tasks.shared.model.HasTrigger; import org.sterl.spring.persistent_tasks.shared.model.TriggerEntity; import org.sterl.spring.persistent_tasks.trigger.TriggerService; +import org.sterl.spring.persistent_tasks.trigger.model.CronTriggerEntity; import org.sterl.spring.persistent_tasks.trigger.model.RunningTriggerEntity; import lombok.RequiredArgsConstructor; @@ -166,4 +167,15 @@ public Optional getLastTriggerHistory(Long id) { if (result.isEmpty()) return Optional.empty(); return Optional.of(result.getContent().getFirst()); } + + /** + * Adds a {@link CronTriggerEntity} for recurring tasks. + * It will also add the {@link TriggerEntity} if required. + * + * @param cronTrigger the {@link CronTriggerEntity} to manage + * @return true if a new {@link TriggerEntity} was created, false if one already exists + */ + public boolean register(CronTriggerEntity cron) { + return triggerService.register(cron); + } } diff --git a/core/src/main/java/org/sterl/spring/persistent_tasks/api/CronSchedule.java b/core/src/main/java/org/sterl/spring/persistent_tasks/api/CronSchedule.java new file mode 100644 index 000000000..a5fc861d3 --- /dev/null +++ b/core/src/main/java/org/sterl/spring/persistent_tasks/api/CronSchedule.java @@ -0,0 +1,64 @@ +package org.sterl.spring.persistent_tasks.api; + +import java.time.OffsetDateTime; +import java.time.temporal.ChronoUnit; + +import org.springframework.scheduling.support.CronExpression; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +/** + * A schedule based on a cron expression. Always uses UTC timezone. + *

+ * Examples: + *

+ */ +@EqualsAndHashCode(of = "expression") +@ToString(of = "expression") +public final class CronSchedule implements Schedule { + private static final long serialVersionUID = 1L; + + @Getter + private final String expression; + private transient CronExpression cronExpression; + + /** + * Creates a new cron schedule. + * + * @param expression the cron expression (6 fields: second minute hour day month weekday) + * @throws IllegalArgumentException if the cron expression is invalid + */ + public CronSchedule(String expression) { + if (expression == null || expression.trim().isEmpty()) { + throw new IllegalArgumentException("Cron expression cannot be null or empty"); + } + this.expression = expression.trim(); + this.cronExpression = CronExpression.parse(this.expression); + } + + @Override + public OffsetDateTime next(OffsetDateTime from) { + if (cronExpression == null) { + cronExpression = CronExpression.parse(expression); + } + + var next = cronExpression.next(OffsetDateTime.now()); + + if (next == null) { + throw new IllegalStateException("No next execution time found for cron: " + expression); + } + + return next.truncatedTo(ChronoUnit.SECONDS); + } + + @Override + public String description() { + return "cron(" + expression + ")"; + } +} diff --git a/core/src/main/java/org/sterl/spring/persistent_tasks/api/CronTriggerBuilder.java b/core/src/main/java/org/sterl/spring/persistent_tasks/api/CronTriggerBuilder.java new file mode 100644 index 000000000..c8a92d324 --- /dev/null +++ b/core/src/main/java/org/sterl/spring/persistent_tasks/api/CronTriggerBuilder.java @@ -0,0 +1,106 @@ +package org.sterl.spring.persistent_tasks.api; + +import java.io.Serializable; +import java.time.Duration; +import java.util.function.Supplier; + +import org.sterl.spring.persistent_tasks.trigger.model.CronTriggerEntity; + +import lombok.AccessLevel; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; + +/** + * Fluent builder for creating scheduled (recurring) triggers. + */ +@RequiredArgsConstructor(access = AccessLevel.PACKAGE) +public class CronTriggerBuilder { + @NonNull + private final TaskId taskId; + private String id; + private Schedule schedule; + private Supplier stateProvider; + private String tag; + private int priority = TriggerRequest.DEFAULT_PRIORITY; + + /** + * Sets the unique ID for this scheduled trigger. + * If not set, defaults to {@link Schedule#description()}. + */ + public CronTriggerBuilder id(String id) { + this.id = id; + return this; + } + + /** + * Sets a fixed state for all trigger executions. + */ + public CronTriggerBuilder state(T state) { + this.stateProvider = () -> state; + return this; + } + + /** + * Sets a state provider that's called for each trigger execution. + * Useful for dynamic state generation. + */ + public CronTriggerBuilder stateProvider(Supplier stateProvider) { + this.stateProvider = stateProvider; + return this; + } + + /** + * Sets the schedule using a cron expression. + * + * @param cronExpression cron expression (6 fields: second minute hour day month weekday) + */ + public CronTriggerBuilder cron(String cronExpression) { + this.schedule = new CronSchedule(cronExpression); + return this; + } + + /** + * Sets the schedule using a fixed interval after each invocation. + * + * @param interval duration between executions + */ + public CronTriggerBuilder after(Duration interval) { + this.schedule = new IntervalSchedule(interval); + return this; + } + + /** + * Sets a custom tag for created triggers. + */ + public CronTriggerBuilder tag(String tag) { + this.tag = tag; + return this; + } + + /** + * Sets the priority for created triggers (higher = more important). + */ + public CronTriggerBuilder priority(int priority) { + this.priority = priority; + return this; + } + + public CronTriggerEntity build() { + if (schedule == null) { + throw new IllegalStateException("Schedule must be set using .cron() or .every()"); + } + if (id == null) { + id = schedule.description(); + } + + return CronTriggerEntity.builder() + .id(id) + .taskId(taskId) + .schedule(schedule) + .stateProvider(stateProvider) + .tag(tag == null ? "cron" : tag) + .priority(priority) + .build(); + + } +} \ No newline at end of file diff --git a/core/src/main/java/org/sterl/spring/persistent_tasks/api/CronTriggerInfo.java b/core/src/main/java/org/sterl/spring/persistent_tasks/api/CronTriggerInfo.java new file mode 100644 index 000000000..941b10ea1 --- /dev/null +++ b/core/src/main/java/org/sterl/spring/persistent_tasks/api/CronTriggerInfo.java @@ -0,0 +1,34 @@ +package org.sterl.spring.persistent_tasks.api; + +import org.springframework.lang.Nullable; + +import lombok.Data; + +/** + * Representing a cron trigger definition registered in-memory. + */ +@Data +public class CronTriggerInfo { + + /** Unique ID of this cron trigger */ + private String id; + + /** The task that this cron trigger executes */ + private String taskName; + + /** The schedule (cron expression or interval description) */ + private String schedule; + + /** Tag for the created triggers */ + @Nullable + private String tag; + + /** Priority for the created triggers */ + private int priority = 5; + + /** Whether this cron trigger is suspended (not creating new triggers) */ + private boolean suspended = false; + + /** Whether this cron trigger has a state provider configured */ + private boolean hasStateProvider = false; +} diff --git a/core/src/main/java/org/sterl/spring/persistent_tasks/api/IntervalSchedule.java b/core/src/main/java/org/sterl/spring/persistent_tasks/api/IntervalSchedule.java new file mode 100644 index 000000000..c37f4bce6 --- /dev/null +++ b/core/src/main/java/org/sterl/spring/persistent_tasks/api/IntervalSchedule.java @@ -0,0 +1,90 @@ +package org.sterl.spring.persistent_tasks.api; + +import java.time.Duration; +import java.time.OffsetDateTime; +import java.time.temporal.ChronoUnit; +import java.util.Objects; + +/** + * A schedule based on a fixed time interval. + *

+ * Examples: + *

    + *
  • Duration.ofHours(1) - Every hour
  • + *
  • Duration.ofMinutes(30) - Every 30 minutes
  • + *
  • Duration.ofDays(1) - Every day
  • + *
+ */ +public final class IntervalSchedule implements Schedule { + private static final long serialVersionUID = 1L; + + private final Duration interval; + + /** + * Creates a new interval schedule. + * + * @param interval the time interval between executions + * @throws IllegalArgumentException if interval is null, zero, or negative + */ + public IntervalSchedule(Duration interval) { + if (interval == null) { + throw new IllegalArgumentException("Interval cannot be null"); + } + if (interval.isZero() || interval.isNegative()) { + throw new IllegalArgumentException("Interval must be positive: " + interval); + } + this.interval = interval; + } + + @Override + public OffsetDateTime next(OffsetDateTime from) { + return from.plus(interval).truncatedTo(ChronoUnit.SECONDS); + } + + @Override + public String description() { + return "every(" + formatDuration(interval) + ")"; + } + + private String formatDuration(Duration d) { + long days = d.toDays(); + if (days > 0 && d.equals(Duration.ofDays(days))) { + return days + (days == 1 ? " day" : " days"); + } + + long hours = d.toHours(); + if (hours > 0 && d.equals(Duration.ofHours(hours))) { + return hours + (hours == 1 ? " hour" : " hours"); + } + + long minutes = d.toMinutes(); + if (minutes > 0 && d.equals(Duration.ofMinutes(minutes))) { + return minutes + (minutes == 1 ? " minute" : " minutes"); + } + + long seconds = d.getSeconds(); + return seconds + (seconds == 1 ? " second" : " seconds"); + } + + @Override + public String toString() { + return description(); + } + + public Duration getInterval() { + return interval; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + IntervalSchedule that = (IntervalSchedule) o; + return Objects.equals(interval, that.interval); + } + + @Override + public int hashCode() { + return Objects.hash(interval); + } +} diff --git a/core/src/main/java/org/sterl/spring/persistent_tasks/api/Schedule.java b/core/src/main/java/org/sterl/spring/persistent_tasks/api/Schedule.java new file mode 100644 index 000000000..eac5a71d8 --- /dev/null +++ b/core/src/main/java/org/sterl/spring/persistent_tasks/api/Schedule.java @@ -0,0 +1,26 @@ +package org.sterl.spring.persistent_tasks.api; + +import java.io.Serializable; +import java.time.OffsetDateTime; + +/** + * Represents a schedule that can calculate the next execution time. + * Used for recurring tasks (cron expressions or fixed intervals). + */ +public interface Schedule extends Serializable { + + /** + * Calculates the next execution time from the given time. + * + * @param from the reference time to calculate from + * @return the next execution time after the given time + */ + OffsetDateTime next(OffsetDateTime from); + + /** + * Returns a human-readable description of this schedule. + * + * @return description of the schedule + */ + String description(); +} diff --git a/core/src/main/java/org/sterl/spring/persistent_tasks/api/TaskId.java b/core/src/main/java/org/sterl/spring/persistent_tasks/api/TaskId.java index 9e45cf3f8..f7bf729ea 100644 --- a/core/src/main/java/org/sterl/spring/persistent_tasks/api/TaskId.java +++ b/core/src/main/java/org/sterl/spring/persistent_tasks/api/TaskId.java @@ -1,16 +1,11 @@ package org.sterl.spring.persistent_tasks.api; import java.io.Serializable; -import java.time.Duration; -import java.time.OffsetDateTime; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import com.github.f4b6a3.uuid.UuidCreator; -import lombok.AccessLevel; -import lombok.RequiredArgsConstructor; - /** * Represents the ID of a persistentTask, which is currently not running. */ @@ -29,6 +24,10 @@ public TriggerBuilder newTrigger() { return new TriggerBuilder<>(this); } + public CronTriggerBuilder newCron() { + return new CronTriggerBuilder<>(this); + } + /** * Creates a new trigger, the ID will stay empty and * will receive an UUID later. @@ -46,91 +45,4 @@ public TriggerRequest newUniqueTrigger(T state) { .correlationId(UuidCreator.getTimeOrderedEpochFast().toString()) .build(); } - - @RequiredArgsConstructor(access = AccessLevel.PRIVATE) - public static class TriggerBuilder { - private final TaskId taskId; - private String id; - private String correlationId; - private String tag; - private TriggerStatus status = TriggerStatus.WAITING; - private T state; - private OffsetDateTime when = OffsetDateTime.now(); - private int priority = TriggerRequest.DEFAULT_PRIORITY; - - public static TriggerBuilder newTrigger(String name) { - return new TriggerBuilder<>(new TaskId(name)); - } - public static TriggerBuilder newTrigger(String name, T state) { - return new TriggerBuilder<>(new TaskId(name)).state(state); - } - public static TriggerBuilder newTrigger(TriggerKey key, T state) { - return new TriggerBuilder<>(new TaskId(key.getTaskName())).id(key.getId()).state(state); - } - public TriggerRequest build() { - var key = TriggerKey.of(id, taskId); - return new TriggerRequest<>(key, status, state, when, priority, correlationId, tag); - } - /** - * The ID of this task, same queued ids are replaced. - */ - public TriggerBuilder id(String id) { - this.id = id; - return this; - } - /** - * An unique ID which is taken over to a chain/set of tasks. - * If task is triggered it in a task, this ID is taken over. - */ - public TriggerBuilder correlationId(String correlationId) { - this.correlationId = correlationId; - return this; - } - public TriggerBuilder tag(String tag) { - this.tag = tag; - return this; - } - public TriggerBuilder state(T state) { - this.state = state; - return this; - } - /** - * The higher the {@link #priority} the earlier this task is picked. - * Same as JMS priority. Default is also 4, like in JMS. - * - * @param priority custom priority e.g. 0-9, also higher numbers are supported - * @return this {@link TriggerBuilder} - */ - public TriggerBuilder priority(int priority) { - this.priority = priority; - return this; - } - /** - * synonym for {@link #runAt(OffsetDateTime)} - */ - public TriggerBuilder when(OffsetDateTime when) { - return runAt(when); - } - public TriggerBuilder runAt(OffsetDateTime when) { - this.when = when; - this.status = TriggerStatus.WAITING; - return this; - } - /** - * synonym for {@link #runAt(OffsetDateTime)} - */ - public TriggerBuilder runAfter(Duration duration) { - runAt(OffsetDateTime.now().plus(duration)); - return this; - } - /** - * Creates a trigger which waits for an external signal and - * will run into {@link TriggerStatus#EXPIRED_SIGNAL} if no signal happens. - */ - public TriggerBuilder waitForSignal(OffsetDateTime timeout) { - this.when = timeout; - this.status = TriggerStatus.AWAITING_SIGNAL; - return this; - } - } } diff --git a/core/src/main/java/org/sterl/spring/persistent_tasks/api/Trigger.java b/core/src/main/java/org/sterl/spring/persistent_tasks/api/Trigger.java index cefa3ac66..4098d8ba2 100644 --- a/core/src/main/java/org/sterl/spring/persistent_tasks/api/Trigger.java +++ b/core/src/main/java/org/sterl/spring/persistent_tasks/api/Trigger.java @@ -42,7 +42,7 @@ public class Trigger { private int executionCount = 0; /** priority, the higher a more priority it will get */ - private int priority = 4; + private int priority = TriggerRequest.DEFAULT_PRIORITY; private TriggerStatus status = TriggerStatus.WAITING; diff --git a/core/src/main/java/org/sterl/spring/persistent_tasks/api/TriggerBuilder.java b/core/src/main/java/org/sterl/spring/persistent_tasks/api/TriggerBuilder.java new file mode 100644 index 000000000..c0689fa3a --- /dev/null +++ b/core/src/main/java/org/sterl/spring/persistent_tasks/api/TriggerBuilder.java @@ -0,0 +1,96 @@ +package org.sterl.spring.persistent_tasks.api; + +import java.io.Serializable; +import java.time.Duration; +import java.time.OffsetDateTime; +import java.time.temporal.ChronoUnit; + +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor(access = AccessLevel.PACKAGE) +public class TriggerBuilder { + private final TaskId taskId; + private String id; + private String correlationId; + private String tag; + private TriggerStatus status = TriggerStatus.WAITING; + private T state; + private OffsetDateTime when = OffsetDateTime.now().truncatedTo(ChronoUnit.SECONDS); + private int priority = TriggerRequest.DEFAULT_PRIORITY; + + public static TriggerBuilder newTrigger(String name) { + return new TriggerBuilder<>(new TaskId(name)); + } + public static TriggerBuilder newTrigger(String name, T state) { + return new TriggerBuilder<>(new TaskId(name)).state(state); + } + public static TriggerBuilder newTrigger(TriggerKey key, T state) { + return new TriggerBuilder<>(new TaskId(key.getTaskName())).id(key.getId()).state(state); + } + public TriggerRequest build() { + var key = TriggerKey.of(id, taskId); + return new TriggerRequest<>(key, status, state, when, priority, correlationId, tag); + } + /** + * The ID of this task, same queued ids are replaced. + */ + public TriggerBuilder id(String id) { + this.id = id; + return this; + } + /** + * An unique ID which is taken over to a chain/set of tasks. + * If task is triggered it in a task, this ID is taken over. + */ + public TriggerBuilder correlationId(String correlationId) { + this.correlationId = correlationId; + return this; + } + public TriggerBuilder tag(String tag) { + this.tag = tag; + return this; + } + public TriggerBuilder state(T state) { + this.state = state; + return this; + } + /** + * The higher the {@link #priority} the earlier this task is picked. + * Same as JMS priority. Default is also 4, like in JMS. + * + * @param priority custom priority e.g. 0-9, also higher numbers are supported + * @return this {@link TriggerBuilder} + */ + public TriggerBuilder priority(int priority) { + this.priority = priority; + return this; + } + /** + * synonym for {@link #runAt(OffsetDateTime)} + */ + public TriggerBuilder when(OffsetDateTime when) { + return runAt(when); + } + public TriggerBuilder runAt(OffsetDateTime when) { + this.when = when; + this.status = TriggerStatus.WAITING; + return this; + } + /** + * synonym for {@link #runAt(OffsetDateTime)} + */ + public TriggerBuilder runAfter(Duration duration) { + runAt(OffsetDateTime.now().plus(duration)); + return this; + } + /** + * Creates a trigger which waits for an external signal and + * will run into {@link TriggerStatus#EXPIRED_SIGNAL} if no signal happens. + */ + public TriggerBuilder waitForSignal(OffsetDateTime timeout) { + this.when = timeout; + this.status = TriggerStatus.AWAITING_SIGNAL; + return this; + } +} \ No newline at end of file diff --git a/core/src/main/java/org/sterl/spring/persistent_tasks/api/TriggerKey.java b/core/src/main/java/org/sterl/spring/persistent_tasks/api/TriggerKey.java index 070cbe865..217ad7a36 100644 --- a/core/src/main/java/org/sterl/spring/persistent_tasks/api/TriggerKey.java +++ b/core/src/main/java/org/sterl/spring/persistent_tasks/api/TriggerKey.java @@ -9,7 +9,6 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import lombok.ToString; /** * Unique key of a trigger during it's execution. But it after that the same key @@ -17,7 +16,6 @@ * is currently scheduled for execution. */ @Data -@ToString @Builder @NoArgsConstructor @AllArgsConstructor diff --git a/core/src/main/java/org/sterl/spring/persistent_tasks/api/event/TriggerTaskCommand.java b/core/src/main/java/org/sterl/spring/persistent_tasks/api/event/TriggerTaskCommand.java index 52bfd7783..b954df6d9 100644 --- a/core/src/main/java/org/sterl/spring/persistent_tasks/api/event/TriggerTaskCommand.java +++ b/core/src/main/java/org/sterl/spring/persistent_tasks/api/event/TriggerTaskCommand.java @@ -5,7 +5,7 @@ import java.util.Collection; import java.util.Collections; -import org.sterl.spring.persistent_tasks.api.TaskId.TriggerBuilder; +import org.sterl.spring.persistent_tasks.api.TriggerBuilder; import org.sterl.spring.persistent_tasks.api.TriggerRequest; /** diff --git a/core/src/main/java/org/sterl/spring/persistent_tasks/scheduler/SchedulerService.java b/core/src/main/java/org/sterl/spring/persistent_tasks/scheduler/SchedulerService.java index dc0baa496..9a5472793 100644 --- a/core/src/main/java/org/sterl/spring/persistent_tasks/scheduler/SchedulerService.java +++ b/core/src/main/java/org/sterl/spring/persistent_tasks/scheduler/SchedulerService.java @@ -47,14 +47,14 @@ public class SchedulerService { @Getter private final String name; private final TriggerService triggerService; - + private final TaskExecutorComponent taskExecutor; private final EditSchedulerStatusComponent editSchedulerStatus; private final RunOrQueueComponent runOrQueue; private final TransactionTemplate trx; - + private final MeterRegistry meterRegistry; @PostConstruct @@ -146,8 +146,7 @@ public List> triggerNextTasks(OffsetDateTime timeDue) { * Runs the given trigger if a free threads are available and the runAt time is * not in the future. * - * @return the reference to the {@link Future} with the key, if no threads are - * available it is resolved + * @return the TriggerKey of the planned trigger */ @Transactional(timeout = 10) public TriggerKey runOrQueue(TriggerRequest triggerRequest) { diff --git a/core/src/main/java/org/sterl/spring/persistent_tasks/scheduler/SchedulerTimer.java b/core/src/main/java/org/sterl/spring/persistent_tasks/scheduler/SchedulerTimer.java index 2b00a649c..2de2cd08d 100644 --- a/core/src/main/java/org/sterl/spring/persistent_tasks/scheduler/SchedulerTimer.java +++ b/core/src/main/java/org/sterl/spring/persistent_tasks/scheduler/SchedulerTimer.java @@ -44,7 +44,7 @@ void rescheduleAbandonedTasks() { try { final var count = s.rescheduleAbandonedTriggers(timeout); if (count.size() > 0) { - log.info("Found {} abandoned tasks for {}. Timeout={}", + log.info("Found {} abandoned tasks for {}. Timeout={}", count.size(), s.getName(), taskTimeout); } } catch (Exception e) { diff --git a/core/src/main/java/org/sterl/spring/persistent_tasks/scheduler/config/SchedulerConfig.java b/core/src/main/java/org/sterl/spring/persistent_tasks/scheduler/config/SchedulerConfig.java index e2268d9e2..d0ab9e8b2 100644 --- a/core/src/main/java/org/sterl/spring/persistent_tasks/scheduler/config/SchedulerConfig.java +++ b/core/src/main/java/org/sterl/spring/persistent_tasks/scheduler/config/SchedulerConfig.java @@ -67,50 +67,34 @@ SchedulerService schedulerService( EditSchedulerStatusComponent editSchedulerStatus, Optional customizer, TransactionTemplate trx) throws UnknownHostException { - + customizer = customizer.isEmpty() ? Optional.of(new SchedulerCustomizer() {}) : customizer; final var name = customizer.get().name(); final var maxShutdownWaitTime = Duration.ofSeconds(10); - return newSchedulerService(name, meterRegistry, triggerService, editSchedulerStatus, - schedulerThreadFactory, maxThreads, + return newSchedulerService(name, meterRegistry, triggerService, + editSchedulerStatus, schedulerThreadFactory, maxThreads, maxShutdownWaitTime, trx); } - @Deprecated - public static SchedulerService newSchedulerService( - String name, - MeterRegistry meterRegistry, - TriggerService triggerService, - EditSchedulerStatusComponent editSchedulerStatus, - int maxThreads, - Duration maxShutdownWaitTime, - TransactionTemplate trx) { - - return newSchedulerService(name, meterRegistry, triggerService, editSchedulerStatus, - SchedulerThreadFactory.DEFAULT_THREAD_POOL_FACTORY, - maxThreads, - maxShutdownWaitTime, trx); - } - public static SchedulerService newSchedulerService( String name, MeterRegistry meterRegistry, TriggerService triggerService, EditSchedulerStatusComponent editSchedulerStatus, SchedulerThreadFactory schedulerThreadFactory, - int maxThreads, + int maxThreads, Duration maxShutdownWaitTime, TransactionTemplate trx) { - + final var taskExecutor = new TaskExecutorComponent(name, triggerService, schedulerThreadFactory, maxThreads); if (maxShutdownWaitTime != null) taskExecutor.setMaxShutdownWaitTime(maxShutdownWaitTime); final var runOrQueue = new RunOrQueueComponent(name, triggerService, taskExecutor); - return new SchedulerService(name, + return new SchedulerService(name, triggerService, - taskExecutor, + taskExecutor, editSchedulerStatus, runOrQueue, trx, diff --git a/core/src/main/java/org/sterl/spring/persistent_tasks/shared/ExtendetConvert.java b/core/src/main/java/org/sterl/spring/persistent_tasks/shared/ExtendetConvert.java index 02781f74f..e4c14ef21 100644 --- a/core/src/main/java/org/sterl/spring/persistent_tasks/shared/ExtendetConvert.java +++ b/core/src/main/java/org/sterl/spring/persistent_tasks/shared/ExtendetConvert.java @@ -8,25 +8,32 @@ import org.springframework.core.convert.converter.Converter; import org.springframework.data.domain.Page; import org.springframework.data.web.PagedModel; +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; @FunctionalInterface public interface ExtendetConvert extends Converter, Function { - default T apply(S s) { + @Nullable + default T apply(@Nullable S s) { + if (s == null) return null; return convert(s); } + @NonNull default Optional convert(Optional s) { if (s.isEmpty()) return Optional.empty(); return Optional.of(convert(s.get())); } + @NonNull default List convert(Collection source) { return source.stream().map(this::convert).toList(); } - default PagedModel toPage(Page page) { + @NonNull + default PagedModel toPage(@NonNull Page page) { return new PagedModel<>(page.map(this::apply)); } } diff --git a/core/src/main/java/org/sterl/spring/persistent_tasks/shared/repository/TriggerRepository.java b/core/src/main/java/org/sterl/spring/persistent_tasks/shared/repository/TriggerRepository.java index 4b55dcde8..1369d9304 100644 --- a/core/src/main/java/org/sterl/spring/persistent_tasks/shared/repository/TriggerRepository.java +++ b/core/src/main/java/org/sterl/spring/persistent_tasks/shared/repository/TriggerRepository.java @@ -126,7 +126,7 @@ SELECT COUNT(e.id) FROM #{#entityName} e WHERE e.data.status IN ( :status ) """) - long countByStatus(@Param("status") Set status); + long countByStatus(@Param("status") Set status); @Query(""" DELETE FROM #{#entityName} e diff --git a/core/src/main/java/org/sterl/spring/persistent_tasks/task/TaskService.java b/core/src/main/java/org/sterl/spring/persistent_tasks/task/TaskService.java index 5217e5d2d..11524e13e 100644 --- a/core/src/main/java/org/sterl/spring/persistent_tasks/task/TaskService.java +++ b/core/src/main/java/org/sterl/spring/persistent_tasks/task/TaskService.java @@ -127,4 +127,5 @@ public TaskId replace(TaskId id, PersistentTask taskId) { + var cronAnnotation = AnnotationUtils.findAnnotation(task.getClass(), + CronTrigger.class); + + if (cronAnnotation == null) return; + + CronTriggerBuilder builder = taskId.newCron(); + + // Set ID if provided + if (cronAnnotation.id() != null && !cronAnnotation.id().isBlank()) { + builder.id(cronAnnotation.id()); + } + + // Set cron expression or fixed delay + if (cronAnnotation.cron() != null && !cronAnnotation.cron().isBlank()) { + builder.cron(cronAnnotation.cron()); + } else if (cronAnnotation.fixedDelay() > 0) { + long millis = cronAnnotation.timeUnit().toMillis(cronAnnotation.fixedDelay()); + builder.after(Duration.ofMillis(millis)); + } else { + throw new IllegalArgumentException("@CronTrigger on " + + taskId + " has neither cron nor fixedDelay configured"); + } + + var cronTrigger = builder.build(); + triggerService.register(cronTrigger); + } private void addTaskIdIfMissing(GenericApplicationContext context, TaskId id) { final var taskIdContextName = id.name() + "Id"; diff --git a/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/CronTrigger.java b/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/CronTrigger.java new file mode 100644 index 000000000..d5c8b8aba --- /dev/null +++ b/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/CronTrigger.java @@ -0,0 +1,48 @@ +package org.sterl.spring.persistent_tasks.trigger; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.concurrent.TimeUnit; + +import org.springframework.aot.hint.annotation.Reflective; + +@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Reflective +public @interface CronTrigger { + /** + * A unique id for this trigger which is optional, by default the cron expression or the delay is used. + */ + String id() default ""; + /** + * A cron-like expression, extending the usual UN*X definition to include triggers + * on the second, minute, hour, day of month, month, and day of week. + *

For example, {@code "0 * * * * MON-FRI"} means once per minute on weekdays + * (at the top of the minute - the 0th second). + *

The fields read from left to right are interpreted as follows. + *

    + *
  • second
  • + *
  • minute
  • + *
  • hour
  • + *
  • day of month
  • + *
  • month
  • + *
  • day of week
  • + *
+ *

The special value {@link #CRON_DISABLED "-"} indicates a disabled cron + * trigger, primarily meant for externally specified values resolved by a + * ${...} placeholder. + * @return an expression that can be parsed to a cron schedule + * @see org.springframework.scheduling.support.CronExpression#parse(String) + */ + String cron() default ""; + + /** the delay time between the cron triggers */ + long fixedDelay() default -1; + + /** the unit of the fixed delay */ + TimeUnit timeUnit() default TimeUnit.MILLISECONDS; +} diff --git a/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/TriggerService.java b/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/TriggerService.java index b034add21..0f0034584 100644 --- a/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/TriggerService.java +++ b/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/TriggerService.java @@ -1,17 +1,22 @@ package org.sterl.spring.persistent_tasks.trigger; import java.io.Serializable; +import java.time.Duration; import java.time.OffsetDateTime; import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.function.Function; +import org.apache.commons.lang3.StringUtils; +import org.springframework.boot.actuate.quartz.QuartzEndpoint.QuartzTriggerGroupSummaryDescriptor.Triggers; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.lang.Nullable; +import org.springframework.scheduling.config.Task; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; +import org.sterl.spring.persistent_tasks.api.CronTriggerBuilder; import org.sterl.spring.persistent_tasks.api.TaskId; import org.sterl.spring.persistent_tasks.api.TriggerGroup; import org.sterl.spring.persistent_tasks.api.TriggerKey; @@ -19,19 +24,26 @@ import org.sterl.spring.persistent_tasks.api.TriggerSearch; import org.sterl.spring.persistent_tasks.api.TriggerStatus; import org.sterl.spring.persistent_tasks.shared.DateUtil; +import org.sterl.spring.persistent_tasks.shared.model.TriggerEntity; import org.sterl.spring.persistent_tasks.shared.stereotype.TransactionalService; import org.sterl.spring.persistent_tasks.task.TaskService; import org.sterl.spring.persistent_tasks.trigger.component.EditTriggerComponent; import org.sterl.spring.persistent_tasks.trigger.component.FailTriggerComponent; import org.sterl.spring.persistent_tasks.trigger.component.LockNextTriggerComponent; +import org.sterl.spring.persistent_tasks.trigger.component.QueueCronTriggerComponent; import org.sterl.spring.persistent_tasks.trigger.component.ReadTriggerComponent; import org.sterl.spring.persistent_tasks.trigger.component.RunTriggerComponent; import org.sterl.spring.persistent_tasks.trigger.component.StateSerializationComponent; +import org.sterl.spring.persistent_tasks.trigger.model.CronTriggerEntity; import org.sterl.spring.persistent_tasks.trigger.model.RunningTriggerEntity; +import org.sterl.spring.persistent_tasks.trigger.repository.CronTriggerRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +/** + * Manages {@link Triggers} to {@link Task}s and the data. + */ @TransactionalService @RequiredArgsConstructor @Slf4j @@ -44,6 +56,8 @@ public class TriggerService { private final FailTriggerComponent failTrigger; private final LockNextTriggerComponent lockNextTrigger; private final StateSerializationComponent stateSerialization; + private final CronTriggerRepository cronTriggerRepository; + private final QueueCronTriggerComponent queueCronTrigger; /** * Executes the given trigger directly in the current thread @@ -118,6 +132,7 @@ public Page searchGroupedTriggers(@Nullable TriggerSearch search, public void deleteAll() { this.editTrigger.deleteAll(); + this.cronTriggerRepository.deleteAll(); } /** @@ -241,4 +256,55 @@ public Optional updateRunAt(TriggerKey key, OffsetDateTime return t; }); } + + /** + * Adds a {@link CronTriggerEntity} for recurring tasks. + * It will also add the {@link TriggerEntity} if required. + * + * @param cronTrigger the {@link CronTriggerEntity} to manage + * @return true if a new {@link TriggerEntity} was created, false if one already exists + */ + @Transactional(propagation = Propagation.SUPPORTS) + public boolean register(CronTriggerEntity cronTrigger) { + this.taskService.assertIsKnown(cronTrigger.getTaskId()); + this.cronTriggerRepository.register(cronTrigger); + return queueCronTrigger.execute(cronTrigger); + } + + public boolean register(CronTrigger cronAnnotation, TaskId taskId) { + CronTriggerBuilder builder = taskId.newCron(); + + // Set ID if provided + if (cronAnnotation.id() != null && !cronAnnotation.id().isBlank()) { + builder.id(cronAnnotation.id()); + } + + // Set cron expression or fixed delay + if (StringUtils.isNotBlank(cronAnnotation.cron())) { + builder.cron(cronAnnotation.cron()); + } else if (cronAnnotation.fixedDelay() > 0) { + builder.after(Duration.ofMillis(cronAnnotation.timeUnit().toMillis(cronAnnotation.fixedDelay()))); + } else { + throw new IllegalArgumentException("@" + CronTrigger.class.getSimpleName() + " on " + + taskId + " has neither cron nor fixedDelay configured"); + } + + return register(builder.build()); + } + + public Collection> cronTriggers() { + return this.cronTriggerRepository.getAll(); + } + + public boolean suspendCron(TriggerKey triggerKey) { + return this.cronTriggerRepository.suspend(triggerKey); + } + + public boolean resumeCron(TriggerKey triggerKey) { + return this.cronTriggerRepository.resume(triggerKey); + } + + public int queueCronTrigger() { + return this.queueCronTrigger.execute(); + } } diff --git a/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/TriggerTimer.java b/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/TriggerTimer.java index b9a71e1f0..5d5d54233 100644 --- a/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/TriggerTimer.java +++ b/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/TriggerTimer.java @@ -9,18 +9,39 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +/** + * Timer component that triggers scheduled operations for the TriggerService. + * Contains no business logic - only delegates to service and component classes. + */ @TimersEnabled @Component @RequiredArgsConstructor @Slf4j public class TriggerTimer { - + private final TriggerService triggerService; + /** + * Checks for expired awaiting triggers and marks them as timed out. + */ @Scheduled(fixedDelayString = "${spring.persistent-tasks.poll-awaiting-trigger-timeout:300}", timeUnit = TimeUnit.SECONDS) void checkAwaitingTriggersForTimeout() { int expired = triggerService.expireTimeoutTriggers().size(); if (expired > 0) log.info("{} triggers have not received the signal, wait expired!", expired); else log.debug("No expired triggers found."); } + + /** + * Ensures all non-suspended cron triggers have corresponding database triggers. + * Delegates to QueueCronTriggerComponent for actual logic. + */ + @Scheduled(fixedDelayString = "${spring.persistent-tasks.queue-cron-triggers:60}", timeUnit = TimeUnit.SECONDS) + void queueCronTrigger() { + int created = triggerService.queueCronTrigger(); + if (created > 0) { + log.info("Created {} cron triggers", created); + } else { + log.debug("No new cron triggers needed."); + } + } } diff --git a/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/api/ToCronTriggerInfo.java b/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/api/ToCronTriggerInfo.java new file mode 100644 index 000000000..4db3be20c --- /dev/null +++ b/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/api/ToCronTriggerInfo.java @@ -0,0 +1,24 @@ +package org.sterl.spring.persistent_tasks.trigger.api; + +import org.springframework.lang.NonNull; +import org.sterl.spring.persistent_tasks.api.CronTriggerInfo; +import org.sterl.spring.persistent_tasks.shared.ExtendetConvert; +import org.sterl.spring.persistent_tasks.trigger.model.CronTriggerEntity; + +public enum ToCronTriggerInfo implements ExtendetConvert, CronTriggerInfo> { + INSTANCE; + + @Override + public CronTriggerInfo convert(@NonNull CronTriggerEntity CronTriggerInfo) { + var info = new CronTriggerInfo(); + info.setId(CronTriggerInfo.getId()); + info.setTaskName(CronTriggerInfo.getTaskId().name()); + info.setSchedule(CronTriggerInfo.getSchedule().description()); + info.setTag(CronTriggerInfo.getTag()); + info.setPriority(CronTriggerInfo.getPriority()); + info.setSuspended(CronTriggerInfo.isSuspended()); + info.setHasStateProvider(CronTriggerInfo.getStateProvider() != null); + return info; + } + +} diff --git a/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/api/TriggerResource.java b/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/api/TriggerResource.java index a49725a0a..3fcb00c1c 100644 --- a/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/api/TriggerResource.java +++ b/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/api/TriggerResource.java @@ -1,6 +1,7 @@ package org.sterl.spring.persistent_tasks.trigger.api; import java.time.OffsetDateTime; +import java.util.List; import java.util.Optional; import org.springframework.data.domain.Pageable; @@ -14,6 +15,7 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import org.sterl.spring.persistent_tasks.api.CronTriggerInfo; import org.sterl.spring.persistent_tasks.api.Trigger; import org.sterl.spring.persistent_tasks.api.TriggerGroup; import org.sterl.spring.persistent_tasks.api.TriggerKey; @@ -70,4 +72,36 @@ public Optional cancelTrigger( return converter.convert(triggerService.cancel(new TriggerKey(id, taskName))); } + + /** + * Lists all registered cron triggers (scheduled triggers). + * + * @return list of all cron trigger definitions + */ + @GetMapping("cron-triggers") + public List listCronTriggerInfos() { + return ToCronTriggerInfo.INSTANCE.convert(triggerService.cronTriggers()); + } + + /** + * Suspends a cron trigger, preventing new trigger instances from being created. + * + * @param id the cron trigger ID to suspend + * @return true if found, otherweise false + */ + @DeleteMapping("cron-triggers/{taskName}/{id}") + public boolean suspendCron(@PathVariable("taskName") String taskName, @PathVariable("id") String id) { + return this.triggerService.suspendCron(new TriggerKey(id, taskName)); + } + + /** + * Resumes a suspended cron trigger, allowing new trigger instances to be created. + * + * @param id the cron trigger ID to resume + * @return true if found, otherweise false + */ + @PostMapping("cron-triggers/{taskName}/{id}") + public boolean resumeCron(@PathVariable("taskName") String taskName, @PathVariable("id") String id) { + return this.triggerService.resumeCron(new TriggerKey(id, taskName)); + } } diff --git a/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/component/QueueCronTriggerComponent.java b/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/component/QueueCronTriggerComponent.java new file mode 100644 index 000000000..aae55374e --- /dev/null +++ b/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/component/QueueCronTriggerComponent.java @@ -0,0 +1,70 @@ +package org.sterl.spring.persistent_tasks.trigger.component; + +import java.io.Serializable; + +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.stereotype.Component; +import org.springframework.transaction.support.TransactionTemplate; +import org.sterl.spring.persistent_tasks.trigger.model.CronTriggerEntity; +import org.sterl.spring.persistent_tasks.trigger.repository.CronTriggerRepository; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +/** + * Create new trigger for {@link CronTriggerEntity}s if required. + */ +@Component +@RequiredArgsConstructor +@Slf4j +public class QueueCronTriggerComponent { + + private final ReadTriggerComponent readTrigger; + private final EditTriggerComponent editTrigger; + private final TransactionTemplate trx; + private final CronTriggerRepository cronTriggerRepository; + + /** + * Ensures all non-suspended cron triggers have corresponding database triggers. + * Creates missing triggers with next scheduled execution time. + * + * @return count of created triggers + */ + public int execute() { + var cronTriggers = cronTriggerRepository.getAll(); + int created = 0; + + for (CronTriggerEntity cronTrigger : cronTriggers) { + if (cronTrigger.isSuspended()) { + continue; // Skip suspended cron triggers + } + + if (execute(cronTrigger)) { + created++; + } + } + + return created; + } + + /** + * Creates a new trigger instance for the given {@link CronTriggerEntity} if none exist. + * + * @param cronTrigger the cron trigger definition + * @return true if created, false if already exists + */ + public boolean execute(CronTriggerEntity cronTrigger) { + var key = cronTrigger.key(); + var allreadyQueued = trx.execute(t -> readTrigger.countByKey(key)); + if (allreadyQueued.longValue() > 0) return false; + + try { + trx.execute(t -> editTrigger.addTrigger(cronTrigger.newTriggerRequest())); + return true; + } catch (DataIntegrityViolationException e) { + // Another node created the trigger - ignore + log.info("Trigger for cron {} already exists (race condition)", cronTrigger.getId()); + return false; + } + } +} diff --git a/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/component/ReadTriggerComponent.java b/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/component/ReadTriggerComponent.java index 6ec08ba2f..d302e4c32 100644 --- a/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/component/ReadTriggerComponent.java +++ b/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/component/ReadTriggerComponent.java @@ -36,6 +36,10 @@ public class ReadTriggerComponent { public long countByTaskName(@NotNull String name) { return triggerRepository.countByTaskName(name); } + + public long countByKey(@NotNull TriggerKey key) { + return triggerRepository.countByKey(key); + } public long countByStatus(@Nullable TriggerStatus status) { if (status == null) return triggerRepository.count(); diff --git a/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/model/CronTriggerEntity.java b/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/model/CronTriggerEntity.java new file mode 100644 index 000000000..9d8be3891 --- /dev/null +++ b/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/model/CronTriggerEntity.java @@ -0,0 +1,79 @@ +package org.sterl.spring.persistent_tasks.trigger.model; + +import java.io.Serializable; +import java.time.OffsetDateTime; +import java.util.function.Supplier; + +import org.sterl.spring.persistent_tasks.api.Schedule; +import org.sterl.spring.persistent_tasks.api.TaskId; +import org.sterl.spring.persistent_tasks.api.TriggerKey; +import org.sterl.spring.persistent_tasks.api.TriggerRequest; + +import lombok.Builder; +import lombok.ToString; +import lombok.Value; +import lombok.With; + +/** + * Represents a registered cron trigger definition (in-memory). + * Cron triggers are periodically checked and create actual trigger instances in the database. + * + * @param the state type for the task + */ +@Value +@Builder +@ToString(of = {"id", "taskId", "schedule", "suspended"}) +public class CronTriggerEntity { + + /** + * Unique identifier for this cron trigger. + * Used as the trigger ID when creating database trigger instances. + */ + String id; + + /** + * The task to execute on schedule. + */ + TaskId taskId; + + /** + * The schedule (cron expression or interval). + */ + Schedule schedule; + + /** + * Optional state provider - called each time a trigger is created. + * If null, trigger will have null state. + */ + Supplier stateProvider; + + String tag; + + /** + * Priority for created triggers (0-9, higher = more important). + */ + @Builder.Default + int priority = 4; + + /** + * If true, this cron trigger is suspended and won't create new triggers. + */ + @With + @Builder.Default + boolean suspended = false; + + public TriggerKey key() { + return TriggerKey.of(id, taskId); + } + + public TriggerRequest newTriggerRequest() { + var nextRun = schedule.next(OffsetDateTime.now()); + + return taskId.newTrigger(stateProvider == null ? null : stateProvider.get()) + .id(getId()) + .runAt(nextRun) + .tag(getTag()) + .priority(getPriority()) + .build(); + } +} diff --git a/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/repository/CronTriggerRepository.java b/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/repository/CronTriggerRepository.java new file mode 100644 index 000000000..4c1ddf4bf --- /dev/null +++ b/core/src/main/java/org/sterl/spring/persistent_tasks/trigger/repository/CronTriggerRepository.java @@ -0,0 +1,116 @@ +package org.sterl.spring.persistent_tasks.trigger.repository; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import org.springframework.lang.NonNull; +import org.springframework.stereotype.Component; +import org.sterl.spring.persistent_tasks.api.TriggerKey; +import org.sterl.spring.persistent_tasks.trigger.model.CronTriggerEntity; + +import lombok.extern.slf4j.Slf4j; + +/** + * In-memory repository for cron trigger definitions. + * Cron triggers are registered at startup and live in memory like task definitions. + */ +@Slf4j +@Component +public class CronTriggerRepository { + + private final ConcurrentMap> cronTriggerEntities = new ConcurrentHashMap<>(); + + /** + * Registers a cron trigger. + * + * @param cronTrigger the cron trigger to register + * @return they unique {@link TriggerKey} + * @throws IllegalStateException if a trigger with this ID already exists + */ + public TriggerKey register(@NonNull CronTriggerEntity cronTrigger) { + final var key = cronTrigger.key(); + if (cronTriggerEntities.containsKey(key)) { + throw new IllegalStateException("Cron trigger with '" + key + "' is already registered!"); + } + cronTriggerEntities.put(key , cronTrigger); + log.info("Registered cron for={}", cronTrigger); + return key; + } + + /** + * Gets a cron trigger by ID. + * + * @param id the trigger ID + * @return the cron trigger if found + */ + public Optional> get(@NonNull TriggerKey key) { + return Optional.ofNullable(cronTriggerEntities.get(key)); + } + + /** + * Returns all registered cron triggers. + * + * @return collection of all cron triggers + */ + public Collection> getAll() { + return cronTriggerEntities.values(); + } + + public boolean suspend(@NonNull TriggerKey key) { + var cronTrigger = cronTriggerEntities.get(key); + if (cronTrigger != null) { + cronTriggerEntities.put(key, cronTrigger.withSuspended(true)); + log.info("Suspended cron={}", key); + return true; + } + return false; + } + public boolean resume(@NonNull TriggerKey key) { + var cronTrigger = cronTriggerEntities.get(key); + if (cronTrigger != null) { + cronTriggerEntities.put(key, cronTrigger.withSuspended(false)); + log.info("Resumed cron={}", key); + return true; + } + return false; + } + + /** + * Unregisters a cron trigger. + * + * @param id the trigger ID + * @return the removed cron trigger, or empty if not found + */ + public Optional> unregister(@NonNull TriggerKey key) { + CronTriggerEntity removed = cronTriggerEntities.remove(key); + if (removed != null) { + log.info("Unregistered cron={}", key); + } + return Optional.ofNullable(removed); + } + + /** + * Removes all cron triggers (for testing). + */ + public void deleteAll() { + log.warn("*** All cron triggers {} will be removed now! ***", cronTriggerEntities.size()); + cronTriggerEntities.clear(); + } + + /** + * Returns the number of registered cron triggers. + */ + public int size() { + return cronTriggerEntities.size(); + } + + /** + * Checks if a cron trigger with the given ID exists. + */ + public boolean contains(@NonNull TriggerKey key) { + return cronTriggerEntities.containsKey(key); + } +} diff --git a/core/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/core/src/main/resources/META-INF/additional-spring-configuration-metadata.json index b44b68e8b..a7683f9b5 100644 --- a/core/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/core/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -6,6 +6,13 @@ "description": "The interval at which the scheduler checks for new tasks in seconds.", "defaultValue": 60 }, + { + "name": "spring.persistent-tasks.queue-cron-triggers", + "type": "java.lang.Integer", + "description": "The interval at which the trigger component checks if cron trigger has to be created in seconds.", + "defaultValue": 60 + }, + { "name": "spring.persistent-tasks.poll-abandoned-triggers", "type": "java.lang.Integer", diff --git a/core/src/test/java/org/sterl/spring/persistent_task/api/TaskIdTest.java b/core/src/test/java/org/sterl/spring/persistent_task/api/TaskIdTest.java index 39c9516ef..7c4b130c6 100644 --- a/core/src/test/java/org/sterl/spring/persistent_task/api/TaskIdTest.java +++ b/core/src/test/java/org/sterl/spring/persistent_task/api/TaskIdTest.java @@ -3,7 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.Test; -import org.sterl.spring.persistent_tasks.api.TaskId.TriggerBuilder; +import org.sterl.spring.persistent_tasks.api.TriggerBuilder; class TaskIdTest { diff --git a/core/src/test/java/org/sterl/spring/persistent_tasks/AbstractSpringTest.java b/core/src/test/java/org/sterl/spring/persistent_tasks/AbstractSpringTest.java index d612a7d31..ff285e247 100644 --- a/core/src/test/java/org/sterl/spring/persistent_tasks/AbstractSpringTest.java +++ b/core/src/test/java/org/sterl/spring/persistent_tasks/AbstractSpringTest.java @@ -3,6 +3,7 @@ import java.net.UnknownHostException; import java.time.Duration; import java.util.Optional; +import java.util.concurrent.TimeUnit; import org.awaitility.Awaitility; import org.junit.jupiter.api.AfterEach; @@ -33,6 +34,7 @@ import org.sterl.spring.persistent_tasks.task.TaskService; import org.sterl.spring.persistent_tasks.test.AsyncAsserts; import org.sterl.spring.persistent_tasks.test.PersistentTaskTestService; +import org.sterl.spring.persistent_tasks.trigger.CronTrigger; import org.sterl.spring.persistent_tasks.trigger.TriggerService; import org.sterl.spring.persistent_tasks.trigger.component.StateSerializationComponent; import org.sterl.spring.persistent_tasks.trigger.model.RunningTriggerEntity; @@ -114,7 +116,8 @@ SchedulerService schedulerA( } @Bean - SchedulerService schedulerB(TriggerService triggerService, + SchedulerService schedulerB( + TriggerService triggerService, MeterRegistry meterRegistry, EditSchedulerStatusComponent editSchedulerStatus, SchedulerThreadFactory threadFactory, @@ -142,6 +145,7 @@ PersistentTask task2(AsyncAsserts asserts) { @Component(Task3.NAME) @RequiredArgsConstructor + @CronTrigger(fixedDelay = 6, timeUnit = TimeUnit.HOURS) public static class Task3 implements PersistentTask { public static final String NAME = "task3"; public static final TaskId ID = new TaskId<>(NAME); diff --git a/core/src/test/java/org/sterl/spring/persistent_tasks/history/HistoryServiceTest.java b/core/src/test/java/org/sterl/spring/persistent_tasks/history/HistoryServiceTest.java index 833e20eba..9d29038f6 100644 --- a/core/src/test/java/org/sterl/spring/persistent_tasks/history/HistoryServiceTest.java +++ b/core/src/test/java/org/sterl/spring/persistent_tasks/history/HistoryServiceTest.java @@ -75,7 +75,10 @@ void testTriggerHistoryTrx() { final var trigger = persistentTaskService.queue(Task3.ID.newUniqueTrigger("Hallo")); // WHEN hibernateAsserts.reset(); - schedulerService.triggerNextTasks().forEach(t -> { + + var futures = schedulerService.triggerNextTasks(); + assertThat(futures).hasSize(1); + futures.forEach(t -> { try {t.get();} catch (Exception ex) {throw new RuntimeException(ex);} }); diff --git a/core/src/test/java/org/sterl/spring/persistent_tasks/scheduler/SchedulerServiceTest.java b/core/src/test/java/org/sterl/spring/persistent_tasks/scheduler/SchedulerServiceTest.java index 49fd6526a..e94f731cb 100644 --- a/core/src/test/java/org/sterl/spring/persistent_tasks/scheduler/SchedulerServiceTest.java +++ b/core/src/test/java/org/sterl/spring/persistent_tasks/scheduler/SchedulerServiceTest.java @@ -15,7 +15,7 @@ import org.sterl.spring.persistent_tasks.AbstractSpringTest.TaskConfig.Task3; import org.sterl.spring.persistent_tasks.PersistentTaskService; import org.sterl.spring.persistent_tasks.api.TaskId; -import org.sterl.spring.persistent_tasks.api.TaskId.TriggerBuilder; +import org.sterl.spring.persistent_tasks.api.TriggerBuilder; import org.sterl.spring.persistent_tasks.api.TriggerKey; import org.sterl.spring.persistent_tasks.api.TriggerRequest; import org.sterl.spring.persistent_tasks.api.TriggerStatus; diff --git a/core/src/test/java/org/sterl/spring/persistent_tasks/scheduler/SchedulerServiceTransactionTest.java b/core/src/test/java/org/sterl/spring/persistent_tasks/scheduler/SchedulerServiceTransactionTest.java index dc09f08b3..df771e8f1 100644 --- a/core/src/test/java/org/sterl/spring/persistent_tasks/scheduler/SchedulerServiceTransactionTest.java +++ b/core/src/test/java/org/sterl/spring/persistent_tasks/scheduler/SchedulerServiceTransactionTest.java @@ -19,7 +19,7 @@ import org.sterl.spring.persistent_tasks.AbstractSpringTest; import org.sterl.spring.persistent_tasks.PersistentTaskService; import org.sterl.spring.persistent_tasks.api.RetryStrategy; -import org.sterl.spring.persistent_tasks.api.TaskId.TriggerBuilder; +import org.sterl.spring.persistent_tasks.api.TriggerBuilder; import org.sterl.spring.persistent_tasks.api.TriggerKey; import org.sterl.spring.persistent_tasks.api.TriggerStatus; import org.sterl.spring.persistent_tasks.api.task.PersistentTask; @@ -101,7 +101,10 @@ void testSaveNoTransactions() throws Exception { // WHEN hibernateAsserts.reset(); COUNTDOWN.countDown(); - schedulerService.triggerNextTasks().forEach(t -> { + var futures = schedulerService.triggerNextTasks(); + // AND + assertThat(futures).hasSize(1); + futures.forEach(t -> { try {t.get();} catch (Exception ex) {throw new RuntimeException(ex);} }); diff --git a/core/src/test/java/org/sterl/spring/persistent_tasks/task/TaskTransactionTest.java b/core/src/test/java/org/sterl/spring/persistent_tasks/task/TaskTransactionTest.java index 1824805c1..1c35ebfc8 100644 --- a/core/src/test/java/org/sterl/spring/persistent_tasks/task/TaskTransactionTest.java +++ b/core/src/test/java/org/sterl/spring/persistent_tasks/task/TaskTransactionTest.java @@ -14,7 +14,7 @@ import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import org.sterl.spring.persistent_tasks.AbstractSpringTest; -import org.sterl.spring.persistent_tasks.api.TaskId.TriggerBuilder; +import org.sterl.spring.persistent_tasks.api.TriggerBuilder; import org.sterl.spring.persistent_tasks.api.task.PersistentTask; import org.sterl.spring.persistent_tasks.api.task.TransactionalTask; import org.sterl.spring.persistent_tasks.task.util.ReflectionUtil; diff --git a/core/src/test/java/org/sterl/spring/persistent_tasks/trigger/CronTriggerTest.java b/core/src/test/java/org/sterl/spring/persistent_tasks/trigger/CronTriggerTest.java new file mode 100644 index 000000000..7f1a5adc0 --- /dev/null +++ b/core/src/test/java/org/sterl/spring/persistent_tasks/trigger/CronTriggerTest.java @@ -0,0 +1,130 @@ +package org.sterl.spring.persistent_tasks.trigger; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.Duration; +import java.time.OffsetDateTime; +import java.util.UUID; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.data.domain.Pageable; +import org.sterl.spring.persistent_tasks.AbstractSpringTest; +import org.sterl.spring.persistent_tasks.AbstractSpringTest.TaskConfig.Task3; +import org.sterl.spring.persistent_tasks.api.TaskId; + +class CronTriggerTest extends AbstractSpringTest { + + @Autowired + private TriggerService subject; + + @Autowired + private TaskId task1Id; + @Autowired + private TaskId task2Id; + @Autowired + private TaskId task3Id; + + @Test + void testAddCron() throws Exception { + // GIVEN + final var cron = task1Id.newCron() + .after(Duration.ofHours(1)) + .id("some-id-to-be-refactoring-save") + .build(); + + // WHEN + final var triggerId = subject.register(cron); + + // THEN + assertThat(triggerId).isTrue(); + hibernateAsserts.assertTrxCount(2); + // one for the trigger and one for the history + hibernateAsserts.assertInsertCount(2); + // AND + assertThat(subject.countTriggers(cron.getTaskId())).isOne(); + assertThat(subject.countTriggers(task2Id)).isZero(); + } + + @Test + void test_cron_triggers_are_not_create_if_exisits() throws Exception { + // GIVEN + final var cron1 = task1Id.newCron() + .after(Duration.ofHours(1)) + .build(); + final var cron2 = task2Id.newCron() + .after(Duration.ofHours(1)) + .build(); + + subject.register(cron1); + subject.register(cron2); + + // WHEN + var count = subject.queueCronTrigger(); + + // THEN - as by default we should create all which are needed + assertThat(count).isZero(); + } + + @Test + void test_replan_missing_cron_triggers() throws Exception { + // GIVEN + final var cron1 = task2Id.newCron() + .after(Duration.ofSeconds(1)) + .build(); + final var cron2 = task3Id.newCron() + .after(Duration.ofHours(1)) + .build(); + + subject.register(cron1); + subject.register(cron2); + assertThat(subject.countTriggers()).isEqualTo(2); + + var run = persistentTaskTestService.runAllDueTrigger(OffsetDateTime.now().plusMinutes(1)); + + // WHEN + assertThat(run).hasSize(1); + assertThat(subject.countTriggers()).isOne(); + var count = subject.queueCronTrigger(); + + // THEN - as by default we should create all which are needed + assertThat(count).isOne(); + } + + @Test + void test_cron_test_builder() throws Exception { + // GIVEN + final var anyState = "my-cool-state-" + UUID.randomUUID(); + final var cron = task3Id.newCron() + .after(Duration.ofMinutes(1)) + .stateProvider(() -> anyState) + .build(); + assertThat(subject.countTriggers(task3Id)).isZero(); + + // WHEN + var task = subject.register(cron); + assertThat(task).isTrue(); + + // THEN + var triggers = subject.findAllTriggers(task3Id, Pageable.ofSize(10)).getContent(); + assertThat(triggers).hasSize(1); + assertThat(triggers.get(0).getData().getRunAt()).isBetween( + OffsetDateTime.now(), OffsetDateTime.now().plusMinutes(1)); + // AND + persistentTaskTestService.runAllDueTrigger(OffsetDateTime.now().plusHours(1)); + asserts.awaitValueOnce(Task3.NAME + "::" + anyState); + } + + @Test + void test_register_annoation() throws Exception { + // GIVEN + var cronAnnotation = AnnotationUtils.findAnnotation(Task3.class, CronTrigger.class); + + // WHEN + boolean res = subject.register(cronAnnotation, task3Id); + + // THEN + assertThat(res).isTrue(); + } +} diff --git a/core/src/test/java/org/sterl/spring/persistent_tasks/trigger/TriggerServiceTest.java b/core/src/test/java/org/sterl/spring/persistent_tasks/trigger/TriggerServiceTest.java index c08c657cb..4b9b6ee03 100644 --- a/core/src/test/java/org/sterl/spring/persistent_tasks/trigger/TriggerServiceTest.java +++ b/core/src/test/java/org/sterl/spring/persistent_tasks/trigger/TriggerServiceTest.java @@ -17,7 +17,7 @@ import org.sterl.spring.persistent_tasks.AbstractSpringTest.TaskConfig.Task3; import org.sterl.spring.persistent_tasks.PersistentTaskService; import org.sterl.spring.persistent_tasks.api.TaskId; -import org.sterl.spring.persistent_tasks.api.TaskId.TriggerBuilder; +import org.sterl.spring.persistent_tasks.api.TriggerBuilder; import org.sterl.spring.persistent_tasks.api.TriggerKey; import org.sterl.spring.persistent_tasks.api.TriggerRequest; import org.sterl.spring.persistent_tasks.api.TriggerSearch; @@ -225,9 +225,10 @@ void testFailedIsOnRetry() throws Exception { // WHEN var trigger = subject.queue(task.newTrigger().state("Hallo :-)").build()); - subject.run(subject.lockNextTrigger("test")); + var running = subject.run(subject.lockNextTrigger("test")); // THEN + assertThat(running).isPresent(); trigger = triggerService.get(trigger.getKey()).get(); assertThat(trigger.getData().getRunAt()).isAfter(OffsetDateTime.now()); assertThat(trigger.getData().getStatus()).isEqualTo(TriggerStatus.WAITING); @@ -268,9 +269,10 @@ void testFailedTriggerHasDuration() throws Exception { // WHEN var trigger = subject.queue(task.newTrigger().state("Hallo :-)").build()); - subject.run(subject.lockNextTrigger("test")); + var running = subject.run(subject.lockNextTrigger("test")); // THEN + assertThat(running).isPresent(); trigger = triggerService.get(trigger.getKey()).get(); assertThat(trigger.getData().getEnd()).isAfter(trigger.getData().getStart()); assertThat(trigger.getData().getRunningDurationInMs()) @@ -457,13 +459,17 @@ void testRescheduleAbandonedTasksOnlyRunning() { @Test void testUnknownTriggersNoRetry() { // GIVEN + var id = UuidCreator.getTimeOrdered().toString(); var t = triggerRepository.save( - new RunningTriggerEntity(new TriggerKey(UuidCreator.getTimeOrdered().toString(), "fooTask-unknown"))); + new RunningTriggerEntity(new TriggerKey(id, "foo-task-unknown")) + .runAt(OffsetDateTime.now())); // WHEN - persistentTaskTestService.runNextTrigger(); + var triggered = persistentTaskTestService.runNextTrigger(); // WHEN + assertThat(triggered).isPresent(); + assertThat(triggered.get().getKey().getId()).isEqualTo(id); var triggerData = persistentTaskService.getLastTriggerData(t.getKey()).get(); assertThat(triggerData.status()).isEqualTo(TriggerStatus.FAILED); assertThat(triggerData.getData().getExceptionName()).isEqualTo(IllegalStateException.class.getName()); diff --git a/core/src/test/java/org/sterl/spring/persistent_tasks/trigger/api/TriggerResourceTest.java b/core/src/test/java/org/sterl/spring/persistent_tasks/trigger/api/TriggerResourceTest.java index 82e854b65..9d03288c3 100644 --- a/core/src/test/java/org/sterl/spring/persistent_tasks/trigger/api/TriggerResourceTest.java +++ b/core/src/test/java/org/sterl/spring/persistent_tasks/trigger/api/TriggerResourceTest.java @@ -17,8 +17,8 @@ import org.sterl.spring.persistent_tasks.AbstractSpringTest; import org.sterl.spring.persistent_tasks.AbstractSpringTest.TaskConfig.Task3; import org.sterl.spring.persistent_tasks.api.TaskId; -import org.sterl.spring.persistent_tasks.api.TaskId.TriggerBuilder; import org.sterl.spring.persistent_tasks.api.Trigger; +import org.sterl.spring.persistent_tasks.api.TriggerBuilder; import org.sterl.spring.persistent_tasks.api.TriggerKey; import org.sterl.spring.persistent_tasks.api.TriggerStatus; import org.sterl.spring.persistent_tasks.shared.model.TriggerEntity; diff --git a/db/pom.xml b/db/pom.xml index 908baa455..026b6b672 100644 --- a/db/pom.xml +++ b/db/pom.xml @@ -6,7 +6,7 @@ org.sterl.spring spring-persistent-tasks-root - 2.3.2-SNAPSHOT + 2.4.0-SNAPSHOT ../pom.xml diff --git a/doc/docs/assets/cron-jobs.png b/doc/docs/assets/cron-jobs.png new file mode 100644 index 000000000..41d374028 Binary files /dev/null and b/doc/docs/assets/cron-jobs.png differ diff --git a/doc/docs/assets/dashboard.png b/doc/docs/assets/dashboard.png index c80660ce1..55f1b054c 100644 Binary files a/doc/docs/assets/dashboard.png and b/doc/docs/assets/dashboard.png differ diff --git a/doc/docs/assets/history-page.png b/doc/docs/assets/history-page.png new file mode 100644 index 000000000..f2955e7c6 Binary files /dev/null and b/doc/docs/assets/history-page.png differ diff --git a/doc/docs/assets/triggers.png b/doc/docs/assets/triggers.png new file mode 100644 index 000000000..044199ba9 Binary files /dev/null and b/doc/docs/assets/triggers.png differ diff --git a/doc/docs/cron-triggers.md b/doc/docs/cron-triggers.md new file mode 100644 index 000000000..99b6a74fd --- /dev/null +++ b/doc/docs/cron-triggers.md @@ -0,0 +1,286 @@ +# Cron Triggers + +![Triggers](/assets/cron-jobs.png) + +Spring Persistent Tasks supports scheduled recurring triggers using cron expressions or fixed intervals. Cron triggers are automatically recreated after execution and survive application restarts. + +## Overview + +Cron triggers are registered schedules that automatically create trigger instances at specified intervals. The framework ensures that: + +- Only one trigger instance exists per cron trigger across all nodes +- Triggers are automatically recreated after successful execution +- Schedules survive application restarts +- Works correctly in multi-node deployments + +## Registering a Cron Trigger + +Use the fluent builder API via `TaskId.newCron()` to create cron triggers. Register them using `TriggerService.register()`. + +### Option 1: Using @CronTrigger Annotation (Simplest) + +Use the `@CronTrigger` annotation on your `@Component` task class for automatic registration: + +```java +@Component(DailyCleanupTask.NAME) +@CronTrigger(id = "daily-cleanup-cron", cron = "0 0 2 * * *") +@RequiredArgsConstructor +@Slf4j +public class DailyCleanupTask implements PersistentTask { + + public static final String NAME = "daily-cleanup"; + public static final TaskId ID = TaskId.of(NAME); + + private final DataService dataService; + + @Override + public void accept(Void state) { + log.info("Running daily cleanup"); + dataService.cleanupOldData(); + } +} +``` + + +### Option 2: Manual Registration with @PostConstruct + +If you need more control, register the cron trigger programmatically: + +```java +@Component(DailyCleanupTask.NAME) +@RequiredArgsConstructor +@Slf4j +public class DailyCleanupTask implements PersistentTask { + + public static final String NAME = "daily-cleanup"; + public static final TaskId ID = TaskId.of(NAME); + + private final TriggerService triggerService; + private final DataService dataService; + + @PostConstruct + public void registerCronTrigger() { + triggerService.register( + ID.newCron() + .id("daily-cleanup-cron") + .cron("0 0 2 * * *") // Every day at 2:00 AM UTC + .build() + ); + } + + @Override + public void accept(Void state) { + log.info("Running daily cleanup"); + dataService.cleanupOldData(); + } +} +``` + + +> **Note**: The `@CronTrigger` annotation automatically registers the cron trigger when the application starts. You don't need to inject `TriggerService` or use `@PostConstruct` when using the annotation. + +## Cron Trigger ID + +**Important**: Consider to specify a unique ID for your cron triggers using `.id("unique-id")`. This ID is used to: +- Identify the cron trigger in the repository +- Create trigger instances in the database +- Prevent duplicate registrations e.g. during refactorings or cron changes + +If you don't specify an ID, it defaults to `schedule.description()`, which may not be unique or meaningful. + +```java +// Good - explicit ID +dailyCleanupTask.newCron() + .id("daily-cleanup-cron") + .cron("0 0 2 * * *") + .build(); + +// Avoid - ID will be auto-generated from schedule +dailyCleanupTask.newCron() + .cron("0 0 2 * * *") + .build(); +``` + +## Cron Expression Format + +Cron expressions use 6 fields (second minute hour day month weekday): + +``` + ┌───────────── second (0-59) + │ ┌───────────── minute (0-59) + │ │ ┌───────────── hour (0-23) + │ │ │ ┌───────────── day of month (1-31) + │ │ │ │ ┌───────────── month (1-12 or JAN-DEC) + │ │ │ │ │ ┌───────────── day of week (0-7 or SUN-SAT, 0 and 7 = Sunday) + │ │ │ │ │ │ + * * * * * * +``` + +### Common Examples + +```java +// Every minute +.cron("0 * * * * *") + +// Every 15 minutes +.cron("0 */15 * * * *") + +// Every hour at minute 30 +.cron("0 30 * * * *") + +// Every day at midnight +.cron("0 0 0 * * *") + +// Every Monday at 9:00 AM +.cron("0 0 9 * * MON") + +// First day of every month at 1:00 AM +.cron("0 0 1 1 * *") + +// Weekdays at 8:00 AM +.cron("0 0 8 * * MON-FRI") +``` + +## Advanced Features + +### Custom Tags + +Add custom tags to identify triggers: + +```java +dailyCleanupTask.newCron() + .id("daily-cleanup-cron") + .cron("0 0 2 * * *") + .tag("maintenance") + .build(); +``` + +### Priority + +Set priority for trigger execution (0-9, higher = more important): + +```java +criticalTask.newCron() + .id("critical-sync") + .every(Duration.ofMinutes(5)) + .priority(9) + .build(); +``` + +### Dynamic State + +Use a state provider for dynamic state generation as soon as a new trigger is registered by the cron: + +```java +public record SyncState(OffsetDateTime timestamp) implements Serializable {} + +TaskId syncTask = taskService.register("data-sync", (SyncState state) -> { + log.info("Syncing data, last timestamp: {}", state.timestamp()); + // Perform sync +}); + +syncTask.newCron() + .id("data-sync-cron") + .every(Duration.ofMinutes(30)) + .stateProvider(() -> new SyncState(OffsetDateTime.now())) + .build(); +``` + + +## Configuration + +Configure the polling interval for cron trigger checks: + +```properties +# Check for missing cron triggers every 60 seconds (default) +spring.persistent-tasks.poll-cron-triggers=60 +``` + +## Suspending and Resuming Cron Triggers + +You can suspend and resume cron triggers at runtime: + +```java +@Service +@RequiredArgsConstructor +public class CronManagementService { + + private final TriggerService triggerService; + + public void suspendDailyCleanup() { + var key = TriggerKey.of("daily-cleanup-cron", "daily-cleanup"); + triggerService.suspendCron(key); + } + + public void resumeDailyCleanup() { + var key = TriggerKey.of("daily-cleanup-cron", "daily-cleanup"); + triggerService.resumeCron(key); + } +} +``` + +## Migration from Spring @Scheduled + +If you're migrating from Spring's `@Scheduled` annotation, here's how to convert your code: + +### Before (Spring @Scheduled) + +```java +@Component +public class ScheduledTasks { + + private final DataService dataService; + + @Scheduled(cron = "0 0 2 * * *") + public void dailyCleanup() { + // cleanup logic - will be executed on each node in the cluster + log.info("Running daily cleanup"); + dataService.cleanup(); + } +} +``` + +### After (Spring Persistent Tasks) - Using @Component + +```java +@Component(DailyCleanupTask.NAME) +@RequiredArgsConstructor +@Slf4j +public class DailyCleanupTask implements PersistentTask { + + public static final String NAME = "daily-cleanup"; + public static final TaskId ID = TaskId.of(NAME); + + private final TriggerService triggerService; + private final DataService dataService; + + @PostConstruct + public void registerCronTrigger() { + triggerService.register( + ID.newCron() + .id("daily-cleanup-cron") + .cron("0 0 2 * * *") + .build() + ); + } + + @Override + public void accept(Void state) { + // cleanup logic - will run only on one node in the cluster + log.info("Running daily cleanup"); + dataService.cleanup(); + } +} +``` + +### Key Differences + +| Feature | Spring @Scheduled | Spring Persistent Tasks | +|---------|------------------|------------------------| +| Persistence | No | Yes | +| Cluster-safe | No | Yes | +| Execution history | No | Yes | +| State between runs | Manual | Built-in | +| Runtime control | Limited | Full (suspend/resume) | +| Retry on failure | Manual | Automatic | +| Requires database | No | Yes | diff --git a/doc/docs/queue-a-spring-task.md b/doc/docs/queue-a-spring-task.md index f535db0dd..e733f13de 100644 --- a/doc/docs/queue-a-spring-task.md +++ b/doc/docs/queue-a-spring-task.md @@ -11,6 +11,8 @@ tags: # Queue a task execution +![Triggers](/assets/triggers.png) + You can queue a task for later execution or request the executor to run a task directly if a free thread is available on the node. ## Direct usage of the `TriggerService` or `PersistentTaskService`. diff --git a/example/src/main/java/org/sterl/spring/example_app/vehicle/task/CheckForOldVehiclesTask.java b/example/src/main/java/org/sterl/spring/example_app/vehicle/task/CheckForOldVehiclesTask.java new file mode 100644 index 000000000..1ae437777 --- /dev/null +++ b/example/src/main/java/org/sterl/spring/example_app/vehicle/task/CheckForOldVehiclesTask.java @@ -0,0 +1,23 @@ +package org.sterl.spring.example_app.vehicle.task; + +import java.util.concurrent.TimeUnit; + +import org.springframework.lang.Nullable; +import org.springframework.stereotype.Component; +import org.sterl.spring.persistent_tasks.api.task.PersistentTask; +import org.sterl.spring.persistent_tasks.trigger.CronTrigger; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +@CronTrigger(fixedDelay = 5, timeUnit = TimeUnit.MINUTES) +@Component +@RequiredArgsConstructor +@Slf4j +public class CheckForOldVehiclesTask implements PersistentTask { + + @Override + public void accept(@Nullable String nothing) { + log.info("doing vehicle cron job search ..."); + } +} diff --git a/example/src/main/resources/application.yml b/example/src/main/resources/application.yml index a186a1d71..15a24a990 100644 --- a/example/src/main/resources/application.yml +++ b/example/src/main/resources/application.yml @@ -14,6 +14,9 @@ spring: liquibase: change-log: classpath:db/changelog/db.changelog-master.xml +server: + port: 7070 + management: endpoints: web: diff --git a/pom.xml b/pom.xml index 5e2cab3d5..697117143 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.sterl.spring spring-persistent-tasks-root - 2.3.2-SNAPSHOT + 2.4.0-SNAPSHOT pom 2024 @@ -41,7 +41,7 @@ ${java.version} UTF-8 UTF-8 - 7.13.0 + 7.20.0 5.1.0 @@ -70,14 +70,19 @@ org.apache.maven.plugins maven-pmd-plugin - 3.26.0 + 3.28.0 + + + org.apache.maven.plugins + maven-jxr-plugin + 3.6.0 org.apache.maven.plugins maven-javadoc-plugin - 3.11.2 + 3.12.0 diff --git a/screenshots/history-screen.png b/screenshots/history-screen.png deleted file mode 100644 index c5b4be9be..000000000 Binary files a/screenshots/history-screen.png and /dev/null differ diff --git a/screenshots/schedulers-screen.png b/screenshots/schedulers-screen.png deleted file mode 100644 index 4f9d83e70..000000000 Binary files a/screenshots/schedulers-screen.png and /dev/null differ diff --git a/screenshots/triggers-screen.png b/screenshots/triggers-screen.png deleted file mode 100644 index 9b980dd80..000000000 Binary files a/screenshots/triggers-screen.png and /dev/null differ diff --git a/test/pom.xml b/test/pom.xml index 5f8e3af4d..10f867f0c 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -6,7 +6,7 @@ org.sterl.spring spring-persistent-tasks-root - 2.3.2-SNAPSHOT + 2.4.0-SNAPSHOT ../pom.xml diff --git a/ui/package.json b/ui/package.json index 2e631d961..190b08937 100644 --- a/ui/package.json +++ b/ui/package.json @@ -2,5 +2,5 @@ "name": "spt-ui", "version": "0.8.0", "author": "Paul Sterl", - "packageManager": "pnpm@10.15.0+sha512.486ebc259d3e999a4e8691ce03b5cac4a71cbeca39372a9b762cb500cfdf0873e2cb16abe3d951b1ee2cf012503f027b98b6584e4df22524e0c7450d9ec7aa7b" + "packageManager": "pnpm@10.28.0" } diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml index 1ff0e0b28..7f56aa6eb 100644 --- a/ui/pnpm-lock.yaml +++ b/ui/pnpm-lock.yaml @@ -11,124 +11,124 @@ importers: spt-ui-lib: dependencies: '@uiw/react-json-view': - specifier: 2.0.0-alpha.38 - version: 2.0.0-alpha.38(@babel/runtime@7.28.4)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + specifier: 2.0.0-alpha.40 + version: 2.0.0-alpha.40(@babel/runtime@7.28.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) axios: - specifier: ^1.12.2 - version: 1.12.2 + specifier: ^1.13.2 + version: 1.13.2 bootstrap: specifier: ^5.3.8 version: 5.3.8(@popperjs/core@2.11.8) react: - specifier: ^19.1.0 - version: 19.1.1 + specifier: ^19.2.0 + version: 19.2.3 react-bootstrap: specifier: ^2.10.10 - version: 2.10.10(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + version: 2.10.10(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react-bootstrap-icons: specifier: ^1.11.6 - version: 1.11.6(react@19.1.1) + version: 1.11.6(react@19.2.3) react-dom: - specifier: ^19.1.0 - version: 19.1.1(react@19.1.1) + specifier: ^19.2.0 + version: 19.2.3(react@19.2.3) devDependencies: '@eslint/js': - specifier: ^9.36.0 - version: 9.36.0 + specifier: ^9.39.2 + version: 9.39.2 '@testing-library/jest-dom': - specifier: ^6.8.0 - version: 6.8.0 + specifier: ^6.9.1 + version: 6.9.1 '@testing-library/react': - specifier: ^16.3.0 - version: 16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + specifier: ^16.3.1 + version: 16.3.1(@testing-library/dom@10.4.0)(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@types/node': - specifier: ^24.5.2 - version: 24.5.2 + specifier: ^25.0.6 + version: 25.0.6 '@types/react': - specifier: ^19.1.13 - version: 19.1.13 + specifier: ^19.2.8 + version: 19.2.8 '@types/react-dom': - specifier: ^19.1.9 - version: 19.1.9(@types/react@19.1.13) + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.8) '@vitejs/plugin-react-swc': - specifier: ^3.11.0 - version: 3.11.0(@swc/helpers@0.5.17)(vite@7.1.7(@types/node@24.5.2)) + specifier: ^4.2.2 + version: 4.2.2(@swc/helpers@0.5.17)(vite@7.3.1(@types/node@25.0.6)) eslint: - specifier: ^9.36.0 - version: 9.36.0 + specifier: ^9.39.2 + version: 9.39.2 eslint-plugin-react-hooks: - specifier: ^5.2.0 - version: 5.2.0(eslint@9.36.0) + specifier: ^7.0.1 + version: 7.0.1(eslint@9.39.2) eslint-plugin-react-refresh: - specifier: ^0.4.20 - version: 0.4.20(eslint@9.36.0) + specifier: ^0.4.26 + version: 0.4.26(eslint@9.39.2) globals: - specifier: ^16.4.0 - version: 16.4.0 + specifier: ^17.0.0 + version: 17.0.0 jsdom: - specifier: ^26.1.0 - version: 26.1.0 + specifier: ^27.4.0 + version: 27.4.0 typescript: - specifier: ~5.8.3 - version: 5.8.3 + specifier: ~5.9.3 + version: 5.9.3 typescript-eslint: - specifier: ^8.44.0 - version: 8.44.0(eslint@9.36.0)(typescript@5.8.3) + specifier: ^8.52.0 + version: 8.52.0(eslint@9.39.2)(typescript@5.9.3) vite: - specifier: ^7.1.7 - version: 7.1.7(@types/node@24.5.2) + specifier: ^7.3.1 + version: 7.3.1(@types/node@25.0.6) vite-plugin-dts: specifier: ^4.5.4 - version: 4.5.4(@types/node@24.5.2)(rollup@4.52.0)(typescript@5.8.3)(vite@7.1.7(@types/node@24.5.2)) + version: 4.5.4(@types/node@25.0.6)(rollup@4.55.1)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.6)) vitest: - specifier: ^3.2.4 - version: 3.2.4(@types/node@24.5.2)(jsdom@26.1.0) + specifier: ^4.0.16 + version: 4.0.16(@types/node@25.0.6)(jsdom@27.4.0) web-app: dependencies: '@uiw/react-json-view': - specifier: 2.0.0-alpha.38 - version: 2.0.0-alpha.38(@babel/runtime@7.28.4)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + specifier: 2.0.0-alpha.40 + version: 2.0.0-alpha.40(@babel/runtime@7.28.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) axios: - specifier: ^1.12.2 - version: 1.12.2 + specifier: ^1.13.2 + version: 1.13.2 bootstrap: specifier: ^5.3.8 version: 5.3.8(@popperjs/core@2.11.8) crossroad: - specifier: ^1.3.3 - version: 1.3.3(react@19.1.1) + specifier: ^2.0.0 + version: 2.0.0(react@19.2.3) luxon: specifier: ^3.7.2 version: 3.7.2 react: - specifier: ^19.1.1 - version: 19.1.1 + specifier: ^19.2.3 + version: 19.2.3 react-bootstrap: specifier: ^2.10.10 - version: 2.10.10(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + version: 2.10.10(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react-bootstrap-icons: specifier: ^1.11.6 - version: 1.11.6(react@19.1.1) + version: 1.11.6(react@19.2.3) react-dom: - specifier: ^19.1.1 - version: 19.1.1(react@19.1.1) + specifier: ^19.2.3 + version: 19.2.3(react@19.2.3) recharts: - specifier: ^3.2.1 - version: 3.2.1(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react-is@17.0.2)(react@19.1.1)(redux@5.0.1) + specifier: ^3.6.0 + version: 3.6.0(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react-is@17.0.2)(react@19.2.3)(redux@5.0.1) spring-persistent-tasks-ui: specifier: workspace:spt-ui-lib@* version: link:../spt-ui-lib devDependencies: '@eslint/js': - specifier: ^9.36.0 - version: 9.36.0 + specifier: ^9.39.2 + version: 9.39.2 '@testing-library/jest-dom': - specifier: ^6.8.0 - version: 6.8.0 + specifier: ^6.9.1 + version: 6.9.1 '@testing-library/react': - specifier: ^16.3.0 - version: 16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + specifier: ^16.3.1 + version: 16.3.1(@testing-library/dom@10.4.0)(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@testing-library/user-event': specifier: ^14.6.1 version: 14.6.1(@testing-library/dom@10.4.0) @@ -136,70 +136,117 @@ importers: specifier: ^3.7.1 version: 3.7.1 '@types/node': - specifier: ^24.5.2 - version: 24.5.2 + specifier: ^25.0.6 + version: 25.0.6 '@types/react': - specifier: ^19.1.13 - version: 19.1.13 + specifier: ^19.2.8 + version: 19.2.8 '@types/react-dom': - specifier: ^19.1.9 - version: 19.1.9(@types/react@19.1.13) + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.8) '@vitejs/plugin-react-swc': - specifier: ^3.11.0 - version: 3.11.0(@swc/helpers@0.5.17)(vite@7.1.7(@types/node@24.5.2)) + specifier: ^4.2.2 + version: 4.2.2(@swc/helpers@0.5.17)(vite@7.3.1(@types/node@25.0.6)) eslint: - specifier: ^9.36.0 - version: 9.36.0 + specifier: ^9.39.2 + version: 9.39.2 eslint-plugin-react: specifier: ^7.37.5 - version: 7.37.5(eslint@9.36.0) + version: 7.37.5(eslint@9.39.2) eslint-plugin-react-hooks: - specifier: ^5.2.0 - version: 5.2.0(eslint@9.36.0) + specifier: ^7.0.1 + version: 7.0.1(eslint@9.39.2) eslint-plugin-react-refresh: - specifier: ^0.4.20 - version: 0.4.20(eslint@9.36.0) + specifier: ^0.4.26 + version: 0.4.26(eslint@9.39.2) globals: - specifier: ^16.4.0 - version: 16.4.0 + specifier: ^17.0.0 + version: 17.0.0 jsdom: - specifier: ^26.1.0 - version: 26.1.0 + specifier: ^27.4.0 + version: 27.4.0 typescript: - specifier: ~5.8.3 - version: 5.8.3 + specifier: ~5.9.3 + version: 5.9.3 typescript-eslint: - specifier: ^8.44.0 - version: 8.44.0(eslint@9.36.0)(typescript@5.8.3) + specifier: ^8.52.0 + version: 8.52.0(eslint@9.39.2)(typescript@5.9.3) vite: - specifier: ^7.1.7 - version: 7.1.7(@types/node@24.5.2) + specifier: ^7.3.1 + version: 7.3.1(@types/node@25.0.6) vitest: - specifier: ^3.2.4 - version: 3.2.4(@types/node@24.5.2)(jsdom@26.1.0) + specifier: ^4.0.16 + version: 4.0.16(@types/node@25.0.6)(jsdom@27.4.0) packages: + '@acemir/cssom@0.9.30': + resolution: {integrity: sha512-9CnlMCI0LmCIq0olalQqdWrJHPzm0/tw3gzOA9zJSgvFX7Xau3D24mAGa4BtwxwY69nsuJW6kQqqCzf/mEcQgg==} + '@adobe/css-tools@4.4.4': resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==} - '@asamuzakjp/css-color@3.2.0': - resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} + '@asamuzakjp/css-color@4.1.1': + resolution: {integrity: sha512-B0Hv6G3gWGMn0xKJ0txEi/jM5iFpT3MfDxmhZFb4W047GvytCf1DHQ1D69W3zHI4yWe2aTZAA0JnbMZ7Xc8DuQ==} + + '@asamuzakjp/dom-selector@6.7.6': + resolution: {integrity: sha512-hBaJER6A9MpdG3WgdlOolHmbOYvSk46y7IQN/1+iqiCuUu6iWdQrs9DGKF8ocqsEqWujWf/V7b7vaDgiUmIvUg==} + + '@asamuzakjp/nwsapi@2.3.9': + resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} '@babel/code-frame@7.27.1': resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} + '@babel/compat-data@7.28.5': + resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.28.5': + resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.28.5': + resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.3': + resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@babel/helper-string-parser@7.27.1': resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.27.1': - resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.28.4': + resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} engines: {node: '>=6.9.0'} - '@babel/parser@7.28.4': - resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==} + '@babel/parser@7.28.5': + resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} engines: {node: '>=6.0.0'} hasBin: true @@ -207,8 +254,16 @@ packages: resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} engines: {node: '>=6.9.0'} - '@babel/types@7.28.4': - resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.28.5': + resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.28.5': + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} engines: {node: '>=6.9.0'} '@csstools/color-helpers@5.1.0': @@ -235,204 +290,217 @@ packages: peerDependencies: '@csstools/css-tokenizer': ^3.0.4 + '@csstools/css-syntax-patches-for-csstree@1.0.25': + resolution: {integrity: sha512-g0Kw9W3vjx5BEBAF8c5Fm2NcB/Fs8jJXh85aXqwEXiL+tqtOut07TWgyaGzAAfTM+gKckrrncyeGEZPcaRgm2Q==} + engines: {node: '>=18'} + '@csstools/css-tokenizer@3.0.4': resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} engines: {node: '>=18'} - '@esbuild/aix-ppc64@0.25.10': - resolution: {integrity: sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==} + '@esbuild/aix-ppc64@0.27.2': + resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.25.10': - resolution: {integrity: sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==} + '@esbuild/android-arm64@0.27.2': + resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.25.10': - resolution: {integrity: sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==} + '@esbuild/android-arm@0.27.2': + resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.25.10': - resolution: {integrity: sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==} + '@esbuild/android-x64@0.27.2': + resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.25.10': - resolution: {integrity: sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==} + '@esbuild/darwin-arm64@0.27.2': + resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.25.10': - resolution: {integrity: sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==} + '@esbuild/darwin-x64@0.27.2': + resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.25.10': - resolution: {integrity: sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==} + '@esbuild/freebsd-arm64@0.27.2': + resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.10': - resolution: {integrity: sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==} + '@esbuild/freebsd-x64@0.27.2': + resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.25.10': - resolution: {integrity: sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==} + '@esbuild/linux-arm64@0.27.2': + resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.25.10': - resolution: {integrity: sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==} + '@esbuild/linux-arm@0.27.2': + resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.25.10': - resolution: {integrity: sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==} + '@esbuild/linux-ia32@0.27.2': + resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.25.10': - resolution: {integrity: sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==} + '@esbuild/linux-loong64@0.27.2': + resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.25.10': - resolution: {integrity: sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==} + '@esbuild/linux-mips64el@0.27.2': + resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.25.10': - resolution: {integrity: sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==} + '@esbuild/linux-ppc64@0.27.2': + resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.25.10': - resolution: {integrity: sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==} + '@esbuild/linux-riscv64@0.27.2': + resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.25.10': - resolution: {integrity: sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==} + '@esbuild/linux-s390x@0.27.2': + resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.25.10': - resolution: {integrity: sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==} + '@esbuild/linux-x64@0.27.2': + resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.10': - resolution: {integrity: sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==} + '@esbuild/netbsd-arm64@0.27.2': + resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.10': - resolution: {integrity: sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==} + '@esbuild/netbsd-x64@0.27.2': + resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.10': - resolution: {integrity: sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==} + '@esbuild/openbsd-arm64@0.27.2': + resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.10': - resolution: {integrity: sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==} + '@esbuild/openbsd-x64@0.27.2': + resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.25.10': - resolution: {integrity: sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==} + '@esbuild/openharmony-arm64@0.27.2': + resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.25.10': - resolution: {integrity: sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==} + '@esbuild/sunos-x64@0.27.2': + resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.25.10': - resolution: {integrity: sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==} + '@esbuild/win32-arm64@0.27.2': + resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.25.10': - resolution: {integrity: sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==} + '@esbuild/win32-ia32@0.27.2': + resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.25.10': - resolution: {integrity: sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==} + '@esbuild/win32-x64@0.27.2': + resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==} engines: {node: '>=18'} cpu: [x64] os: [win32] - '@eslint-community/eslint-utils@4.9.0': - resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - '@eslint-community/regexpp@4.12.1': - resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/config-array@0.21.0': - resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} + '@eslint/config-array@0.21.1': + resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/config-helpers@0.3.1': - resolution: {integrity: sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==} + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/core@0.15.2': - resolution: {integrity: sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==} + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/eslintrc@3.3.1': - resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + '@eslint/eslintrc@3.3.3': + resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.36.0': - resolution: {integrity: sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==} + '@eslint/js@9.39.2': + resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/object-schema@2.1.6': - resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/plugin-kit@0.3.5': - resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==} + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@exodus/bytes@1.8.0': + resolution: {integrity: sha512-8JPn18Bcp8Uo1T82gR8lh2guEOa5KKU/IEKvvdp0sgmi7coPBWf1Doi1EXsGZb2ehc8ym/StJCjffYV+ne7sXQ==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + peerDependencies: + '@exodus/crypto': ^1.0.0-rc.4 + peerDependenciesMeta: + '@exodus/crypto': + optional: true + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -457,33 +525,34 @@ packages: resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} engines: {node: 20 || >=22} - '@jridgewell/sourcemap-codec@1.5.5': - resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} - '@microsoft/api-extractor-model@7.30.7': - resolution: {integrity: sha512-TBbmSI2/BHpfR9YhQA7nH0nqVmGgJ0xH0Ex4D99/qBDAUpnhA2oikGmdXanbw9AWWY/ExBYIpkmY8dBHdla3YQ==} + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} - '@microsoft/api-extractor@7.52.13': - resolution: {integrity: sha512-K6/bBt8zZfn9yc06gNvA+/NlBGJC/iJlObpdufXHEJtqcD4Dln4ITCLZpwP3DNZ5NyBFeTkKdv596go3V72qlA==} - hasBin: true + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - '@microsoft/tsdoc-config@0.17.1': - resolution: {integrity: sha512-UtjIFe0C6oYgTnad4q1QP4qXwLhe6tIpNTRStJ2RZEPIkqQPREAwE5spzVxsdn9UaEMUqhh0AqSx3X4nWAKXWw==} + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@microsoft/tsdoc@0.15.1': - resolution: {integrity: sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==} + '@microsoft/api-extractor-model@7.32.2': + resolution: {integrity: sha512-Ussc25rAalc+4JJs9HNQE7TuO9y6jpYQX9nWD1DhqUzYPBr3Lr7O9intf+ZY8kD5HnIqeIRJX7ccCT0QyBy2Ww==} - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} + '@microsoft/api-extractor@7.55.2': + resolution: {integrity: sha512-1jlWO4qmgqYoVUcyh+oXYRztZde/pAi7cSVzBz/rc+S7CoVzDasy8QE13dx6sLG4VRo8SfkkLbFORR6tBw4uGQ==} + hasBin: true - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} + '@microsoft/tsdoc-config@0.18.0': + resolution: {integrity: sha512-8N/vClYyfOH+l4fLkkr9+myAoR6M7akc8ntBJ4DJdWH2b09uVfr71+LTMpNyG19fNqWDg8KEDZhx5wxuqHyGjw==} - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} + '@microsoft/tsdoc@0.16.0': + resolution: {integrity: sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==} '@popperjs/core@2.11.8': resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} @@ -494,8 +563,8 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - '@reduxjs/toolkit@2.9.0': - resolution: {integrity: sha512-fSfQlSRu9Z5yBkvsNhYF2rPS8cGXn/TZVrlwN1948QyZ8xMZ0JvP50S2acZNaf+o63u6aEeMjipFyksjIcWrog==} + '@reduxjs/toolkit@2.11.2': + resolution: {integrity: sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==} peerDependencies: react: ^16.9.0 || ^17.0.0 || ^18 || ^19 react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0 @@ -521,8 +590,8 @@ packages: react: '>=16.14.0' react-dom: '>=16.14.0' - '@rolldown/pluginutils@1.0.0-beta.27': - resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + '@rolldown/pluginutils@1.0.0-beta.47': + resolution: {integrity: sha512-8QagwMH3kNCuzD8EWL8R2YPW5e4OrHNSAHRFDdmFqEwEaD/KcNKjVoumo+gP2vW5eKB2UPbM6vTYiGZX0ixLnw==} '@rollup/pluginutils@5.3.0': resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} @@ -533,206 +602,229 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.52.0': - resolution: {integrity: sha512-VxDYCDqOaR7NXzAtvRx7G1u54d2kEHopb28YH/pKzY6y0qmogP3gG7CSiWsq9WvDFxOQMpNEyjVAHZFXfH3o/A==} + '@rollup/rollup-android-arm-eabi@4.55.1': + resolution: {integrity: sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.52.0': - resolution: {integrity: sha512-pqDirm8koABIKvzL59YI9W9DWbRlTX7RWhN+auR8HXJxo89m4mjqbah7nJZjeKNTNYopqL+yGg+0mhCpf3xZtQ==} + '@rollup/rollup-android-arm64@4.55.1': + resolution: {integrity: sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.52.0': - resolution: {integrity: sha512-YCdWlY/8ltN6H78HnMsRHYlPiKvqKagBP1r+D7SSylxX+HnsgXGCmLiV3Y4nSyY9hW8qr8U9LDUx/Lo7M6MfmQ==} + '@rollup/rollup-darwin-arm64@4.55.1': + resolution: {integrity: sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.52.0': - resolution: {integrity: sha512-z4nw6y1j+OOSGzuVbSWdIp1IUks9qNw4dc7z7lWuWDKojY38VMWBlEN7F9jk5UXOkUcp97vA1N213DF+Lz8BRg==} + '@rollup/rollup-darwin-x64@4.55.1': + resolution: {integrity: sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.52.0': - resolution: {integrity: sha512-Q/dv9Yvyr5rKlK8WQJZVrp5g2SOYeZUs9u/t2f9cQ2E0gJjYB/BWoedXfUT0EcDJefi2zzVfhcOj8drWCzTviw==} + '@rollup/rollup-freebsd-arm64@4.55.1': + resolution: {integrity: sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.52.0': - resolution: {integrity: sha512-kdBsLs4Uile/fbjZVvCRcKB4q64R+1mUq0Yd7oU1CMm1Av336ajIFqNFovByipciuUQjBCPMxwJhCgfG2re3rg==} + '@rollup/rollup-freebsd-x64@4.55.1': + resolution: {integrity: sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.52.0': - resolution: {integrity: sha512-aL6hRwu0k7MTUESgkg7QHY6CoqPgr6gdQXRJI1/VbFlUMwsSzPGSR7sG5d+MCbYnJmJwThc2ol3nixj1fvI/zQ==} + '@rollup/rollup-linux-arm-gnueabihf@4.55.1': + resolution: {integrity: sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.52.0': - resolution: {integrity: sha512-BTs0M5s1EJejgIBJhCeiFo7GZZ2IXWkFGcyZhxX4+8usnIo5Mti57108vjXFIQmmJaRyDwmV59Tw64Ap1dkwMw==} + '@rollup/rollup-linux-arm-musleabihf@4.55.1': + resolution: {integrity: sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.52.0': - resolution: {integrity: sha512-uj672IVOU9m08DBGvoPKPi/J8jlVgjh12C9GmjjBxCTQc3XtVmRkRKyeHSmIKQpvJ7fIm1EJieBUcnGSzDVFyw==} + '@rollup/rollup-linux-arm64-gnu@4.55.1': + resolution: {integrity: sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.52.0': - resolution: {integrity: sha512-/+IVbeDMDCtB/HP/wiWsSzduD10SEGzIZX2945KSgZRNi4TSkjHqRJtNTVtVb8IRwhJ65ssI56krlLik+zFWkw==} + '@rollup/rollup-linux-arm64-musl@4.55.1': + resolution: {integrity: sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loong64-gnu@4.52.0': - resolution: {integrity: sha512-U1vVzvSWtSMWKKrGoROPBXMh3Vwn93TA9V35PldokHGqiUbF6erSzox/5qrSMKp6SzakvyjcPiVF8yB1xKr9Pg==} + '@rollup/rollup-linux-loong64-gnu@4.55.1': + resolution: {integrity: sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-ppc64-gnu@4.52.0': - resolution: {integrity: sha512-X/4WfuBAdQRH8cK3DYl8zC00XEE6aM472W+QCycpQJeLWVnHfkv7RyBFVaTqNUMsTgIX8ihMjCvFF9OUgeABzw==} + '@rollup/rollup-linux-loong64-musl@4.55.1': + resolution: {integrity: sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.55.1': + resolution: {integrity: sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.52.0': - resolution: {integrity: sha512-xIRYc58HfWDBZoLmWfWXg2Sq8VCa2iJ32B7mqfWnkx5mekekl0tMe7FHpY8I72RXEcUkaWawRvl3qA55og+cwQ==} + '@rollup/rollup-linux-ppc64-musl@4.55.1': + resolution: {integrity: sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.55.1': + resolution: {integrity: sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.52.0': - resolution: {integrity: sha512-mbsoUey05WJIOz8U1WzNdf+6UMYGwE3fZZnQqsM22FZ3wh1N887HT6jAOjXs6CNEK3Ntu2OBsyQDXfIjouI4dw==} + '@rollup/rollup-linux-riscv64-musl@4.55.1': + resolution: {integrity: sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.52.0': - resolution: {integrity: sha512-qP6aP970bucEi5KKKR4AuPFd8aTx9EF6BvutvYxmZuWLJHmnq4LvBfp0U+yFDMGwJ+AIJEH5sIP+SNypauMWzg==} + '@rollup/rollup-linux-s390x-gnu@4.55.1': + resolution: {integrity: sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.52.0': - resolution: {integrity: sha512-nmSVN+F2i1yKZ7rJNKO3G7ZzmxJgoQBQZ/6c4MuS553Grmr7WqR7LLDcYG53Z2m9409z3JLt4sCOhLdbKQ3HmA==} + '@rollup/rollup-linux-x64-gnu@4.55.1': + resolution: {integrity: sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.52.0': - resolution: {integrity: sha512-2d0qRo33G6TfQVjaMR71P+yJVGODrt5V6+T0BDYH4EMfGgdC/2HWDVjSSFw888GSzAZUwuska3+zxNUCDco6rQ==} + '@rollup/rollup-linux-x64-musl@4.55.1': + resolution: {integrity: sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==} cpu: [x64] os: [linux] - '@rollup/rollup-openharmony-arm64@4.52.0': - resolution: {integrity: sha512-A1JalX4MOaFAAyGgpO7XP5khquv/7xKzLIyLmhNrbiCxWpMlnsTYr8dnsWM7sEeotNmxvSOEL7F65j0HXFcFsw==} + '@rollup/rollup-openbsd-x64@4.55.1': + resolution: {integrity: sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.55.1': + resolution: {integrity: sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.52.0': - resolution: {integrity: sha512-YQugafP/rH0eOOHGjmNgDURrpYHrIX0yuojOI8bwCyXwxC9ZdTd3vYkmddPX0oHONLXu9Rb1dDmT0VNpjkzGGw==} + '@rollup/rollup-win32-arm64-msvc@4.55.1': + resolution: {integrity: sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.52.0': - resolution: {integrity: sha512-zYdUYhi3Qe2fndujBqL5FjAFzvNeLxtIqfzNEVKD1I7C37/chv1VxhscWSQHTNfjPCrBFQMnynwA3kpZpZ8w4A==} + '@rollup/rollup-win32-ia32-msvc@4.55.1': + resolution: {integrity: sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.52.0': - resolution: {integrity: sha512-fGk03kQylNaCOQ96HDMeT7E2n91EqvCDd3RwvT5k+xNdFCeMGnj5b5hEgTGrQuyidqSsD3zJDQ21QIaxXqTBJw==} + '@rollup/rollup-win32-x64-gnu@4.55.1': + resolution: {integrity: sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.52.0': - resolution: {integrity: sha512-6iKDCVSIUQ8jPMoIV0OytRKniaYyy5EbY/RRydmLW8ZR3cEBhxbWl5ro0rkUNe0ef6sScvhbY79HrjRm8i3vDQ==} + '@rollup/rollup-win32-x64-msvc@4.55.1': + resolution: {integrity: sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==} cpu: [x64] os: [win32] - '@rushstack/node-core-library@5.14.0': - resolution: {integrity: sha512-eRong84/rwQUlATGFW3TMTYVyqL1vfW9Lf10PH+mVGfIb9HzU3h5AASNIw+axnBLjnD0n3rT5uQBwu9fvzATrg==} + '@rushstack/node-core-library@5.19.1': + resolution: {integrity: sha512-ESpb2Tajlatgbmzzukg6zyAhH+sICqJR2CNXNhXcEbz6UGCQfrKCtkxOpJTftWc8RGouroHG0Nud1SJAszvpmA==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/problem-matcher@0.1.1': + resolution: {integrity: sha512-Fm5XtS7+G8HLcJHCWpES5VmeMyjAKaWeyZU5qPzZC+22mPlJzAsOxymHiWIfuirtPckX3aptWws+K2d0BzniJA==} peerDependencies: '@types/node': '*' peerDependenciesMeta: '@types/node': optional: true - '@rushstack/rig-package@0.5.3': - resolution: {integrity: sha512-olzSSjYrvCNxUFZowevC3uz8gvKr3WTpHQ7BkpjtRpA3wK+T0ybep/SRUMfr195gBzJm5gaXw0ZMgjIyHqJUow==} + '@rushstack/rig-package@0.6.0': + resolution: {integrity: sha512-ZQmfzsLE2+Y91GF15c65L/slMRVhF6Hycq04D4TwtdGaUAbIXXg9c5pKA5KFU7M4QMaihoobp9JJYpYcaY3zOw==} - '@rushstack/terminal@0.16.0': - resolution: {integrity: sha512-WEvNuKkoR1PXorr9SxO0dqFdSp1BA+xzDrIm/Bwlc5YHg2FFg6oS+uCTYjerOhFuqCW+A3vKBm6EmKWSHfgx/A==} + '@rushstack/terminal@0.19.5': + resolution: {integrity: sha512-6k5tpdB88G0K7QrH/3yfKO84HK9ggftfUZ51p7fePyCE7+RLLHkWZbID9OFWbXuna+eeCFE7AkKnRMHMxNbz7Q==} peerDependencies: '@types/node': '*' peerDependenciesMeta: '@types/node': optional: true - '@rushstack/ts-command-line@5.0.3': - resolution: {integrity: sha512-bgPhQEqLVv/2hwKLYv/XvsTWNZ9B/+X1zJ7WgQE9rO5oiLzrOZvkIW4pk13yOQBhHyjcND5qMOa6p83t+Z66iQ==} + '@rushstack/ts-command-line@5.1.5': + resolution: {integrity: sha512-YmrFTFUdHXblYSa+Xc9OO9FsL/XFcckZy0ycQ6q7VSBsVs5P0uD9vcges5Q9vctGlVdu27w+Ct6IuJ458V0cTQ==} - '@standard-schema/spec@1.0.0': - resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} '@standard-schema/utils@0.3.0': resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} - '@swc/core-darwin-arm64@1.13.5': - resolution: {integrity: sha512-lKNv7SujeXvKn16gvQqUQI5DdyY8v7xcoO3k06/FJbHJS90zEwZdQiMNRiqpYw/orU543tPaWgz7cIYWhbopiQ==} + '@swc/core-darwin-arm64@1.15.8': + resolution: {integrity: sha512-M9cK5GwyWWRkRGwwCbREuj6r8jKdES/haCZ3Xckgkl8MUQJZA3XB7IXXK1IXRNeLjg6m7cnoMICpXv1v1hlJOg==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] - '@swc/core-darwin-x64@1.13.5': - resolution: {integrity: sha512-ILd38Fg/w23vHb0yVjlWvQBoE37ZJTdlLHa8LRCFDdX4WKfnVBiblsCU9ar4QTMNdeTBEX9iUF4IrbNWhaF1Ng==} + '@swc/core-darwin-x64@1.15.8': + resolution: {integrity: sha512-j47DasuOvXl80sKJHSi2X25l44CMc3VDhlJwA7oewC1nV1VsSzwX+KOwE5tLnfORvVJJyeiXgJORNYg4jeIjYQ==} engines: {node: '>=10'} cpu: [x64] os: [darwin] - '@swc/core-linux-arm-gnueabihf@1.13.5': - resolution: {integrity: sha512-Q6eS3Pt8GLkXxqz9TAw+AUk9HpVJt8Uzm54MvPsqp2yuGmY0/sNaPPNVqctCX9fu/Nu8eaWUen0si6iEiCsazQ==} + '@swc/core-linux-arm-gnueabihf@1.15.8': + resolution: {integrity: sha512-siAzDENu2rUbwr9+fayWa26r5A9fol1iORG53HWxQL1J8ym4k7xt9eME0dMPXlYZDytK5r9sW8zEA10F2U3Xwg==} engines: {node: '>=10'} cpu: [arm] os: [linux] - '@swc/core-linux-arm64-gnu@1.13.5': - resolution: {integrity: sha512-aNDfeN+9af+y+M2MYfxCzCy/VDq7Z5YIbMqRI739o8Ganz6ST+27kjQFd8Y/57JN/hcnUEa9xqdS3XY7WaVtSw==} + '@swc/core-linux-arm64-gnu@1.15.8': + resolution: {integrity: sha512-o+1y5u6k2FfPYbTRUPvurwzNt5qd0NTumCTFscCNuBksycloXY16J8L+SMW5QRX59n4Hp9EmFa3vpvNHRVv1+Q==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-arm64-musl@1.13.5': - resolution: {integrity: sha512-9+ZxFN5GJag4CnYnq6apKTnnezpfJhCumyz0504/JbHLo+Ue+ZtJnf3RhyA9W9TINtLE0bC4hKpWi8ZKoETyOQ==} + '@swc/core-linux-arm64-musl@1.15.8': + resolution: {integrity: sha512-koiCqL09EwOP1S2RShCI7NbsQuG6r2brTqUYE7pV7kZm9O17wZ0LSz22m6gVibpwEnw8jI3IE1yYsQTVpluALw==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-x64-gnu@1.13.5': - resolution: {integrity: sha512-WD530qvHrki8Ywt/PloKUjaRKgstQqNGvmZl54g06kA+hqtSE2FTG9gngXr3UJxYu/cNAjJYiBifm7+w4nbHbA==} + '@swc/core-linux-x64-gnu@1.15.8': + resolution: {integrity: sha512-4p6lOMU3bC+Vd5ARtKJ/FxpIC5G8v3XLoPEZ5s7mLR8h7411HWC/LmTXDHcrSXRC55zvAVia1eldy6zDLz8iFQ==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-linux-x64-musl@1.13.5': - resolution: {integrity: sha512-Luj8y4OFYx4DHNQTWjdIuKTq2f5k6uSXICqx+FSabnXptaOBAbJHNbHT/06JZh6NRUouaf0mYXN0mcsqvkhd7Q==} + '@swc/core-linux-x64-musl@1.15.8': + resolution: {integrity: sha512-z3XBnbrZAL+6xDGAhJoN4lOueIxC/8rGrJ9tg+fEaeqLEuAtHSW2QHDHxDwkxZMjuF/pZ6MUTjHjbp8wLbuRLA==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-win32-arm64-msvc@1.13.5': - resolution: {integrity: sha512-cZ6UpumhF9SDJvv4DA2fo9WIzlNFuKSkZpZmPG1c+4PFSEMy5DFOjBSllCvnqihCabzXzpn6ykCwBmHpy31vQw==} + '@swc/core-win32-arm64-msvc@1.15.8': + resolution: {integrity: sha512-djQPJ9Rh9vP8GTS/Df3hcc6XP6xnG5c8qsngWId/BLA9oX6C7UzCPAn74BG/wGb9a6j4w3RINuoaieJB3t+7iQ==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@swc/core-win32-ia32-msvc@1.13.5': - resolution: {integrity: sha512-C5Yi/xIikrFUzZcyGj9L3RpKljFvKiDMtyDzPKzlsDrKIw2EYY+bF88gB6oGY5RGmv4DAX8dbnpRAqgFD0FMEw==} + '@swc/core-win32-ia32-msvc@1.15.8': + resolution: {integrity: sha512-/wfAgxORg2VBaUoFdytcVBVCgf1isWZIEXB9MZEUty4wwK93M/PxAkjifOho9RN3WrM3inPLabICRCEgdHpKKQ==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - '@swc/core-win32-x64-msvc@1.13.5': - resolution: {integrity: sha512-YrKdMVxbYmlfybCSbRtrilc6UA8GF5aPmGKBdPvjrarvsmf4i7ZHGCEnLtfOMd3Lwbs2WUZq3WdMbozYeLU93Q==} + '@swc/core-win32-x64-msvc@1.15.8': + resolution: {integrity: sha512-GpMePrh9Sl4d61o4KAHOOv5is5+zt6BEXCOCgs/H0FLGeii7j9bWDE8ExvKFy2GRRZVNR1ugsnzaGWHKM6kuzA==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@swc/core@1.13.5': - resolution: {integrity: sha512-WezcBo8a0Dg2rnR82zhwoR6aRNxeTGfK5QCD6TQ+kg3xx/zNT02s/0o+81h/3zhvFSB24NtqEr8FTw88O5W/JQ==} + '@swc/core@1.15.8': + resolution: {integrity: sha512-T8keoJjXaSUoVBCIjgL6wAnhADIb09GOELzKg10CjNg+vLX48P93SME6jTfte9MZIm5m+Il57H3rTSk/0kzDUw==} engines: {node: '>=10'} peerDependencies: '@swc/helpers': '>=0.5.17' @@ -753,12 +845,12 @@ packages: resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==} engines: {node: '>=18'} - '@testing-library/jest-dom@6.8.0': - resolution: {integrity: sha512-WgXcWzVM6idy5JaftTVC8Vs83NKRmGJz4Hqs4oyOuO2J4r/y79vvKZsb+CaGyCSEbUPI6OsewfPd0G1A0/TUZQ==} + '@testing-library/jest-dom@6.9.1': + resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==} engines: {node: '>=14', npm: '>=6', yarn: '>=1'} - '@testing-library/react@16.3.0': - resolution: {integrity: sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw==} + '@testing-library/react@16.3.1': + resolution: {integrity: sha512-gr4KtAWqIOQoucWYD/f6ki+j5chXfcPc74Col/6poTyqTmn7zRmodWahWRCp8tYd+GMqBonw6hstNzqjbs6gjw==} engines: {node: '>=18'} peerDependencies: '@testing-library/dom': ^10.0.0 @@ -784,8 +876,8 @@ packages: '@types/aria-query@5.0.4': resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} - '@types/chai@5.2.2': - resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} '@types/d3-array@3.2.2': resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} @@ -826,24 +918,24 @@ packages: '@types/luxon@3.7.1': resolution: {integrity: sha512-H3iskjFIAn5SlJU7OuxUmTEpebK6TKB8rxZShDslBMZJ5u9S//KM1sbdAisiSrqwLQncVjnpi2OK2J51h+4lsg==} - '@types/node@24.5.2': - resolution: {integrity: sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==} + '@types/node@25.0.6': + resolution: {integrity: sha512-NNu0sjyNxpoiW3YuVFfNz7mxSQ+S4X2G28uqg2s+CzoqoQjLPsWSbsFFyztIAqt2vb8kfEAsJNepMGPTxFDx3Q==} '@types/prop-types@15.7.15': resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} - '@types/react-dom@19.1.9': - resolution: {integrity: sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==} + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: - '@types/react': ^19.0.0 + '@types/react': ^19.2.0 '@types/react-transition-group@4.4.12': resolution: {integrity: sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==} peerDependencies: '@types/react': '*' - '@types/react@19.1.13': - resolution: {integrity: sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ==} + '@types/react@19.2.8': + resolution: {integrity: sha512-3MbSL37jEchWZz2p2mjntRZtPt837ij10ApxKfgmXCTuHWagYg7iA5bqPw6C8BMPfwidlvfPI/fxOc42HLhcyg==} '@types/use-sync-external-store@0.0.6': resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} @@ -851,120 +943,121 @@ packages: '@types/warning@3.0.3': resolution: {integrity: sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q==} - '@typescript-eslint/eslint-plugin@8.44.0': - resolution: {integrity: sha512-EGDAOGX+uwwekcS0iyxVDmRV9HX6FLSM5kzrAToLTsr9OWCIKG/y3lQheCq18yZ5Xh78rRKJiEpP0ZaCs4ryOQ==} + '@typescript-eslint/eslint-plugin@8.52.0': + resolution: {integrity: sha512-okqtOgqu2qmZJ5iN4TWlgfF171dZmx2FzdOv2K/ixL2LZWDStL8+JgQerI2sa8eAEfoydG9+0V96m7V+P8yE1Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.44.0 + '@typescript-eslint/parser': ^8.52.0 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.44.0': - resolution: {integrity: sha512-VGMpFQGUQWYT9LfnPcX8ouFojyrZ/2w3K5BucvxL/spdNehccKhB4jUyB1yBCXpr2XFm0jkECxgrpXBW2ipoAw==} + '@typescript-eslint/parser@8.52.0': + resolution: {integrity: sha512-iIACsx8pxRnguSYhHiMn2PvhvfpopO9FXHyn1mG5txZIsAaB6F0KwbFnUQN3KCiG3Jcuad/Cao2FAs1Wp7vAyg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.44.0': - resolution: {integrity: sha512-ZeaGNraRsq10GuEohKTo4295Z/SuGcSq2LzfGlqiuEvfArzo/VRrT0ZaJsVPuKZ55lVbNk8U6FcL+ZMH8CoyVA==} + '@typescript-eslint/project-service@8.52.0': + resolution: {integrity: sha512-xD0MfdSdEmeFa3OmVqonHi+Cciab96ls1UhIF/qX/O/gPu5KXD0bY9lu33jj04fjzrXHcuvjBcBC+D3SNSadaw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@8.44.0': - resolution: {integrity: sha512-87Jv3E+al8wpD+rIdVJm/ItDBe/Im09zXIjFoipOjr5gHUhJmTzfFLuTJ/nPTMc2Srsroy4IBXwcTCHyRR7KzA==} + '@typescript-eslint/scope-manager@8.52.0': + resolution: {integrity: sha512-ixxqmmCcc1Nf8S0mS0TkJ/3LKcC8mruYJPOU6Ia2F/zUUR4pApW7LzrpU3JmtePbRUTes9bEqRc1Gg4iyRnDzA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.44.0': - resolution: {integrity: sha512-x5Y0+AuEPqAInc6yd0n5DAcvtoQ/vyaGwuX5HE9n6qAefk1GaedqrLQF8kQGylLUb9pnZyLf+iEiL9fr8APDtQ==} + '@typescript-eslint/tsconfig-utils@8.52.0': + resolution: {integrity: sha512-jl+8fzr/SdzdxWJznq5nvoI7qn2tNYV/ZBAEcaFMVXf+K6jmXvAFrgo/+5rxgnL152f//pDEAYAhhBAZGrVfwg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.44.0': - resolution: {integrity: sha512-9cwsoSxJ8Sak67Be/hD2RNt/fsqmWnNE1iHohG8lxqLSNY8xNfyY7wloo5zpW3Nu9hxVgURevqfcH6vvKCt6yg==} + '@typescript-eslint/type-utils@8.52.0': + resolution: {integrity: sha512-JD3wKBRWglYRQkAtsyGz1AewDu3mTc7NtRjR/ceTyGoPqmdS5oCdx/oZMWD5Zuqmo6/MpsYs0wp6axNt88/2EQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@8.44.0': - resolution: {integrity: sha512-ZSl2efn44VsYM0MfDQe68RKzBz75NPgLQXuGypmym6QVOWL5kegTZuZ02xRAT9T+onqvM6T8CdQk0OwYMB6ZvA==} + '@typescript-eslint/types@8.52.0': + resolution: {integrity: sha512-LWQV1V4q9V4cT4H5JCIx3481iIFxH1UkVk+ZkGGAV1ZGcjGI9IoFOfg3O6ywz8QqCDEp7Inlg6kovMofsNRaGg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.44.0': - resolution: {integrity: sha512-lqNj6SgnGcQZwL4/SBJ3xdPEfcBuhCG8zdcwCPgYcmiPLgokiNDKlbPzCwEwu7m279J/lBYWtDYL+87OEfn8Jw==} + '@typescript-eslint/typescript-estree@8.52.0': + resolution: {integrity: sha512-XP3LClsCc0FsTK5/frGjolyADTh3QmsLp6nKd476xNI9CsSsLnmn4f0jrzNoAulmxlmNIpeXuHYeEQv61Q6qeQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.44.0': - resolution: {integrity: sha512-nktOlVcg3ALo0mYlV+L7sWUD58KG4CMj1rb2HUVOO4aL3K/6wcD+NERqd0rrA5Vg06b42YhF6cFxeixsp9Riqg==} + '@typescript-eslint/utils@8.52.0': + resolution: {integrity: sha512-wYndVMWkweqHpEpwPhwqE2lnD2DxC6WVLupU/DOt/0/v+/+iQbbzO3jOHjmBMnhu0DgLULvOaU4h4pwHYi2oRQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/visitor-keys@8.44.0': - resolution: {integrity: sha512-zaz9u8EJ4GBmnehlrpoKvj/E3dNbuQ7q0ucyZImm3cLqJ8INTc970B1qEqDX/Rzq65r3TvVTN7kHWPBoyW7DWw==} + '@typescript-eslint/visitor-keys@8.52.0': + resolution: {integrity: sha512-ink3/Zofus34nmBsPjow63FP5M7IGff0RKAgqR6+CFpdk22M7aLwC9gOcLGYqr7MczLPzZVERW9hRog3O4n1sQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@uiw/react-json-view@2.0.0-alpha.38': - resolution: {integrity: sha512-uHH55qEO2Y7h2mFreRdDnAV6RiWWXSmUYBAKD4gTXg4CifhYKwj2uA18vI2oomEwxI2d4RW+BsyVKZlPvlynWA==} + '@uiw/react-json-view@2.0.0-alpha.40': + resolution: {integrity: sha512-j8YgmUrLAokX0k3TJC1+Rae3G2XS2hTYA9SsnQVWeQpn/PiqxwG8mI4A5TCASUTltPtpM/9Yp+mRm7L4Wjy8rw==} peerDependencies: '@babel/runtime': '>=7.10.0' react: '>=18.0.0' react-dom: '>=18.0.0' - '@vitejs/plugin-react-swc@3.11.0': - resolution: {integrity: sha512-YTJCGFdNMHCMfjODYtxRNVAYmTWQ1Lb8PulP/2/f/oEEtglw8oKxKIZmmRkyXrVrHfsKOaVkAc3NT9/dMutO5w==} + '@vitejs/plugin-react-swc@4.2.2': + resolution: {integrity: sha512-x+rE6tsxq/gxrEJN3Nv3dIV60lFflPj94c90b+NNo6n1QV1QQUTLoL0MpaOVasUZ0zqVBn7ead1B5ecx1JAGfA==} + engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: vite: ^4 || ^5 || ^6 || ^7 - '@vitest/expect@3.2.4': - resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + '@vitest/expect@4.0.16': + resolution: {integrity: sha512-eshqULT2It7McaJkQGLkPjPjNph+uevROGuIMJdG3V+0BSR2w9u6J9Lwu+E8cK5TETlfou8GRijhafIMhXsimA==} - '@vitest/mocker@3.2.4': - resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} + '@vitest/mocker@4.0.16': + resolution: {integrity: sha512-yb6k4AZxJTB+q9ycAvsoxGn+j/po0UaPgajllBgt1PzoMAAmJGYFdDk0uCcRcxb3BrME34I6u8gHZTQlkqSZpg==} peerDependencies: msw: ^2.4.9 - vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + vite: ^6.0.0 || ^7.0.0-0 peerDependenciesMeta: msw: optional: true vite: optional: true - '@vitest/pretty-format@3.2.4': - resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + '@vitest/pretty-format@4.0.16': + resolution: {integrity: sha512-eNCYNsSty9xJKi/UdVD8Ou16alu7AYiS2fCPRs0b1OdhJiV89buAXQLpTbe+X8V9L6qrs9CqyvU7OaAopJYPsA==} - '@vitest/runner@3.2.4': - resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} + '@vitest/runner@4.0.16': + resolution: {integrity: sha512-VWEDm5Wv9xEo80ctjORcTQRJ539EGPB3Pb9ApvVRAY1U/WkHXmmYISqU5E79uCwcW7xYUV38gwZD+RV755fu3Q==} - '@vitest/snapshot@3.2.4': - resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} + '@vitest/snapshot@4.0.16': + resolution: {integrity: sha512-sf6NcrYhYBsSYefxnry+DR8n3UV4xWZwWxYbCJUt2YdvtqzSPR7VfGrY0zsv090DAbjFZsi7ZaMi1KnSRyK1XA==} - '@vitest/spy@3.2.4': - resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} + '@vitest/spy@4.0.16': + resolution: {integrity: sha512-4jIOWjKP0ZUaEmJm00E0cOBLU+5WE0BpeNr3XN6TEF05ltro6NJqHWxXD0kA8/Zc8Nh23AT8WQxwNG+WeROupw==} - '@vitest/utils@3.2.4': - resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + '@vitest/utils@4.0.16': + resolution: {integrity: sha512-h8z9yYhV3e1LEfaQ3zdypIrnAg/9hguReGZoS7Gl0aBG5xgA410zBqECqmaF/+RkTggRsfnzc1XaAHA6bmUufA==} - '@volar/language-core@2.4.23': - resolution: {integrity: sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==} + '@volar/language-core@2.4.27': + resolution: {integrity: sha512-DjmjBWZ4tJKxfNC1F6HyYERNHPYS7L7OPFyCrestykNdUZMFYzI9WTyvwPcaNaHlrEUwESHYsfEw3isInncZxQ==} - '@volar/source-map@2.4.23': - resolution: {integrity: sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q==} + '@volar/source-map@2.4.27': + resolution: {integrity: sha512-ynlcBReMgOZj2i6po+qVswtDUeeBRCTgDurjMGShbm8WYZgJ0PA4RmtebBJ0BCYol1qPv3GQF6jK7C9qoVc7lg==} - '@volar/typescript@2.4.23': - resolution: {integrity: sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==} + '@volar/typescript@2.4.27': + resolution: {integrity: sha512-eWaYCcl/uAPInSK2Lze6IqVWaBu/itVqR5InXcHXFyles4zO++Mglt3oxdgj75BDcv1Knr9Y93nowS8U3wqhxg==} - '@vue/compiler-core@3.5.21': - resolution: {integrity: sha512-8i+LZ0vf6ZgII5Z9XmUvrCyEzocvWT+TeR2VBUVlzIH6Tyv57E20mPZ1bCS+tbejgUgmjrEh7q/0F0bibskAmw==} + '@vue/compiler-core@3.5.26': + resolution: {integrity: sha512-vXyI5GMfuoBCnv5ucIT7jhHKl55Y477yxP6fc4eUswjP8FG3FFVFd41eNDArR+Uk3QKn2Z85NavjaxLxOC19/w==} - '@vue/compiler-dom@3.5.21': - resolution: {integrity: sha512-jNtbu/u97wiyEBJlJ9kmdw7tAr5Vy0Aj5CgQmo+6pxWNQhXZDPsRr1UWPN4v3Zf82s2H3kF51IbzZ4jMWAgPlQ==} + '@vue/compiler-dom@3.5.26': + resolution: {integrity: sha512-y1Tcd3eXs834QjswshSilCBnKGeQjQXB6PqFn/1nxcQw4pmG42G8lwz+FZPAZAby6gZeHSt/8LMPfZ4Rb+Bd/A==} '@vue/compiler-vue2@2.7.16': resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} @@ -977,8 +1070,8 @@ packages: typescript: optional: true - '@vue/shared@3.5.21': - resolution: {integrity: sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw==} + '@vue/shared@3.5.26': + resolution: {integrity: sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A==} acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} @@ -1090,12 +1183,19 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - axios@1.12.2: - resolution: {integrity: sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==} + axios@1.13.2: + resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==} balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + baseline-browser-mapping@2.9.14: + resolution: {integrity: sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg==} + hasBin: true + + bidi-js@1.0.3: + resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} + bootstrap@5.3.8: resolution: {integrity: sha512-HP1SZDqaLDPwsNiqRqi5NcP0SSXciX2s9E+RyqJIIqGo+vJeN5AJVM98CXmW/Wux0nQ5L7jeWUdplCEf0Ee+tg==} peerDependencies: @@ -1107,13 +1207,10 @@ packages: brace-expansion@2.0.2: resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} - braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} - - cac@6.7.14: - resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} - engines: {node: '>=8'} + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} @@ -1131,18 +1228,17 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - chai@5.3.3: - resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + caniuse-lite@1.0.30001764: + resolution: {integrity: sha512-9JGuzl2M+vPL+pz70gtMF9sHdMFbY9FJaQBi186cHKH3pSzDvzoUJUPV6fqiKIMyXbud9ZLg4F3Yza1vJ1+93g==} + + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - check-error@2.1.1: - resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} - engines: {node: '>= 16'} - classnames@2.5.1: resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} @@ -1173,25 +1269,35 @@ packages: confbox@0.2.2: resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} - crossroad@1.3.3: - resolution: {integrity: sha512-8QurKhUX2uR2c1XiyqOgScW9NDFofnfH56b6jvQyyfrjgKwzhJC2R0xt1YyhGWdPmjCX/SkpYu/qlBKW350Cxw==} + crossroad@2.0.0: + resolution: {integrity: sha512-ZvglOpHn5sNZOQrdth+LzRZe1QkQr129nR4o0B7Se37FPLjuZIxiGLMWjiiqHNohFwDDButGcHylKumQKy8lVQ==} peerDependencies: react: '>=16.8.0' + css-tree@3.1.0: + resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + css.escape@1.5.1: resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} - cssstyle@4.6.0: - resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==} - engines: {node: '>=18'} + cssstyle@5.3.7: + resolution: {integrity: sha512-7D2EPVltRrsTkhpQmksIu+LxeWAIEk6wRDMJ1qljlv+CKHJM+cJLlfhWIzNA44eAsHXSNe3+vO6DW1yCYx8SuQ==} + engines: {node: '>=20'} csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + d3-array@3.2.4: resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} engines: {node: '>=12'} @@ -1236,9 +1342,9 @@ packages: resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} engines: {node: '>=12'} - data-urls@5.0.0: - resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} - engines: {node: '>=18'} + data-urls@6.0.0: + resolution: {integrity: sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA==} + engines: {node: '>=20'} data-view-buffer@1.0.2: resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} @@ -1270,10 +1376,6 @@ packages: decimal.js@10.6.0: resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} - deep-eql@5.0.2: - resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} - engines: {node: '>=6'} - deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -1293,6 +1395,10 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} + diff@8.0.2: + resolution: {integrity: sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==} + engines: {node: '>=0.3.1'} + doctrine@2.1.0: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} @@ -1310,16 +1416,19 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} - entities@4.5.0: - resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} - engines: {node: '>=0.12'} + electron-to-chromium@1.5.267: + resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} entities@6.0.1: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} - es-abstract@1.24.0: - resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==} + entities@7.0.0: + resolution: {integrity: sha512-FDWG5cmEYf2Z00IkYRhbFrwIwvdFKH07uV8dvNy0omp/Qb1xcyCWp2UDtcwJF4QZZvk0sLudP6/hAu42TaqVhQ==} + engines: {node: '>=0.12'} + + es-abstract@1.24.1: + resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} engines: {node: '>= 0.4'} es-define-property@1.0.1: @@ -1330,8 +1439,8 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-iterator-helpers@1.2.1: - resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} + es-iterator-helpers@1.2.2: + resolution: {integrity: sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==} engines: {node: '>= 0.4'} es-module-lexer@1.7.0: @@ -1353,26 +1462,30 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} - es-toolkit@1.39.10: - resolution: {integrity: sha512-E0iGnTtbDhkeczB0T+mxmoVlT4YNweEKBLq7oaU4p11mecdsZpNWOglI4895Vh4usbQ+LsJiuLuI2L0Vdmfm2w==} + es-toolkit@1.43.0: + resolution: {integrity: sha512-SKCT8AsWvYzBBuUqMk4NPwFlSdqLpJwmy6AP322ERn8W2YLIB6JBXnwMI2Qsh2gfphT3q7EKAxKb23cvFHFwKA==} - esbuild@0.25.10: - resolution: {integrity: sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==} + esbuild@0.27.2: + resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} engines: {node: '>=18'} hasBin: true + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - eslint-plugin-react-hooks@5.2.0: - resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} - engines: {node: '>=10'} + eslint-plugin-react-hooks@7.0.1: + resolution: {integrity: sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==} + engines: {node: '>=18'} peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 - eslint-plugin-react-refresh@0.4.20: - resolution: {integrity: sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==} + eslint-plugin-react-refresh@0.4.26: + resolution: {integrity: sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==} peerDependencies: eslint: '>=8.40' @@ -1394,8 +1507,8 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.36.0: - resolution: {integrity: sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==} + eslint@9.39.2: + resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -1408,8 +1521,8 @@ packages: resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - esquery@1.6.0: - resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} engines: {node: '>=0.10'} esrecurse@4.3.0: @@ -1433,29 +1546,22 @@ packages: eventemitter3@5.0.1: resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} - expect-type@1.2.2: - resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} - exsolve@1.0.7: - resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==} + exsolve@1.0.8: + resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==} fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} - fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fastq@1.19.1: - resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} - fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -1469,10 +1575,6 @@ packages: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} - fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} - find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -1497,12 +1599,12 @@ packages: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} - form-data@4.0.4: - resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} engines: {node: '>= 6'} - fs-extra@11.3.2: - resolution: {integrity: sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==} + fs-extra@11.3.3: + resolution: {integrity: sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==} engines: {node: '>=14.14'} fsevents@2.3.3: @@ -1520,6 +1622,14 @@ packages: functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -1532,10 +1642,6 @@ packages: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} @@ -1544,8 +1650,8 @@ packages: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} - globals@16.4.0: - resolution: {integrity: sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==} + globals@17.0.0: + resolution: {integrity: sha512-gv5BeD2EssA793rlFWVPMMCqefTlpusw6/2TbAVMy0FzcG8wKJn4O+NqJ4+XWmmwrayJgw5TzrmWjFgmz1XPqw==} engines: {node: '>=18'} globalthis@1.0.4: @@ -1559,9 +1665,6 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - has-bigints@1.1.0: resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} engines: {node: '>= 0.4'} @@ -1593,9 +1696,15 @@ packages: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true - html-encoding-sniffer@4.0.0: - resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} - engines: {node: '>=18'} + hermes-estree@0.25.1: + resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} + + hermes-parser@0.25.1: + resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + + html-encoding-sniffer@6.0.0: + resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} http-proxy-agent@7.0.2: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} @@ -1605,10 +1714,6 @@ packages: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} - iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} - engines: {node: '>=0.10.0'} - ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -1617,8 +1722,11 @@ packages: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} - immer@10.1.3: - resolution: {integrity: sha512-tmjF/k8QDKydUlm3mZU+tjM6zeq9/fFpPqH9SzWmBnVVKsPBg/V66qsMwb3/Bo90cgUN+ghdVBess+hPsxUyRw==} + immer@10.2.0: + resolution: {integrity: sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==} + + immer@11.1.3: + resolution: {integrity: sha512-6jQTc5z0KJFtr1UgFpIL3N9XSC3saRaI9PwWtzM2pSqkNGtiNkYY2OSwkOGDK2XcTRcLb1pi/aNkKZz0nxVH4Q==} import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} @@ -1687,8 +1795,8 @@ packages: resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} engines: {node: '>= 0.4'} - is-generator-function@1.1.0: - resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==} + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} engines: {node: '>= 0.4'} is-glob@4.0.3: @@ -1707,10 +1815,6 @@ packages: resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} engines: {node: '>= 0.4'} - is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} @@ -1766,22 +1870,24 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-tokens@9.0.1: - resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} - - js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true - jsdom@26.1.0: - resolution: {integrity: sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==} - engines: {node: '>=18'} + jsdom@27.4.0: + resolution: {integrity: sha512-mjzqwWRD9Y1J1KUi7W97Gja1bwOOM5Ug0EZ6UDK3xS7j7mndrkwozHtSblfomlzyB4NepioNt+B2sOSzczVgtQ==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: canvas: ^3.0.0 peerDependenciesMeta: canvas: optional: true + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} @@ -1794,6 +1900,11 @@ packages: json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + jsonfile@6.2.0: resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} @@ -1829,11 +1940,12 @@ packages: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true - loupe@3.2.1: - resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + lru-cache@11.2.4: + resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==} + engines: {node: 20 || >=22} - lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} lru-cache@6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} @@ -1847,20 +1959,15 @@ packages: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true - magic-string@0.30.19: - resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - - micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} + mdn-data@2.12.2: + resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} @@ -1902,8 +2009,8 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - nwsapi@2.2.22: - resolution: {integrity: sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ==} + node-releases@2.0.27: + resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} @@ -1933,6 +2040,9 @@ packages: resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} + obug@2.1.1: + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -1953,8 +2063,8 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} - parse5@7.3.0: - resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + parse5@8.0.0: + resolution: {integrity: sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==} path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} @@ -1973,17 +2083,9 @@ packages: pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - pathval@2.0.1: - resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} - engines: {node: '>= 14.16'} - picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - picomatch@4.0.3: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} @@ -2028,9 +2130,6 @@ packages: quansync@0.2.11: resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - react-bootstrap-icons@1.11.6: resolution: {integrity: sha512-ycXiyeSyzbS1C4+MlPTYe0riB+UlZ7LV7YZQYqlERV2cxDiKtntI0huHmP/3VVvzPt4tGxqK0K+Y6g7We3U6tQ==} peerDependencies: @@ -2046,10 +2145,10 @@ packages: '@types/react': optional: true - react-dom@19.1.1: - resolution: {integrity: sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==} + react-dom@19.2.3: + resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==} peerDependencies: - react: ^19.1.1 + react: ^19.2.3 react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -2078,12 +2177,12 @@ packages: react: '>=16.6.0' react-dom: '>=16.6.0' - react@19.1.1: - resolution: {integrity: sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==} + react@19.2.3: + resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==} engines: {node: '>=0.10.0'} - recharts@3.2.1: - resolution: {integrity: sha512-0JKwHRiFZdmLq/6nmilxEZl3pqb4T+aKkOkOi/ZISRZwfBhVMgInxzlYU9D4KnCH3KINScLy68m/OvMXoYGZUw==} + recharts@3.6.0: + resolution: {integrity: sha512-L5bjxvQRAe26RlToBAziKUB7whaGKEwD3znoM6fz3DrTowCIC/FnJYnuq1GEzB8Zv2kdTfaxQfi5GoH0tBinyg==} engines: {node: '>=18'} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -2121,8 +2220,8 @@ packages: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} - resolve@1.22.10: - resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + resolve@1.22.11: + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} engines: {node: '>= 0.4'} hasBin: true @@ -2130,21 +2229,11 @@ packages: resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} hasBin: true - reusify@1.1.0: - resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - - rollup@4.52.0: - resolution: {integrity: sha512-+IuescNkTJQgX7AkIDtITipZdIGcWF0pnVvZTWStiazUmcGA2ag8dfg0urest2XlXUi9kuhfQ+qmdc5Stc3z7g==} + rollup@4.55.1: + resolution: {integrity: sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - rrweb-cssom@0.8.0: - resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} - - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - safe-array-concat@1.1.3: resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} engines: {node: '>=0.4'} @@ -2157,15 +2246,12 @@ packages: resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} engines: {node: '>= 0.4'} - safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - saxes@6.0.0: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} - scheduler@0.26.0: - resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} @@ -2176,8 +2262,8 @@ packages: engines: {node: '>=10'} hasBin: true - semver@7.7.2: - resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} engines: {node: '>=10'} hasBin: true @@ -2234,8 +2320,8 @@ packages: stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - std-env@3.9.0: - resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} stop-iteration-iterator@1.1.0: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} @@ -2272,9 +2358,6 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - strip-literal@3.0.0: - resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==} - supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -2296,46 +2379,35 @@ packages: tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinyexec@0.3.2: - resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + tinyexec@1.0.2: + resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} + engines: {node: '>=18'} tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} - tinypool@1.1.1: - resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} - engines: {node: ^18.0.0 || >=20.0.0} - - tinyrainbow@2.0.0: - resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + tinyrainbow@3.0.3: + resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} engines: {node: '>=14.0.0'} - tinyspy@4.0.4: - resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} - engines: {node: '>=14.0.0'} + tldts-core@7.0.19: + resolution: {integrity: sha512-lJX2dEWx0SGH4O6p+7FPwYmJ/bu1JbcGJ8RLaG9b7liIgZ85itUVEPbMtWRVrde/0fnDPEPHW10ZsKW3kVsE9A==} - tldts-core@6.1.86: - resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==} - - tldts@6.1.86: - resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==} + tldts@7.0.19: + resolution: {integrity: sha512-8PWx8tvC4jDB39BQw1m4x8y5MH1BcQ5xHeL2n7UVFulMPH/3Q0uiamahFJ3lXA0zO2SUyRXuVVbWSDmstlt9YA==} hasBin: true - to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - - tough-cookie@5.1.2: - resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==} + tough-cookie@6.0.0: + resolution: {integrity: sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==} engines: {node: '>=16'} - tr46@5.1.1: - resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} - engines: {node: '>=18'} + tr46@6.0.0: + resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} + engines: {node: '>=20'} - ts-api-utils@2.1.0: - resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + ts-api-utils@2.4.0: + resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} engines: {node: '>=18.12'} peerDependencies: typescript: '>=4.8.4' @@ -2363,8 +2435,8 @@ packages: resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} - typescript-eslint@8.44.0: - resolution: {integrity: sha512-ib7mCkYuIzYonCq9XWF5XNw+fkj2zg629PSa9KNIQ47RXFF763S5BIX4wqz1+FLPogTZoiw8KmCiRPRa8bL3qw==} + typescript-eslint@8.52.0: + resolution: {integrity: sha512-atlQQJ2YkO4pfTVQmQ+wvYQwexPDOIgo+RaVcD7gHgzy/IQA+XTyuxNM9M9TVXvttkF7koBHmcwisKdOAf2EcA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -2375,13 +2447,13 @@ packages: engines: {node: '>=14.17'} hasBin: true - typescript@5.8.3: - resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} hasBin: true - ufo@1.6.1: - resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} + ufo@1.6.2: + resolution: {integrity: sha512-heMioaxBcG9+Znsda5Q8sQbWnLJSl98AFDXTO80wELWEzX3hordXsTdxrIfMQoO9IY1MEnoGoPjpoKpMj+Yx0Q==} unbox-primitive@1.1.0: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} @@ -2397,29 +2469,30 @@ packages: peerDependencies: react: '>=16.14.0' - undici-types@7.12.0: - resolution: {integrity: sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==} + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - use-sync-external-store@1.5.0: - resolution: {integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==} + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 victory-vendor@37.3.6: resolution: {integrity: sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==} - vite-node@3.2.4: - resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} - hasBin: true - vite-plugin-dts@4.5.4: resolution: {integrity: sha512-d4sOM8M/8z7vRXHHq/ebbblfaxENjogAAekcfcDCCwAyvGqnPrc7f4NZbvItS+g4WTgerW0xDwSz5qz11JT3vg==} peerDependencies: @@ -2429,8 +2502,8 @@ packages: vite: optional: true - vite@7.1.7: - resolution: {integrity: sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA==} + vite@7.3.1: + resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -2469,26 +2542,32 @@ packages: yaml: optional: true - vitest@3.2.4: - resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + vitest@4.0.16: + resolution: {integrity: sha512-E4t7DJ9pESL6E3I8nFjPa4xGUd3PmiWDLsDztS2qXSJWfHtbQnwAWylaBvSNY48I3vr8PTqIZlyK8TE3V3CA4Q==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' - '@types/debug': ^4.1.12 - '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 - '@vitest/browser': 3.2.4 - '@vitest/ui': 3.2.4 + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.0.16 + '@vitest/browser-preview': 4.0.16 + '@vitest/browser-webdriverio': 4.0.16 + '@vitest/ui': 4.0.16 happy-dom: '*' jsdom: '*' peerDependenciesMeta: '@edge-runtime/vm': optional: true - '@types/debug': + '@opentelemetry/api': optional: true '@types/node': optional: true - '@vitest/browser': + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': optional: true '@vitest/ui': optional: true @@ -2507,21 +2586,17 @@ packages: warning@4.0.3: resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==} - webidl-conversions@7.0.0: - resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} - engines: {node: '>=12'} - - whatwg-encoding@3.1.1: - resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} - engines: {node: '>=18'} + webidl-conversions@8.0.1: + resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} + engines: {node: '>=20'} whatwg-mimetype@4.0.0: resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} engines: {node: '>=18'} - whatwg-url@14.2.0: - resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==} - engines: {node: '>=18'} + whatwg-url@15.1.0: + resolution: {integrity: sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==} + engines: {node: '>=20'} which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} @@ -2553,8 +2628,8 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - ws@8.18.3: - resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} + ws@8.19.0: + resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -2572,6 +2647,9 @@ packages: xmlchars@2.2.0: resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} @@ -2579,38 +2657,140 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + zod-validation-error@4.0.2: + resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + + zod@4.3.5: + resolution: {integrity: sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==} + snapshots: + '@acemir/cssom@0.9.30': {} + '@adobe/css-tools@4.4.4': {} - '@asamuzakjp/css-color@3.2.0': + '@asamuzakjp/css-color@4.1.1': dependencies: '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) '@csstools/css-tokenizer': 3.0.4 - lru-cache: 10.4.3 + lru-cache: 11.2.4 + + '@asamuzakjp/dom-selector@6.7.6': + dependencies: + '@asamuzakjp/nwsapi': 2.3.9 + bidi-js: 1.0.3 + css-tree: 3.1.0 + is-potential-custom-element-name: 1.0.1 + lru-cache: 11.2.4 + + '@asamuzakjp/nwsapi@2.3.9': {} '@babel/code-frame@7.27.1': dependencies: - '@babel/helper-validator-identifier': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 js-tokens: 4.0.0 picocolors: 1.1.1 + '@babel/compat-data@7.28.5': {} + + '@babel/core@7.28.5': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.28.5': + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.27.2': + dependencies: + '@babel/compat-data': 7.28.5 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.1 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + '@babel/helper-string-parser@7.27.1': {} - '@babel/helper-validator-identifier@7.27.1': {} + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.28.4': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 - '@babel/parser@7.28.4': + '@babel/parser@7.28.5': dependencies: - '@babel/types': 7.28.4 + '@babel/types': 7.28.5 '@babel/runtime@7.28.4': {} - '@babel/types@7.28.4': + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + + '@babel/traverse@7.28.5': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.28.5': dependencies: '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 '@csstools/color-helpers@5.1.0': {} @@ -2630,108 +2810,112 @@ snapshots: dependencies: '@csstools/css-tokenizer': 3.0.4 + '@csstools/css-syntax-patches-for-csstree@1.0.25': {} + '@csstools/css-tokenizer@3.0.4': {} - '@esbuild/aix-ppc64@0.25.10': + '@esbuild/aix-ppc64@0.27.2': optional: true - '@esbuild/android-arm64@0.25.10': + '@esbuild/android-arm64@0.27.2': optional: true - '@esbuild/android-arm@0.25.10': + '@esbuild/android-arm@0.27.2': optional: true - '@esbuild/android-x64@0.25.10': + '@esbuild/android-x64@0.27.2': optional: true - '@esbuild/darwin-arm64@0.25.10': + '@esbuild/darwin-arm64@0.27.2': optional: true - '@esbuild/darwin-x64@0.25.10': + '@esbuild/darwin-x64@0.27.2': optional: true - '@esbuild/freebsd-arm64@0.25.10': + '@esbuild/freebsd-arm64@0.27.2': optional: true - '@esbuild/freebsd-x64@0.25.10': + '@esbuild/freebsd-x64@0.27.2': optional: true - '@esbuild/linux-arm64@0.25.10': + '@esbuild/linux-arm64@0.27.2': optional: true - '@esbuild/linux-arm@0.25.10': + '@esbuild/linux-arm@0.27.2': optional: true - '@esbuild/linux-ia32@0.25.10': + '@esbuild/linux-ia32@0.27.2': optional: true - '@esbuild/linux-loong64@0.25.10': + '@esbuild/linux-loong64@0.27.2': optional: true - '@esbuild/linux-mips64el@0.25.10': + '@esbuild/linux-mips64el@0.27.2': optional: true - '@esbuild/linux-ppc64@0.25.10': + '@esbuild/linux-ppc64@0.27.2': optional: true - '@esbuild/linux-riscv64@0.25.10': + '@esbuild/linux-riscv64@0.27.2': optional: true - '@esbuild/linux-s390x@0.25.10': + '@esbuild/linux-s390x@0.27.2': optional: true - '@esbuild/linux-x64@0.25.10': + '@esbuild/linux-x64@0.27.2': optional: true - '@esbuild/netbsd-arm64@0.25.10': + '@esbuild/netbsd-arm64@0.27.2': optional: true - '@esbuild/netbsd-x64@0.25.10': + '@esbuild/netbsd-x64@0.27.2': optional: true - '@esbuild/openbsd-arm64@0.25.10': + '@esbuild/openbsd-arm64@0.27.2': optional: true - '@esbuild/openbsd-x64@0.25.10': + '@esbuild/openbsd-x64@0.27.2': optional: true - '@esbuild/openharmony-arm64@0.25.10': + '@esbuild/openharmony-arm64@0.27.2': optional: true - '@esbuild/sunos-x64@0.25.10': + '@esbuild/sunos-x64@0.27.2': optional: true - '@esbuild/win32-arm64@0.25.10': + '@esbuild/win32-arm64@0.27.2': optional: true - '@esbuild/win32-ia32@0.25.10': + '@esbuild/win32-ia32@0.27.2': optional: true - '@esbuild/win32-x64@0.25.10': + '@esbuild/win32-x64@0.27.2': optional: true - '@eslint-community/eslint-utils@4.9.0(eslint@9.36.0)': + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.2)': dependencies: - eslint: 9.36.0 + eslint: 9.39.2 eslint-visitor-keys: 3.4.3 - '@eslint-community/regexpp@4.12.1': {} + '@eslint-community/regexpp@4.12.2': {} - '@eslint/config-array@0.21.0': + '@eslint/config-array@0.21.1': dependencies: - '@eslint/object-schema': 2.1.6 + '@eslint/object-schema': 2.1.7 debug: 4.4.3 minimatch: 3.1.2 transitivePeerDependencies: - supports-color - '@eslint/config-helpers@0.3.1': {} + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 - '@eslint/core@0.15.2': + '@eslint/core@0.17.0': dependencies: '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.3.1': + '@eslint/eslintrc@3.3.3': dependencies: ajv: 6.12.6 debug: 4.4.3 @@ -2739,21 +2923,23 @@ snapshots: globals: 14.0.0 ignore: 5.3.2 import-fresh: 3.3.1 - js-yaml: 4.1.0 + js-yaml: 4.1.1 minimatch: 3.1.2 strip-json-comments: 3.1.1 transitivePeerDependencies: - supports-color - '@eslint/js@9.36.0': {} + '@eslint/js@9.39.2': {} - '@eslint/object-schema@2.1.6': {} + '@eslint/object-schema@2.1.7': {} - '@eslint/plugin-kit@0.3.5': + '@eslint/plugin-kit@0.4.1': dependencies: - '@eslint/core': 0.15.2 + '@eslint/core': 0.17.0 levn: 0.4.1 + '@exodus/bytes@1.8.0': {} + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.7': @@ -2771,257 +2957,277 @@ snapshots: dependencies: '@isaacs/balanced-match': 4.0.1 + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + '@jridgewell/sourcemap-codec@1.5.5': {} - '@microsoft/api-extractor-model@7.30.7(@types/node@24.5.2)': + '@jridgewell/trace-mapping@0.3.31': dependencies: - '@microsoft/tsdoc': 0.15.1 - '@microsoft/tsdoc-config': 0.17.1 - '@rushstack/node-core-library': 5.14.0(@types/node@24.5.2) + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@microsoft/api-extractor-model@7.32.2(@types/node@25.0.6)': + dependencies: + '@microsoft/tsdoc': 0.16.0 + '@microsoft/tsdoc-config': 0.18.0 + '@rushstack/node-core-library': 5.19.1(@types/node@25.0.6) transitivePeerDependencies: - '@types/node' - '@microsoft/api-extractor@7.52.13(@types/node@24.5.2)': + '@microsoft/api-extractor@7.55.2(@types/node@25.0.6)': dependencies: - '@microsoft/api-extractor-model': 7.30.7(@types/node@24.5.2) - '@microsoft/tsdoc': 0.15.1 - '@microsoft/tsdoc-config': 0.17.1 - '@rushstack/node-core-library': 5.14.0(@types/node@24.5.2) - '@rushstack/rig-package': 0.5.3 - '@rushstack/terminal': 0.16.0(@types/node@24.5.2) - '@rushstack/ts-command-line': 5.0.3(@types/node@24.5.2) + '@microsoft/api-extractor-model': 7.32.2(@types/node@25.0.6) + '@microsoft/tsdoc': 0.16.0 + '@microsoft/tsdoc-config': 0.18.0 + '@rushstack/node-core-library': 5.19.1(@types/node@25.0.6) + '@rushstack/rig-package': 0.6.0 + '@rushstack/terminal': 0.19.5(@types/node@25.0.6) + '@rushstack/ts-command-line': 5.1.5(@types/node@25.0.6) + diff: 8.0.2 lodash: 4.17.21 minimatch: 10.0.3 - resolve: 1.22.10 + resolve: 1.22.11 semver: 7.5.4 source-map: 0.6.1 typescript: 5.8.2 transitivePeerDependencies: - '@types/node' - '@microsoft/tsdoc-config@0.17.1': + '@microsoft/tsdoc-config@0.18.0': dependencies: - '@microsoft/tsdoc': 0.15.1 + '@microsoft/tsdoc': 0.16.0 ajv: 8.12.0 jju: 1.4.0 - resolve: 1.22.10 - - '@microsoft/tsdoc@0.15.1': {} - - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} + resolve: 1.22.11 - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.19.1 + '@microsoft/tsdoc@0.16.0': {} '@popperjs/core@2.11.8': {} - '@react-aria/ssr@3.9.10(react@19.1.1)': + '@react-aria/ssr@3.9.10(react@19.2.3)': dependencies: '@swc/helpers': 0.5.17 - react: 19.1.1 + react: 19.2.3 - '@reduxjs/toolkit@2.9.0(react-redux@9.2.0(@types/react@19.1.13)(react@19.1.1)(redux@5.0.1))(react@19.1.1)': + '@reduxjs/toolkit@2.11.2(react-redux@9.2.0(@types/react@19.2.8)(react@19.2.3)(redux@5.0.1))(react@19.2.3)': dependencies: - '@standard-schema/spec': 1.0.0 + '@standard-schema/spec': 1.1.0 '@standard-schema/utils': 0.3.0 - immer: 10.1.3 + immer: 11.1.3 redux: 5.0.1 redux-thunk: 3.1.0(redux@5.0.1) reselect: 5.1.1 optionalDependencies: - react: 19.1.1 - react-redux: 9.2.0(@types/react@19.1.13)(react@19.1.1)(redux@5.0.1) + react: 19.2.3 + react-redux: 9.2.0(@types/react@19.2.8)(react@19.2.3)(redux@5.0.1) - '@restart/hooks@0.4.16(react@19.1.1)': + '@restart/hooks@0.4.16(react@19.2.3)': dependencies: dequal: 2.0.3 - react: 19.1.1 + react: 19.2.3 - '@restart/hooks@0.5.1(react@19.1.1)': + '@restart/hooks@0.5.1(react@19.2.3)': dependencies: dequal: 2.0.3 - react: 19.1.1 + react: 19.2.3 - '@restart/ui@1.9.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@restart/ui@1.9.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@babel/runtime': 7.28.4 '@popperjs/core': 2.11.8 - '@react-aria/ssr': 3.9.10(react@19.1.1) - '@restart/hooks': 0.5.1(react@19.1.1) + '@react-aria/ssr': 3.9.10(react@19.2.3) + '@restart/hooks': 0.5.1(react@19.2.3) '@types/warning': 3.0.3 dequal: 2.0.3 dom-helpers: 5.2.1 - react: 19.1.1 - react-dom: 19.1.1(react@19.1.1) - uncontrollable: 8.0.4(react@19.1.1) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + uncontrollable: 8.0.4(react@19.2.3) warning: 4.0.3 - '@rolldown/pluginutils@1.0.0-beta.27': {} + '@rolldown/pluginutils@1.0.0-beta.47': {} - '@rollup/pluginutils@5.3.0(rollup@4.52.0)': + '@rollup/pluginutils@5.3.0(rollup@4.55.1)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 picomatch: 4.0.3 optionalDependencies: - rollup: 4.52.0 + rollup: 4.55.1 + + '@rollup/rollup-android-arm-eabi@4.55.1': + optional: true - '@rollup/rollup-android-arm-eabi@4.52.0': + '@rollup/rollup-android-arm64@4.55.1': optional: true - '@rollup/rollup-android-arm64@4.52.0': + '@rollup/rollup-darwin-arm64@4.55.1': optional: true - '@rollup/rollup-darwin-arm64@4.52.0': + '@rollup/rollup-darwin-x64@4.55.1': optional: true - '@rollup/rollup-darwin-x64@4.52.0': + '@rollup/rollup-freebsd-arm64@4.55.1': optional: true - '@rollup/rollup-freebsd-arm64@4.52.0': + '@rollup/rollup-freebsd-x64@4.55.1': optional: true - '@rollup/rollup-freebsd-x64@4.52.0': + '@rollup/rollup-linux-arm-gnueabihf@4.55.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.52.0': + '@rollup/rollup-linux-arm-musleabihf@4.55.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.52.0': + '@rollup/rollup-linux-arm64-gnu@4.55.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.52.0': + '@rollup/rollup-linux-arm64-musl@4.55.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.52.0': + '@rollup/rollup-linux-loong64-gnu@4.55.1': optional: true - '@rollup/rollup-linux-loong64-gnu@4.52.0': + '@rollup/rollup-linux-loong64-musl@4.55.1': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.52.0': + '@rollup/rollup-linux-ppc64-gnu@4.55.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.52.0': + '@rollup/rollup-linux-ppc64-musl@4.55.1': optional: true - '@rollup/rollup-linux-riscv64-musl@4.52.0': + '@rollup/rollup-linux-riscv64-gnu@4.55.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.52.0': + '@rollup/rollup-linux-riscv64-musl@4.55.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.52.0': + '@rollup/rollup-linux-s390x-gnu@4.55.1': optional: true - '@rollup/rollup-linux-x64-musl@4.52.0': + '@rollup/rollup-linux-x64-gnu@4.55.1': optional: true - '@rollup/rollup-openharmony-arm64@4.52.0': + '@rollup/rollup-linux-x64-musl@4.55.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.52.0': + '@rollup/rollup-openbsd-x64@4.55.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.52.0': + '@rollup/rollup-openharmony-arm64@4.55.1': optional: true - '@rollup/rollup-win32-x64-gnu@4.52.0': + '@rollup/rollup-win32-arm64-msvc@4.55.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.52.0': + '@rollup/rollup-win32-ia32-msvc@4.55.1': optional: true - '@rushstack/node-core-library@5.14.0(@types/node@24.5.2)': + '@rollup/rollup-win32-x64-gnu@4.55.1': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.55.1': + optional: true + + '@rushstack/node-core-library@5.19.1(@types/node@25.0.6)': dependencies: ajv: 8.13.0 ajv-draft-04: 1.0.0(ajv@8.13.0) ajv-formats: 3.0.1(ajv@8.13.0) - fs-extra: 11.3.2 + fs-extra: 11.3.3 import-lazy: 4.0.0 jju: 1.4.0 - resolve: 1.22.10 + resolve: 1.22.11 semver: 7.5.4 optionalDependencies: - '@types/node': 24.5.2 + '@types/node': 25.0.6 - '@rushstack/rig-package@0.5.3': + '@rushstack/problem-matcher@0.1.1(@types/node@25.0.6)': + optionalDependencies: + '@types/node': 25.0.6 + + '@rushstack/rig-package@0.6.0': dependencies: - resolve: 1.22.10 + resolve: 1.22.11 strip-json-comments: 3.1.1 - '@rushstack/terminal@0.16.0(@types/node@24.5.2)': + '@rushstack/terminal@0.19.5(@types/node@25.0.6)': dependencies: - '@rushstack/node-core-library': 5.14.0(@types/node@24.5.2) + '@rushstack/node-core-library': 5.19.1(@types/node@25.0.6) + '@rushstack/problem-matcher': 0.1.1(@types/node@25.0.6) supports-color: 8.1.1 optionalDependencies: - '@types/node': 24.5.2 + '@types/node': 25.0.6 - '@rushstack/ts-command-line@5.0.3(@types/node@24.5.2)': + '@rushstack/ts-command-line@5.1.5(@types/node@25.0.6)': dependencies: - '@rushstack/terminal': 0.16.0(@types/node@24.5.2) + '@rushstack/terminal': 0.19.5(@types/node@25.0.6) '@types/argparse': 1.0.38 argparse: 1.0.10 string-argv: 0.3.2 transitivePeerDependencies: - '@types/node' - '@standard-schema/spec@1.0.0': {} + '@standard-schema/spec@1.1.0': {} '@standard-schema/utils@0.3.0': {} - '@swc/core-darwin-arm64@1.13.5': + '@swc/core-darwin-arm64@1.15.8': optional: true - '@swc/core-darwin-x64@1.13.5': + '@swc/core-darwin-x64@1.15.8': optional: true - '@swc/core-linux-arm-gnueabihf@1.13.5': + '@swc/core-linux-arm-gnueabihf@1.15.8': optional: true - '@swc/core-linux-arm64-gnu@1.13.5': + '@swc/core-linux-arm64-gnu@1.15.8': optional: true - '@swc/core-linux-arm64-musl@1.13.5': + '@swc/core-linux-arm64-musl@1.15.8': optional: true - '@swc/core-linux-x64-gnu@1.13.5': + '@swc/core-linux-x64-gnu@1.15.8': optional: true - '@swc/core-linux-x64-musl@1.13.5': + '@swc/core-linux-x64-musl@1.15.8': optional: true - '@swc/core-win32-arm64-msvc@1.13.5': + '@swc/core-win32-arm64-msvc@1.15.8': optional: true - '@swc/core-win32-ia32-msvc@1.13.5': + '@swc/core-win32-ia32-msvc@1.15.8': optional: true - '@swc/core-win32-x64-msvc@1.13.5': + '@swc/core-win32-x64-msvc@1.15.8': optional: true - '@swc/core@1.13.5(@swc/helpers@0.5.17)': + '@swc/core@1.15.8(@swc/helpers@0.5.17)': dependencies: '@swc/counter': 0.1.3 '@swc/types': 0.1.25 optionalDependencies: - '@swc/core-darwin-arm64': 1.13.5 - '@swc/core-darwin-x64': 1.13.5 - '@swc/core-linux-arm-gnueabihf': 1.13.5 - '@swc/core-linux-arm64-gnu': 1.13.5 - '@swc/core-linux-arm64-musl': 1.13.5 - '@swc/core-linux-x64-gnu': 1.13.5 - '@swc/core-linux-x64-musl': 1.13.5 - '@swc/core-win32-arm64-msvc': 1.13.5 - '@swc/core-win32-ia32-msvc': 1.13.5 - '@swc/core-win32-x64-msvc': 1.13.5 + '@swc/core-darwin-arm64': 1.15.8 + '@swc/core-darwin-x64': 1.15.8 + '@swc/core-linux-arm-gnueabihf': 1.15.8 + '@swc/core-linux-arm64-gnu': 1.15.8 + '@swc/core-linux-arm64-musl': 1.15.8 + '@swc/core-linux-x64-gnu': 1.15.8 + '@swc/core-linux-x64-musl': 1.15.8 + '@swc/core-win32-arm64-msvc': 1.15.8 + '@swc/core-win32-ia32-msvc': 1.15.8 + '@swc/core-win32-x64-msvc': 1.15.8 '@swc/helpers': 0.5.17 '@swc/counter@0.1.3': {} @@ -3045,7 +3251,7 @@ snapshots: lz-string: 1.5.0 pretty-format: 27.5.1 - '@testing-library/jest-dom@6.8.0': + '@testing-library/jest-dom@6.9.1': dependencies: '@adobe/css-tools': 4.4.4 aria-query: 5.3.2 @@ -3054,15 +3260,15 @@ snapshots: picocolors: 1.1.1 redent: 3.0.0 - '@testing-library/react@16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@testing-library/react@16.3.1(@testing-library/dom@10.4.0)(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@babel/runtime': 7.28.4 '@testing-library/dom': 10.4.0 - react: 19.1.1 - react-dom: 19.1.1(react@19.1.1) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) optionalDependencies: - '@types/react': 19.1.13 - '@types/react-dom': 19.1.9(@types/react@19.1.13) + '@types/react': 19.2.8 + '@types/react-dom': 19.2.3(@types/react@19.2.8) '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.0)': dependencies: @@ -3072,9 +3278,10 @@ snapshots: '@types/aria-query@5.0.4': {} - '@types/chai@5.2.2': + '@types/chai@5.2.3': dependencies: '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 '@types/d3-array@3.2.2': {} @@ -3108,221 +3315,216 @@ snapshots: '@types/luxon@3.7.1': {} - '@types/node@24.5.2': + '@types/node@25.0.6': dependencies: - undici-types: 7.12.0 + undici-types: 7.16.0 '@types/prop-types@15.7.15': {} - '@types/react-dom@19.1.9(@types/react@19.1.13)': + '@types/react-dom@19.2.3(@types/react@19.2.8)': dependencies: - '@types/react': 19.1.13 + '@types/react': 19.2.8 - '@types/react-transition-group@4.4.12(@types/react@19.1.13)': + '@types/react-transition-group@4.4.12(@types/react@19.2.8)': dependencies: - '@types/react': 19.1.13 + '@types/react': 19.2.8 - '@types/react@19.1.13': + '@types/react@19.2.8': dependencies: - csstype: 3.1.3 + csstype: 3.2.3 '@types/use-sync-external-store@0.0.6': {} '@types/warning@3.0.3': {} - '@typescript-eslint/eslint-plugin@8.44.0(@typescript-eslint/parser@8.44.0(eslint@9.36.0)(typescript@5.8.3))(eslint@9.36.0)(typescript@5.8.3)': + '@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)': dependencies: - '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.44.0(eslint@9.36.0)(typescript@5.8.3) - '@typescript-eslint/scope-manager': 8.44.0 - '@typescript-eslint/type-utils': 8.44.0(eslint@9.36.0)(typescript@5.8.3) - '@typescript-eslint/utils': 8.44.0(eslint@9.36.0)(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.44.0 - eslint: 9.36.0 - graphemer: 1.4.0 + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.52.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.52.0 + '@typescript-eslint/type-utils': 8.52.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/utils': 8.52.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.52.0 + eslint: 9.39.2 ignore: 7.0.5 natural-compare: 1.4.0 - ts-api-utils: 2.1.0(typescript@5.8.3) - typescript: 5.8.3 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.44.0(eslint@9.36.0)(typescript@5.8.3)': + '@typescript-eslint/parser@8.52.0(eslint@9.39.2)(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.44.0 - '@typescript-eslint/types': 8.44.0 - '@typescript-eslint/typescript-estree': 8.44.0(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.44.0 + '@typescript-eslint/scope-manager': 8.52.0 + '@typescript-eslint/types': 8.52.0 + '@typescript-eslint/typescript-estree': 8.52.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.52.0 debug: 4.4.3 - eslint: 9.36.0 - typescript: 5.8.3 + eslint: 9.39.2 + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.44.0(typescript@5.8.3)': + '@typescript-eslint/project-service@8.52.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.44.0(typescript@5.8.3) - '@typescript-eslint/types': 8.44.0 + '@typescript-eslint/tsconfig-utils': 8.52.0(typescript@5.9.3) + '@typescript-eslint/types': 8.52.0 debug: 4.4.3 - typescript: 5.8.3 + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.44.0': + '@typescript-eslint/scope-manager@8.52.0': dependencies: - '@typescript-eslint/types': 8.44.0 - '@typescript-eslint/visitor-keys': 8.44.0 + '@typescript-eslint/types': 8.52.0 + '@typescript-eslint/visitor-keys': 8.52.0 - '@typescript-eslint/tsconfig-utils@8.44.0(typescript@5.8.3)': + '@typescript-eslint/tsconfig-utils@8.52.0(typescript@5.9.3)': dependencies: - typescript: 5.8.3 + typescript: 5.9.3 - '@typescript-eslint/type-utils@8.44.0(eslint@9.36.0)(typescript@5.8.3)': + '@typescript-eslint/type-utils@8.52.0(eslint@9.39.2)(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.44.0 - '@typescript-eslint/typescript-estree': 8.44.0(typescript@5.8.3) - '@typescript-eslint/utils': 8.44.0(eslint@9.36.0)(typescript@5.8.3) + '@typescript-eslint/types': 8.52.0 + '@typescript-eslint/typescript-estree': 8.52.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.52.0(eslint@9.39.2)(typescript@5.9.3) debug: 4.4.3 - eslint: 9.36.0 - ts-api-utils: 2.1.0(typescript@5.8.3) - typescript: 5.8.3 + eslint: 9.39.2 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.44.0': {} + '@typescript-eslint/types@8.52.0': {} - '@typescript-eslint/typescript-estree@8.44.0(typescript@5.8.3)': + '@typescript-eslint/typescript-estree@8.52.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.44.0(typescript@5.8.3) - '@typescript-eslint/tsconfig-utils': 8.44.0(typescript@5.8.3) - '@typescript-eslint/types': 8.44.0 - '@typescript-eslint/visitor-keys': 8.44.0 + '@typescript-eslint/project-service': 8.52.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.52.0(typescript@5.9.3) + '@typescript-eslint/types': 8.52.0 + '@typescript-eslint/visitor-keys': 8.52.0 debug: 4.4.3 - fast-glob: 3.3.3 - is-glob: 4.0.3 minimatch: 9.0.5 - semver: 7.7.2 - ts-api-utils: 2.1.0(typescript@5.8.3) - typescript: 5.8.3 + semver: 7.7.3 + tinyglobby: 0.2.15 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.44.0(eslint@9.36.0)(typescript@5.8.3)': + '@typescript-eslint/utils@8.52.0(eslint@9.39.2)(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.36.0) - '@typescript-eslint/scope-manager': 8.44.0 - '@typescript-eslint/types': 8.44.0 - '@typescript-eslint/typescript-estree': 8.44.0(typescript@5.8.3) - eslint: 9.36.0 - typescript: 5.8.3 + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) + '@typescript-eslint/scope-manager': 8.52.0 + '@typescript-eslint/types': 8.52.0 + '@typescript-eslint/typescript-estree': 8.52.0(typescript@5.9.3) + eslint: 9.39.2 + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.44.0': + '@typescript-eslint/visitor-keys@8.52.0': dependencies: - '@typescript-eslint/types': 8.44.0 + '@typescript-eslint/types': 8.52.0 eslint-visitor-keys: 4.2.1 - '@uiw/react-json-view@2.0.0-alpha.38(@babel/runtime@7.28.4)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@uiw/react-json-view@2.0.0-alpha.40(@babel/runtime@7.28.4)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': dependencies: '@babel/runtime': 7.28.4 - react: 19.1.1 - react-dom: 19.1.1(react@19.1.1) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) - '@vitejs/plugin-react-swc@3.11.0(@swc/helpers@0.5.17)(vite@7.1.7(@types/node@24.5.2))': + '@vitejs/plugin-react-swc@4.2.2(@swc/helpers@0.5.17)(vite@7.3.1(@types/node@25.0.6))': dependencies: - '@rolldown/pluginutils': 1.0.0-beta.27 - '@swc/core': 1.13.5(@swc/helpers@0.5.17) - vite: 7.1.7(@types/node@24.5.2) + '@rolldown/pluginutils': 1.0.0-beta.47 + '@swc/core': 1.15.8(@swc/helpers@0.5.17) + vite: 7.3.1(@types/node@25.0.6) transitivePeerDependencies: - '@swc/helpers' - '@vitest/expect@3.2.4': + '@vitest/expect@4.0.16': dependencies: - '@types/chai': 5.2.2 - '@vitest/spy': 3.2.4 - '@vitest/utils': 3.2.4 - chai: 5.3.3 - tinyrainbow: 2.0.0 + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.0.16 + '@vitest/utils': 4.0.16 + chai: 6.2.2 + tinyrainbow: 3.0.3 - '@vitest/mocker@3.2.4(vite@7.1.7(@types/node@24.5.2))': + '@vitest/mocker@4.0.16(vite@7.3.1(@types/node@25.0.6))': dependencies: - '@vitest/spy': 3.2.4 + '@vitest/spy': 4.0.16 estree-walker: 3.0.3 - magic-string: 0.30.19 + magic-string: 0.30.21 optionalDependencies: - vite: 7.1.7(@types/node@24.5.2) + vite: 7.3.1(@types/node@25.0.6) - '@vitest/pretty-format@3.2.4': + '@vitest/pretty-format@4.0.16': dependencies: - tinyrainbow: 2.0.0 + tinyrainbow: 3.0.3 - '@vitest/runner@3.2.4': + '@vitest/runner@4.0.16': dependencies: - '@vitest/utils': 3.2.4 + '@vitest/utils': 4.0.16 pathe: 2.0.3 - strip-literal: 3.0.0 - '@vitest/snapshot@3.2.4': + '@vitest/snapshot@4.0.16': dependencies: - '@vitest/pretty-format': 3.2.4 - magic-string: 0.30.19 + '@vitest/pretty-format': 4.0.16 + magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@3.2.4': - dependencies: - tinyspy: 4.0.4 + '@vitest/spy@4.0.16': {} - '@vitest/utils@3.2.4': + '@vitest/utils@4.0.16': dependencies: - '@vitest/pretty-format': 3.2.4 - loupe: 3.2.1 - tinyrainbow: 2.0.0 + '@vitest/pretty-format': 4.0.16 + tinyrainbow: 3.0.3 - '@volar/language-core@2.4.23': + '@volar/language-core@2.4.27': dependencies: - '@volar/source-map': 2.4.23 + '@volar/source-map': 2.4.27 - '@volar/source-map@2.4.23': {} + '@volar/source-map@2.4.27': {} - '@volar/typescript@2.4.23': + '@volar/typescript@2.4.27': dependencies: - '@volar/language-core': 2.4.23 + '@volar/language-core': 2.4.27 path-browserify: 1.0.1 vscode-uri: 3.1.0 - '@vue/compiler-core@3.5.21': + '@vue/compiler-core@3.5.26': dependencies: - '@babel/parser': 7.28.4 - '@vue/shared': 3.5.21 - entities: 4.5.0 + '@babel/parser': 7.28.5 + '@vue/shared': 3.5.26 + entities: 7.0.0 estree-walker: 2.0.2 source-map-js: 1.2.1 - '@vue/compiler-dom@3.5.21': + '@vue/compiler-dom@3.5.26': dependencies: - '@vue/compiler-core': 3.5.21 - '@vue/shared': 3.5.21 + '@vue/compiler-core': 3.5.26 + '@vue/shared': 3.5.26 '@vue/compiler-vue2@2.7.16': dependencies: de-indent: 1.0.2 he: 1.2.0 - '@vue/language-core@2.2.0(typescript@5.8.3)': + '@vue/language-core@2.2.0(typescript@5.9.3)': dependencies: - '@volar/language-core': 2.4.23 - '@vue/compiler-dom': 3.5.21 + '@volar/language-core': 2.4.27 + '@vue/compiler-dom': 3.5.26 '@vue/compiler-vue2': 2.7.16 - '@vue/shared': 3.5.21 + '@vue/shared': 3.5.26 alien-signals: 0.4.14 minimatch: 9.0.5 muggle-string: 0.4.1 path-browserify: 1.0.1 optionalDependencies: - typescript: 5.8.3 + typescript: 5.9.3 - '@vue/shared@3.5.21': {} + '@vue/shared@3.5.26': {} acorn-jsx@5.3.2(acorn@8.15.0): dependencies: @@ -3393,7 +3595,7 @@ snapshots: call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 is-string: 1.1.1 @@ -3403,7 +3605,7 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-object-atoms: 1.1.1 es-shim-unscopables: 1.1.0 @@ -3412,21 +3614,21 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-shim-unscopables: 1.1.0 array.prototype.flatmap@1.3.3: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-shim-unscopables: 1.1.0 array.prototype.tosorted@1.1.4: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-shim-unscopables: 1.1.0 @@ -3435,7 +3637,7 @@ snapshots: array-buffer-byte-length: 1.0.2 call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 @@ -3450,16 +3652,22 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 - axios@1.12.2: + axios@1.13.2: dependencies: follow-redirects: 1.15.11 - form-data: 4.0.4 + form-data: 4.0.5 proxy-from-env: 1.1.0 transitivePeerDependencies: - debug balanced-match@1.0.2: {} + baseline-browser-mapping@2.9.14: {} + + bidi-js@1.0.3: + dependencies: + require-from-string: 2.0.2 + bootstrap@5.3.8(@popperjs/core@2.11.8): dependencies: '@popperjs/core': 2.11.8 @@ -3473,11 +3681,13 @@ snapshots: dependencies: balanced-match: 1.0.2 - braces@3.0.3: + browserslist@4.28.1: dependencies: - fill-range: 7.1.1 - - cac@6.7.14: {} + baseline-browser-mapping: 2.9.14 + caniuse-lite: 1.0.30001764 + electron-to-chromium: 1.5.267 + node-releases: 2.0.27 + update-browserslist-db: 1.2.3(browserslist@4.28.1) call-bind-apply-helpers@1.0.2: dependencies: @@ -3498,21 +3708,15 @@ snapshots: callsites@3.1.0: {} - chai@5.3.3: - dependencies: - assertion-error: 2.0.1 - check-error: 2.1.1 - deep-eql: 5.0.2 - loupe: 3.2.1 - pathval: 2.0.1 + caniuse-lite@1.0.30001764: {} + + chai@6.2.2: {} chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - check-error@2.1.1: {} - classnames@2.5.1: {} clsx@2.1.1: {} @@ -3535,25 +3739,36 @@ snapshots: confbox@0.2.2: {} + convert-source-map@2.0.0: {} + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 - crossroad@1.3.3(react@19.1.1): + crossroad@2.0.0(react@19.2.3): dependencies: - react: 19.1.1 + react: 19.2.3 + + css-tree@3.1.0: + dependencies: + mdn-data: 2.12.2 + source-map-js: 1.2.1 css.escape@1.5.1: {} - cssstyle@4.6.0: + cssstyle@5.3.7: dependencies: - '@asamuzakjp/css-color': 3.2.0 - rrweb-cssom: 0.8.0 + '@asamuzakjp/css-color': 4.1.1 + '@csstools/css-syntax-patches-for-csstree': 1.0.25 + css-tree: 3.1.0 + lru-cache: 11.2.4 csstype@3.1.3: {} + csstype@3.2.3: {} + d3-array@3.2.4: dependencies: internmap: 2.0.3 @@ -3592,10 +3807,10 @@ snapshots: d3-timer@3.0.1: {} - data-urls@5.0.0: + data-urls@6.0.0: dependencies: whatwg-mimetype: 4.0.0 - whatwg-url: 14.2.0 + whatwg-url: 15.1.0 data-view-buffer@1.0.2: dependencies: @@ -3625,8 +3840,6 @@ snapshots: decimal.js@10.6.0: {} - deep-eql@5.0.2: {} - deep-is@0.1.4: {} define-data-property@1.1.4: @@ -3645,6 +3858,8 @@ snapshots: dequal@2.0.3: {} + diff@8.0.2: {} + doctrine@2.1.0: dependencies: esutils: 2.0.3 @@ -3664,11 +3879,13 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 - entities@4.5.0: {} + electron-to-chromium@1.5.267: {} entities@6.0.1: {} - es-abstract@1.24.0: + entities@7.0.0: {} + + es-abstract@1.24.1: dependencies: array-buffer-byte-length: 1.0.2 arraybuffer.prototype.slice: 1.0.4 @@ -3729,12 +3946,12 @@ snapshots: es-errors@1.3.0: {} - es-iterator-helpers@1.2.1: + es-iterator-helpers@1.2.2: dependencies: call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-set-tostringtag: 2.1.0 function-bind: 1.1.2 @@ -3771,56 +3988,65 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 - es-toolkit@1.39.10: {} + es-toolkit@1.43.0: {} - esbuild@0.25.10: + esbuild@0.27.2: optionalDependencies: - '@esbuild/aix-ppc64': 0.25.10 - '@esbuild/android-arm': 0.25.10 - '@esbuild/android-arm64': 0.25.10 - '@esbuild/android-x64': 0.25.10 - '@esbuild/darwin-arm64': 0.25.10 - '@esbuild/darwin-x64': 0.25.10 - '@esbuild/freebsd-arm64': 0.25.10 - '@esbuild/freebsd-x64': 0.25.10 - '@esbuild/linux-arm': 0.25.10 - '@esbuild/linux-arm64': 0.25.10 - '@esbuild/linux-ia32': 0.25.10 - '@esbuild/linux-loong64': 0.25.10 - '@esbuild/linux-mips64el': 0.25.10 - '@esbuild/linux-ppc64': 0.25.10 - '@esbuild/linux-riscv64': 0.25.10 - '@esbuild/linux-s390x': 0.25.10 - '@esbuild/linux-x64': 0.25.10 - '@esbuild/netbsd-arm64': 0.25.10 - '@esbuild/netbsd-x64': 0.25.10 - '@esbuild/openbsd-arm64': 0.25.10 - '@esbuild/openbsd-x64': 0.25.10 - '@esbuild/openharmony-arm64': 0.25.10 - '@esbuild/sunos-x64': 0.25.10 - '@esbuild/win32-arm64': 0.25.10 - '@esbuild/win32-ia32': 0.25.10 - '@esbuild/win32-x64': 0.25.10 + '@esbuild/aix-ppc64': 0.27.2 + '@esbuild/android-arm': 0.27.2 + '@esbuild/android-arm64': 0.27.2 + '@esbuild/android-x64': 0.27.2 + '@esbuild/darwin-arm64': 0.27.2 + '@esbuild/darwin-x64': 0.27.2 + '@esbuild/freebsd-arm64': 0.27.2 + '@esbuild/freebsd-x64': 0.27.2 + '@esbuild/linux-arm': 0.27.2 + '@esbuild/linux-arm64': 0.27.2 + '@esbuild/linux-ia32': 0.27.2 + '@esbuild/linux-loong64': 0.27.2 + '@esbuild/linux-mips64el': 0.27.2 + '@esbuild/linux-ppc64': 0.27.2 + '@esbuild/linux-riscv64': 0.27.2 + '@esbuild/linux-s390x': 0.27.2 + '@esbuild/linux-x64': 0.27.2 + '@esbuild/netbsd-arm64': 0.27.2 + '@esbuild/netbsd-x64': 0.27.2 + '@esbuild/openbsd-arm64': 0.27.2 + '@esbuild/openbsd-x64': 0.27.2 + '@esbuild/openharmony-arm64': 0.27.2 + '@esbuild/sunos-x64': 0.27.2 + '@esbuild/win32-arm64': 0.27.2 + '@esbuild/win32-ia32': 0.27.2 + '@esbuild/win32-x64': 0.27.2 + + escalade@3.2.0: {} escape-string-regexp@4.0.0: {} - eslint-plugin-react-hooks@5.2.0(eslint@9.36.0): + eslint-plugin-react-hooks@7.0.1(eslint@9.39.2): dependencies: - eslint: 9.36.0 + '@babel/core': 7.28.5 + '@babel/parser': 7.28.5 + eslint: 9.39.2 + hermes-parser: 0.25.1 + zod: 4.3.5 + zod-validation-error: 4.0.2(zod@4.3.5) + transitivePeerDependencies: + - supports-color - eslint-plugin-react-refresh@0.4.20(eslint@9.36.0): + eslint-plugin-react-refresh@0.4.26(eslint@9.39.2): dependencies: - eslint: 9.36.0 + eslint: 9.39.2 - eslint-plugin-react@7.37.5(eslint@9.36.0): + eslint-plugin-react@7.37.5(eslint@9.39.2): dependencies: array-includes: 3.1.9 array.prototype.findlast: 1.2.5 array.prototype.flatmap: 1.3.3 array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 - es-iterator-helpers: 1.2.1 - eslint: 9.36.0 + es-iterator-helpers: 1.2.2 + eslint: 9.39.2 estraverse: 5.3.0 hasown: 2.0.2 jsx-ast-utils: 3.3.5 @@ -3843,21 +4069,20 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.36.0: + eslint@9.39.2: dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.36.0) - '@eslint-community/regexpp': 4.12.1 - '@eslint/config-array': 0.21.0 - '@eslint/config-helpers': 0.3.1 - '@eslint/core': 0.15.2 - '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.36.0 - '@eslint/plugin-kit': 0.3.5 + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.1 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.3 + '@eslint/js': 9.39.2 + '@eslint/plugin-kit': 0.4.1 '@humanfs/node': 0.16.7 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 '@types/estree': 1.0.8 - '@types/json-schema': 7.0.15 ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 @@ -3866,7 +4091,7 @@ snapshots: eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 espree: 10.4.0 - esquery: 1.6.0 + esquery: 1.7.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 file-entry-cache: 8.0.0 @@ -3889,7 +4114,7 @@ snapshots: acorn-jsx: 5.3.2(acorn@8.15.0) eslint-visitor-keys: 4.2.1 - esquery@1.6.0: + esquery@1.7.0: dependencies: estraverse: 5.3.0 @@ -3909,28 +4134,16 @@ snapshots: eventemitter3@5.0.1: {} - expect-type@1.2.2: {} + expect-type@1.3.0: {} - exsolve@1.0.7: {} + exsolve@1.0.8: {} fast-deep-equal@3.1.3: {} - fast-glob@3.3.3: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - fast-json-stable-stringify@2.1.0: {} fast-levenshtein@2.0.6: {} - fastq@1.19.1: - dependencies: - reusify: 1.1.0 - fdir@6.5.0(picomatch@4.0.3): optionalDependencies: picomatch: 4.0.3 @@ -3939,10 +4152,6 @@ snapshots: dependencies: flat-cache: 4.0.1 - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 - find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -3961,7 +4170,7 @@ snapshots: dependencies: is-callable: 1.2.7 - form-data@4.0.4: + form-data@4.0.5: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 @@ -3969,7 +4178,7 @@ snapshots: hasown: 2.0.2 mime-types: 2.1.35 - fs-extra@11.3.2: + fs-extra@11.3.3: dependencies: graceful-fs: 4.2.11 jsonfile: 6.2.0 @@ -3991,6 +4200,10 @@ snapshots: functions-have-names@1.2.3: {} + generator-function@2.0.1: {} + + gensync@1.0.0-beta.2: {} + get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -4015,17 +4228,13 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - glob-parent@6.0.2: dependencies: is-glob: 4.0.3 globals@14.0.0: {} - globals@16.4.0: {} + globals@17.0.0: {} globalthis@1.0.4: dependencies: @@ -4036,8 +4245,6 @@ snapshots: graceful-fs@4.2.11: {} - graphemer@1.4.0: {} - has-bigints@1.1.0: {} has-flag@4.0.0: {} @@ -4062,9 +4269,17 @@ snapshots: he@1.2.0: {} - html-encoding-sniffer@4.0.0: + hermes-estree@0.25.1: {} + + hermes-parser@0.25.1: + dependencies: + hermes-estree: 0.25.1 + + html-encoding-sniffer@6.0.0: dependencies: - whatwg-encoding: 3.1.1 + '@exodus/bytes': 1.8.0 + transitivePeerDependencies: + - '@exodus/crypto' http-proxy-agent@7.0.2: dependencies: @@ -4080,15 +4295,13 @@ snapshots: transitivePeerDependencies: - supports-color - iconv-lite@0.6.3: - dependencies: - safer-buffer: 2.1.2 - ignore@5.3.2: {} ignore@7.0.5: {} - immer@10.1.3: {} + immer@10.2.0: {} + + immer@11.1.3: {} import-fresh@3.3.1: dependencies: @@ -4159,9 +4372,10 @@ snapshots: dependencies: call-bound: 1.0.4 - is-generator-function@1.1.0: + is-generator-function@1.1.2: dependencies: call-bound: 1.0.4 + generator-function: 2.0.1 get-proto: 1.0.1 has-tostringtag: 1.0.2 safe-regex-test: 1.1.0 @@ -4179,8 +4393,6 @@ snapshots: call-bound: 1.0.4 has-tostringtag: 1.0.2 - is-number@7.0.0: {} - is-potential-custom-element-name@1.0.1: {} is-regex@1.2.1: @@ -4239,39 +4451,40 @@ snapshots: js-tokens@4.0.0: {} - js-tokens@9.0.1: {} - - js-yaml@4.1.0: + js-yaml@4.1.1: dependencies: argparse: 2.0.1 - jsdom@26.1.0: + jsdom@27.4.0: dependencies: - cssstyle: 4.6.0 - data-urls: 5.0.0 + '@acemir/cssom': 0.9.30 + '@asamuzakjp/dom-selector': 6.7.6 + '@exodus/bytes': 1.8.0 + cssstyle: 5.3.7 + data-urls: 6.0.0 decimal.js: 10.6.0 - html-encoding-sniffer: 4.0.0 + html-encoding-sniffer: 6.0.0 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.22 - parse5: 7.3.0 - rrweb-cssom: 0.8.0 + parse5: 8.0.0 saxes: 6.0.0 symbol-tree: 3.2.4 - tough-cookie: 5.1.2 + tough-cookie: 6.0.0 w3c-xmlserializer: 5.0.0 - webidl-conversions: 7.0.0 - whatwg-encoding: 3.1.1 + webidl-conversions: 8.0.1 whatwg-mimetype: 4.0.0 - whatwg-url: 14.2.0 - ws: 8.18.3 + whatwg-url: 15.1.0 + ws: 8.19.0 xml-name-validator: 5.0.0 transitivePeerDependencies: + - '@exodus/crypto' - bufferutil - supports-color - utf-8-validate + jsesc@3.1.0: {} + json-buffer@3.0.1: {} json-schema-traverse@0.4.1: {} @@ -4280,6 +4493,8 @@ snapshots: json-stable-stringify-without-jsonify@1.0.1: {} + json5@2.2.3: {} + jsonfile@6.2.0: dependencies: universalify: 2.0.1 @@ -4322,9 +4537,11 @@ snapshots: dependencies: js-tokens: 4.0.0 - loupe@3.2.1: {} + lru-cache@11.2.4: {} - lru-cache@10.4.3: {} + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 lru-cache@6.0.0: dependencies: @@ -4334,18 +4551,13 @@ snapshots: lz-string@1.5.0: {} - magic-string@0.30.19: + magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 math-intrinsics@1.1.0: {} - merge2@1.4.1: {} - - micromatch@4.0.8: - dependencies: - braces: 3.0.3 - picomatch: 2.3.1 + mdn-data@2.12.2: {} mime-db@1.52.0: {} @@ -4372,7 +4584,7 @@ snapshots: acorn: 8.15.0 pathe: 2.0.3 pkg-types: 1.3.1 - ufo: 1.6.1 + ufo: 1.6.2 ms@2.1.3: {} @@ -4382,7 +4594,7 @@ snapshots: natural-compare@1.4.0: {} - nwsapi@2.2.22: {} + node-releases@2.0.27: {} object-assign@4.1.1: {} @@ -4410,7 +4622,7 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-object-atoms: 1.1.1 object.values@1.2.1: @@ -4420,6 +4632,8 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + obug@2.1.1: {} + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -4447,7 +4661,7 @@ snapshots: dependencies: callsites: 3.1.0 - parse5@7.3.0: + parse5@8.0.0: dependencies: entities: 6.0.1 @@ -4461,12 +4675,8 @@ snapshots: pathe@2.0.3: {} - pathval@2.0.1: {} - picocolors@1.1.1: {} - picomatch@2.3.1: {} - picomatch@4.0.3: {} pkg-types@1.3.1: @@ -4478,7 +4688,7 @@ snapshots: pkg-types@2.3.0: dependencies: confbox: 0.2.2 - exsolve: 1.0.7 + exsolve: 1.0.8 pathe: 2.0.3 possible-typed-array-names@1.1.0: {} @@ -4497,9 +4707,9 @@ snapshots: ansi-styles: 5.2.0 react-is: 17.0.2 - prop-types-extra@1.1.1(react@19.1.1): + prop-types-extra@1.1.1(react@19.2.3): dependencies: - react: 19.1.1 + react: 19.2.3 react-is: 16.13.1 warning: 4.0.3 @@ -4515,37 +4725,35 @@ snapshots: quansync@0.2.11: {} - queue-microtask@1.2.3: {} - - react-bootstrap-icons@1.11.6(react@19.1.1): + react-bootstrap-icons@1.11.6(react@19.2.3): dependencies: prop-types: 15.8.1 - react: 19.1.1 + react: 19.2.3 - react-bootstrap@2.10.10(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + react-bootstrap@2.10.10(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: '@babel/runtime': 7.28.4 - '@restart/hooks': 0.4.16(react@19.1.1) - '@restart/ui': 1.9.4(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@restart/hooks': 0.4.16(react@19.2.3) + '@restart/ui': 1.9.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3) '@types/prop-types': 15.7.15 - '@types/react-transition-group': 4.4.12(@types/react@19.1.13) + '@types/react-transition-group': 4.4.12(@types/react@19.2.8) classnames: 2.5.1 dom-helpers: 5.2.1 invariant: 2.2.4 prop-types: 15.8.1 - prop-types-extra: 1.1.1(react@19.1.1) - react: 19.1.1 - react-dom: 19.1.1(react@19.1.1) - react-transition-group: 4.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - uncontrollable: 7.2.1(react@19.1.1) + prop-types-extra: 1.1.1(react@19.2.3) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) + react-transition-group: 4.4.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + uncontrollable: 7.2.1(react@19.2.3) warning: 4.0.3 optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.2.8 - react-dom@19.1.1(react@19.1.1): + react-dom@19.2.3(react@19.2.3): dependencies: - react: 19.1.1 - scheduler: 0.26.0 + react: 19.2.3 + scheduler: 0.27.0 react-is@16.13.1: {} @@ -4553,41 +4761,41 @@ snapshots: react-lifecycles-compat@3.0.4: {} - react-redux@9.2.0(@types/react@19.1.13)(react@19.1.1)(redux@5.0.1): + react-redux@9.2.0(@types/react@19.2.8)(react@19.2.3)(redux@5.0.1): dependencies: '@types/use-sync-external-store': 0.0.6 - react: 19.1.1 - use-sync-external-store: 1.5.0(react@19.1.1) + react: 19.2.3 + use-sync-external-store: 1.6.0(react@19.2.3) optionalDependencies: - '@types/react': 19.1.13 + '@types/react': 19.2.8 redux: 5.0.1 - react-transition-group@4.4.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + react-transition-group@4.4.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: '@babel/runtime': 7.28.4 dom-helpers: 5.2.1 loose-envify: 1.4.0 prop-types: 15.8.1 - react: 19.1.1 - react-dom: 19.1.1(react@19.1.1) + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) - react@19.1.1: {} + react@19.2.3: {} - recharts@3.2.1(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react-is@17.0.2)(react@19.1.1)(redux@5.0.1): + recharts@3.6.0(@types/react@19.2.8)(react-dom@19.2.3(react@19.2.3))(react-is@17.0.2)(react@19.2.3)(redux@5.0.1): dependencies: - '@reduxjs/toolkit': 2.9.0(react-redux@9.2.0(@types/react@19.1.13)(react@19.1.1)(redux@5.0.1))(react@19.1.1) + '@reduxjs/toolkit': 2.11.2(react-redux@9.2.0(@types/react@19.2.8)(react@19.2.3)(redux@5.0.1))(react@19.2.3) clsx: 2.1.1 decimal.js-light: 2.5.1 - es-toolkit: 1.39.10 + es-toolkit: 1.43.0 eventemitter3: 5.0.1 - immer: 10.1.3 - react: 19.1.1 - react-dom: 19.1.1(react@19.1.1) + immer: 10.2.0 + react: 19.2.3 + react-dom: 19.2.3(react@19.2.3) react-is: 17.0.2 - react-redux: 9.2.0(@types/react@19.1.13)(react@19.1.1)(redux@5.0.1) + react-redux: 9.2.0(@types/react@19.2.8)(react@19.2.3)(redux@5.0.1) reselect: 5.1.1 tiny-invariant: 1.3.3 - use-sync-external-store: 1.5.0(react@19.1.1) + use-sync-external-store: 1.6.0(react@19.2.3) victory-vendor: 37.3.6 transitivePeerDependencies: - '@types/react' @@ -4608,7 +4816,7 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 @@ -4630,7 +4838,7 @@ snapshots: resolve-from@4.0.0: {} - resolve@1.22.10: + resolve@1.22.11: dependencies: is-core-module: 2.16.1 path-parse: 1.0.7 @@ -4642,42 +4850,37 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - reusify@1.1.0: {} - - rollup@4.52.0: + rollup@4.55.1: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.52.0 - '@rollup/rollup-android-arm64': 4.52.0 - '@rollup/rollup-darwin-arm64': 4.52.0 - '@rollup/rollup-darwin-x64': 4.52.0 - '@rollup/rollup-freebsd-arm64': 4.52.0 - '@rollup/rollup-freebsd-x64': 4.52.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.52.0 - '@rollup/rollup-linux-arm-musleabihf': 4.52.0 - '@rollup/rollup-linux-arm64-gnu': 4.52.0 - '@rollup/rollup-linux-arm64-musl': 4.52.0 - '@rollup/rollup-linux-loong64-gnu': 4.52.0 - '@rollup/rollup-linux-ppc64-gnu': 4.52.0 - '@rollup/rollup-linux-riscv64-gnu': 4.52.0 - '@rollup/rollup-linux-riscv64-musl': 4.52.0 - '@rollup/rollup-linux-s390x-gnu': 4.52.0 - '@rollup/rollup-linux-x64-gnu': 4.52.0 - '@rollup/rollup-linux-x64-musl': 4.52.0 - '@rollup/rollup-openharmony-arm64': 4.52.0 - '@rollup/rollup-win32-arm64-msvc': 4.52.0 - '@rollup/rollup-win32-ia32-msvc': 4.52.0 - '@rollup/rollup-win32-x64-gnu': 4.52.0 - '@rollup/rollup-win32-x64-msvc': 4.52.0 + '@rollup/rollup-android-arm-eabi': 4.55.1 + '@rollup/rollup-android-arm64': 4.55.1 + '@rollup/rollup-darwin-arm64': 4.55.1 + '@rollup/rollup-darwin-x64': 4.55.1 + '@rollup/rollup-freebsd-arm64': 4.55.1 + '@rollup/rollup-freebsd-x64': 4.55.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.55.1 + '@rollup/rollup-linux-arm-musleabihf': 4.55.1 + '@rollup/rollup-linux-arm64-gnu': 4.55.1 + '@rollup/rollup-linux-arm64-musl': 4.55.1 + '@rollup/rollup-linux-loong64-gnu': 4.55.1 + '@rollup/rollup-linux-loong64-musl': 4.55.1 + '@rollup/rollup-linux-ppc64-gnu': 4.55.1 + '@rollup/rollup-linux-ppc64-musl': 4.55.1 + '@rollup/rollup-linux-riscv64-gnu': 4.55.1 + '@rollup/rollup-linux-riscv64-musl': 4.55.1 + '@rollup/rollup-linux-s390x-gnu': 4.55.1 + '@rollup/rollup-linux-x64-gnu': 4.55.1 + '@rollup/rollup-linux-x64-musl': 4.55.1 + '@rollup/rollup-openbsd-x64': 4.55.1 + '@rollup/rollup-openharmony-arm64': 4.55.1 + '@rollup/rollup-win32-arm64-msvc': 4.55.1 + '@rollup/rollup-win32-ia32-msvc': 4.55.1 + '@rollup/rollup-win32-x64-gnu': 4.55.1 + '@rollup/rollup-win32-x64-msvc': 4.55.1 fsevents: 2.3.3 - rrweb-cssom@0.8.0: {} - - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - safe-array-concat@1.1.3: dependencies: call-bind: 1.0.8 @@ -4697,13 +4900,11 @@ snapshots: es-errors: 1.3.0 is-regex: 1.2.1 - safer-buffer@2.1.2: {} - saxes@6.0.0: dependencies: xmlchars: 2.2.0 - scheduler@0.26.0: {} + scheduler@0.27.0: {} semver@6.3.1: {} @@ -4711,7 +4912,7 @@ snapshots: dependencies: lru-cache: 6.0.0 - semver@7.7.2: {} + semver@7.7.3: {} set-function-length@1.2.2: dependencies: @@ -4779,7 +4980,7 @@ snapshots: stackback@0.0.2: {} - std-env@3.9.0: {} + std-env@3.10.0: {} stop-iteration-iterator@1.1.0: dependencies: @@ -4793,7 +4994,7 @@ snapshots: call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 @@ -4807,7 +5008,7 @@ snapshots: string.prototype.repeat@1.0.0: dependencies: define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 string.prototype.trim@1.2.10: dependencies: @@ -4815,7 +5016,7 @@ snapshots: call-bound: 1.0.4 define-data-property: 1.1.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-object-atoms: 1.1.1 has-property-descriptors: 1.0.2 @@ -4838,10 +5039,6 @@ snapshots: strip-json-comments@3.1.1: {} - strip-literal@3.0.0: - dependencies: - js-tokens: 9.0.1 - supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -4858,40 +5055,32 @@ snapshots: tinybench@2.9.0: {} - tinyexec@0.3.2: {} + tinyexec@1.0.2: {} tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 - tinypool@1.1.1: {} - - tinyrainbow@2.0.0: {} - - tinyspy@4.0.4: {} - - tldts-core@6.1.86: {} + tinyrainbow@3.0.3: {} - tldts@6.1.86: - dependencies: - tldts-core: 6.1.86 + tldts-core@7.0.19: {} - to-regex-range@5.0.1: + tldts@7.0.19: dependencies: - is-number: 7.0.0 + tldts-core: 7.0.19 - tough-cookie@5.1.2: + tough-cookie@6.0.0: dependencies: - tldts: 6.1.86 + tldts: 7.0.19 - tr46@5.1.1: + tr46@6.0.0: dependencies: punycode: 2.3.1 - ts-api-utils@2.1.0(typescript@5.8.3): + ts-api-utils@2.4.0(typescript@5.9.3): dependencies: - typescript: 5.8.3 + typescript: 5.9.3 tslib@2.8.1: {} @@ -4932,22 +5121,22 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typescript-eslint@8.44.0(eslint@9.36.0)(typescript@5.8.3): + typescript-eslint@8.52.0(eslint@9.39.2)(typescript@5.9.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.44.0(@typescript-eslint/parser@8.44.0(eslint@9.36.0)(typescript@5.8.3))(eslint@9.36.0)(typescript@5.8.3) - '@typescript-eslint/parser': 8.44.0(eslint@9.36.0)(typescript@5.8.3) - '@typescript-eslint/typescript-estree': 8.44.0(typescript@5.8.3) - '@typescript-eslint/utils': 8.44.0(eslint@9.36.0)(typescript@5.8.3) - eslint: 9.36.0 - typescript: 5.8.3 + '@typescript-eslint/eslint-plugin': 8.52.0(@typescript-eslint/parser@8.52.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/parser': 8.52.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.52.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.52.0(eslint@9.39.2)(typescript@5.9.3) + eslint: 9.39.2 + typescript: 5.9.3 transitivePeerDependencies: - supports-color typescript@5.8.2: {} - typescript@5.8.3: {} + typescript@5.9.3: {} - ufo@1.6.1: {} + ufo@1.6.2: {} unbox-primitive@1.1.0: dependencies: @@ -4956,29 +5145,35 @@ snapshots: has-symbols: 1.1.0 which-boxed-primitive: 1.1.1 - uncontrollable@7.2.1(react@19.1.1): + uncontrollable@7.2.1(react@19.2.3): dependencies: '@babel/runtime': 7.28.4 - '@types/react': 19.1.13 + '@types/react': 19.2.8 invariant: 2.2.4 - react: 19.1.1 + react: 19.2.3 react-lifecycles-compat: 3.0.4 - uncontrollable@8.0.4(react@19.1.1): + uncontrollable@8.0.4(react@19.2.3): dependencies: - react: 19.1.1 + react: 19.2.3 - undici-types@7.12.0: {} + undici-types@7.16.0: {} universalify@2.0.1: {} + update-browserslist-db@1.2.3(browserslist@4.28.1): + dependencies: + browserslist: 4.28.1 + escalade: 3.2.0 + picocolors: 1.1.1 + uri-js@4.4.1: dependencies: punycode: 2.3.1 - use-sync-external-store@1.5.0(react@19.1.1): + use-sync-external-store@1.6.0(react@19.2.3): dependencies: - react: 19.1.1 + react: 19.2.3 victory-vendor@37.3.6: dependencies: @@ -4997,86 +5192,62 @@ snapshots: d3-time: 3.1.0 d3-timer: 3.0.1 - vite-node@3.2.4(@types/node@24.5.2): + vite-plugin-dts@4.5.4(@types/node@25.0.6)(rollup@4.55.1)(typescript@5.9.3)(vite@7.3.1(@types/node@25.0.6)): dependencies: - cac: 6.7.14 - debug: 4.4.3 - es-module-lexer: 1.7.0 - pathe: 2.0.3 - vite: 7.1.7(@types/node@24.5.2) - transitivePeerDependencies: - - '@types/node' - - jiti - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - - vite-plugin-dts@4.5.4(@types/node@24.5.2)(rollup@4.52.0)(typescript@5.8.3)(vite@7.1.7(@types/node@24.5.2)): - dependencies: - '@microsoft/api-extractor': 7.52.13(@types/node@24.5.2) - '@rollup/pluginutils': 5.3.0(rollup@4.52.0) - '@volar/typescript': 2.4.23 - '@vue/language-core': 2.2.0(typescript@5.8.3) + '@microsoft/api-extractor': 7.55.2(@types/node@25.0.6) + '@rollup/pluginutils': 5.3.0(rollup@4.55.1) + '@volar/typescript': 2.4.27 + '@vue/language-core': 2.2.0(typescript@5.9.3) compare-versions: 6.1.1 debug: 4.4.3 kolorist: 1.8.0 local-pkg: 1.1.2 - magic-string: 0.30.19 - typescript: 5.8.3 + magic-string: 0.30.21 + typescript: 5.9.3 optionalDependencies: - vite: 7.1.7(@types/node@24.5.2) + vite: 7.3.1(@types/node@25.0.6) transitivePeerDependencies: - '@types/node' - rollup - supports-color - vite@7.1.7(@types/node@24.5.2): + vite@7.3.1(@types/node@25.0.6): dependencies: - esbuild: 0.25.10 + esbuild: 0.27.2 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.52.0 + rollup: 4.55.1 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 24.5.2 + '@types/node': 25.0.6 fsevents: 2.3.3 - vitest@3.2.4(@types/node@24.5.2)(jsdom@26.1.0): - dependencies: - '@types/chai': 5.2.2 - '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.1.7(@types/node@24.5.2)) - '@vitest/pretty-format': 3.2.4 - '@vitest/runner': 3.2.4 - '@vitest/snapshot': 3.2.4 - '@vitest/spy': 3.2.4 - '@vitest/utils': 3.2.4 - chai: 5.3.3 - debug: 4.4.3 - expect-type: 1.2.2 - magic-string: 0.30.19 + vitest@4.0.16(@types/node@25.0.6)(jsdom@27.4.0): + dependencies: + '@vitest/expect': 4.0.16 + '@vitest/mocker': 4.0.16(vite@7.3.1(@types/node@25.0.6)) + '@vitest/pretty-format': 4.0.16 + '@vitest/runner': 4.0.16 + '@vitest/snapshot': 4.0.16 + '@vitest/spy': 4.0.16 + '@vitest/utils': 4.0.16 + es-module-lexer: 1.7.0 + expect-type: 1.3.0 + magic-string: 0.30.21 + obug: 2.1.1 pathe: 2.0.3 picomatch: 4.0.3 - std-env: 3.9.0 + std-env: 3.10.0 tinybench: 2.9.0 - tinyexec: 0.3.2 + tinyexec: 1.0.2 tinyglobby: 0.2.15 - tinypool: 1.1.1 - tinyrainbow: 2.0.0 - vite: 7.1.7(@types/node@24.5.2) - vite-node: 3.2.4(@types/node@24.5.2) + tinyrainbow: 3.0.3 + vite: 7.3.1(@types/node@25.0.6) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 24.5.2 - jsdom: 26.1.0 + '@types/node': 25.0.6 + jsdom: 27.4.0 transitivePeerDependencies: - jiti - less @@ -5086,7 +5257,6 @@ snapshots: - sass-embedded - stylus - sugarss - - supports-color - terser - tsx - yaml @@ -5101,18 +5271,14 @@ snapshots: dependencies: loose-envify: 1.4.0 - webidl-conversions@7.0.0: {} - - whatwg-encoding@3.1.1: - dependencies: - iconv-lite: 0.6.3 + webidl-conversions@8.0.1: {} whatwg-mimetype@4.0.0: {} - whatwg-url@14.2.0: + whatwg-url@15.1.0: dependencies: - tr46: 5.1.1 - webidl-conversions: 7.0.0 + tr46: 6.0.0 + webidl-conversions: 8.0.1 which-boxed-primitive@1.1.1: dependencies: @@ -5130,7 +5296,7 @@ snapshots: is-async-function: 2.1.1 is-date-object: 1.1.0 is-finalizationregistry: 1.1.1 - is-generator-function: 1.1.0 + is-generator-function: 1.1.2 is-regex: 1.2.1 is-weakref: 1.1.1 isarray: 2.0.5 @@ -5166,12 +5332,20 @@ snapshots: word-wrap@1.2.5: {} - ws@8.18.3: {} + ws@8.19.0: {} xml-name-validator@5.0.0: {} xmlchars@2.2.0: {} + yallist@3.1.1: {} + yallist@4.0.0: {} yocto-queue@0.1.0: {} + + zod-validation-error@4.0.2(zod@4.3.5): + dependencies: + zod: 4.3.5 + + zod@4.3.5: {} diff --git a/ui/pom.xml b/ui/pom.xml index 02925ca0b..30a6cd7d1 100644 --- a/ui/pom.xml +++ b/ui/pom.xml @@ -6,7 +6,7 @@ org.sterl.spring spring-persistent-tasks-root - 2.3.2-SNAPSHOT + 2.4.0-SNAPSHOT ../pom.xml diff --git a/ui/spt-ui-lib/eslint.config.js b/ui/spt-ui-lib/eslint.config.js index 40d5eb3f9..4ca41b076 100644 --- a/ui/spt-ui-lib/eslint.config.js +++ b/ui/spt-ui-lib/eslint.config.js @@ -3,23 +3,27 @@ import globals from "globals"; import reactHooks from "eslint-plugin-react-hooks"; import reactRefresh from "eslint-plugin-react-refresh"; import tseslint from "typescript-eslint"; -import { globalIgnores } from "eslint/config"; -export default tseslint.config([ - globalIgnores(["dist"]), +export default tseslint.config( + { ignores: ["dist"] }, + js.configs.recommended, + ...tseslint.configs.recommended, { files: ["**/*.{ts,tsx}"], - extends: [ - js.configs.recommended, - tseslint.configs.recommended, - reactHooks.configs["recommended-latest"], - reactRefresh.configs.vite, - ], languageOptions: { ecmaVersion: 2020, globals: globals.browser, }, + plugins: { + "react-hooks": reactHooks, + "react-refresh": reactRefresh, + }, rules: { + ...reactHooks.configs.recommended.rules, + "react-refresh/only-export-components": [ + "warn", + { allowConstantExport: true }, + ], "@typescript-eslint/no-unused-vars": [ "error", { @@ -34,4 +38,4 @@ export default tseslint.config([ ], }, }, -]); +); diff --git a/ui/spt-ui-lib/lib/server-api.ts b/ui/spt-ui-lib/lib/server-api.ts index 2eeb84805..479c93836 100644 --- a/ui/spt-ui-lib/lib/server-api.ts +++ b/ui/spt-ui-lib/lib/server-api.ts @@ -16,6 +16,16 @@ export interface SchedulerEntity { lastPing: string; } +export interface CronTriggerInfo { + id: string; + taskName: string; + schedule: string; + tag?: string; + priority: number; + suspended: boolean; + hasStateProvider: boolean; +} + export interface HistoryTrigger { id: number; instanceId: number; diff --git a/ui/spt-ui-lib/package.json b/ui/spt-ui-lib/package.json index d939847fe..c2a9c194c 100644 --- a/ui/spt-ui-lib/package.json +++ b/ui/spt-ui-lib/package.json @@ -37,33 +37,33 @@ "test": "vitest" }, "dependencies": { - "@uiw/react-json-view": "2.0.0-alpha.38", - "axios": "^1.12.2", + "@uiw/react-json-view": "2.0.0-alpha.40", + "axios": "^1.13.2", "bootstrap": "^5.3.8", "react-bootstrap": "^2.10.10", "react-bootstrap-icons": "^1.11.6" }, "peerDependencies": { - "react": "^19.1.0", - "react-dom": "^19.1.0" + "react": "^19.2.0", + "react-dom": "^19.2.0" }, "devDependencies": { - "@eslint/js": "^9.36.0", - "@testing-library/jest-dom": "^6.8.0", - "@testing-library/react": "^16.3.0", - "@types/node": "^24.5.2", - "@types/react": "^19.1.13", - "@types/react-dom": "^19.1.9", - "@vitejs/plugin-react-swc": "^3.11.0", - "eslint": "^9.36.0", - "eslint-plugin-react-hooks": "^5.2.0", - "eslint-plugin-react-refresh": "^0.4.20", - "globals": "^16.4.0", - "jsdom": "^26.1.0", - "typescript": "~5.8.3", - "typescript-eslint": "^8.44.0", - "vite": "^7.1.7", + "@eslint/js": "^9.39.2", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.1", + "@types/node": "^25.0.6", + "@types/react": "^19.2.8", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react-swc": "^4.2.2", + "eslint": "^9.39.2", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.4.26", + "globals": "^17.0.0", + "jsdom": "^27.4.0", + "typescript": "~5.9.3", + "typescript-eslint": "^8.52.0", + "vite": "^7.3.1", "vite-plugin-dts": "^4.5.4", - "vitest": "^3.2.4" + "vitest": "^4.0.16" } } diff --git a/ui/spt-ui-lib/vite.config.ts b/ui/spt-ui-lib/vite.config.ts index 6cb78c82d..a59ef65ce 100644 --- a/ui/spt-ui-lib/vite.config.ts +++ b/ui/spt-ui-lib/vite.config.ts @@ -1,6 +1,6 @@ /// // https://vite.dev/config/ -import { defineConfig } from "vite"; +import { defineConfig } from "vitest/config"; import react from "@vitejs/plugin-react-swc"; import { resolve } from "node:path"; import dts from "vite-plugin-dts"; diff --git a/ui/web-app/eslint.config.js b/ui/web-app/eslint.config.js index 0e72e345f..3d5355828 100644 --- a/ui/web-app/eslint.config.js +++ b/ui/web-app/eslint.config.js @@ -7,11 +7,9 @@ import react from "eslint-plugin-react"; export default tseslint.config( { ignores: ["dist"] }, + js.configs.recommended, + ...tseslint.configs.recommendedTypeChecked, { - extends: [ - js.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - ], files: ["**/*.{ts,tsx}"], languageOptions: { ecmaVersion: 2020, @@ -22,7 +20,7 @@ export default tseslint.config( }, }, // Set the react version - settings: { react: { version: "18.3" } }, + settings: { react: { version: "19.1" } }, plugins: { react: react, "react-hooks": reactHooks, diff --git a/ui/web-app/package.json b/ui/web-app/package.json index dc1ef71b4..8216de5b0 100644 --- a/ui/web-app/package.json +++ b/ui/web-app/package.json @@ -12,37 +12,37 @@ "preview": "vite preview" }, "dependencies": { - "@uiw/react-json-view": "2.0.0-alpha.38", - "axios": "^1.12.2", + "@uiw/react-json-view": "2.0.0-alpha.40", + "axios": "^1.13.2", "bootstrap": "^5.3.8", - "crossroad": "^1.3.3", + "crossroad": "^2.0.0", "luxon": "^3.7.2", - "react": "^19.1.1", + "react": "^19.2.3", "react-bootstrap": "^2.10.10", "react-bootstrap-icons": "^1.11.6", - "react-dom": "^19.1.1", - "recharts": "^3.2.1", + "react-dom": "^19.2.3", + "recharts": "^3.6.0", "spring-persistent-tasks-ui": "workspace:spt-ui-lib@*" }, "devDependencies": { - "@eslint/js": "^9.36.0", - "@testing-library/jest-dom": "^6.8.0", - "@testing-library/react": "^16.3.0", + "@eslint/js": "^9.39.2", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.1", "@testing-library/user-event": "^14.6.1", "@types/luxon": "^3.7.1", - "@types/node": "^24.5.2", - "@types/react": "^19.1.13", - "@types/react-dom": "^19.1.9", - "@vitejs/plugin-react-swc": "^3.11.0", - "eslint": "^9.36.0", + "@types/node": "^25.0.6", + "@types/react": "^19.2.8", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react-swc": "^4.2.2", + "eslint": "^9.39.2", "eslint-plugin-react": "^7.37.5", - "eslint-plugin-react-hooks": "^5.2.0", - "eslint-plugin-react-refresh": "^0.4.20", - "globals": "^16.4.0", - "jsdom": "^26.1.0", - "typescript": "~5.8.3", - "typescript-eslint": "^8.44.0", - "vite": "^7.1.7", - "vitest": "^3.2.4" + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.4.26", + "globals": "^17.0.0", + "jsdom": "^27.4.0", + "typescript": "~5.9.3", + "typescript-eslint": "^8.52.0", + "vite": "^7.3.1", + "vitest": "^4.0.16" } } diff --git a/ui/web-app/src/App.tsx b/ui/web-app/src/App.tsx index dce766137..92fc8a596 100644 --- a/ui/web-app/src/App.tsx +++ b/ui/web-app/src/App.tsx @@ -7,9 +7,11 @@ import * as Icon from "react-bootstrap-icons"; const SchedulersPage = lazy(() => import("./scheduler/scheduler.page")); const TriggersPage = lazy(() => import("./trigger/triggers.page")); const HistoryPage = lazy(() => import("./history/history.page")); +const CronJobsPage = lazy(() => import("./cron/cron.page")); const routes: MenuItem[] = [ { label: "Home", path: "", component: SchedulersPage }, + { label: "Cron Jobs", path: "/cron", component: CronJobsPage }, { label: "Triggers", path: "/triggers", component: TriggersPage }, { label: "History", path: "/history", component: HistoryPage }, ]; diff --git a/ui/web-app/src/cron/cron.page.tsx b/ui/web-app/src/cron/cron.page.tsx new file mode 100644 index 000000000..c9e191713 --- /dev/null +++ b/ui/web-app/src/cron/cron.page.tsx @@ -0,0 +1,7 @@ +import CronJobsView from "./views/cron-jobs.view"; + +const CronJobsPage = () => { + return ; +}; + +export default CronJobsPage; diff --git a/ui/web-app/src/cron/views/cron-job-card.view.tsx b/ui/web-app/src/cron/views/cron-job-card.view.tsx new file mode 100644 index 000000000..a22db694d --- /dev/null +++ b/ui/web-app/src/cron/views/cron-job-card.view.tsx @@ -0,0 +1,107 @@ +import { Card, Row, Col, Badge, Button, Stack } from "react-bootstrap"; +import { CronTriggerInfo } from "spring-persistent-tasks-ui"; +import * as Icon from "react-bootstrap-icons"; + +interface Props { + job: CronTriggerInfo; + isLoading: boolean; + onSuspendResume: (job: CronTriggerInfo) => void; + onViewTriggers: (taskName: string, id: string) => void; + onViewHistory: (taskName: string, id: string) => void; +} + +const CronJobCard = ({ + job, + isLoading, + onSuspendResume, + onViewTriggers, + onViewHistory, +}: Props) => { + return ( + + + + +

+ Cron ID +
{job.id}
+
+
+ Task Name +
{job.taskName}
+
+
+ Schedule +
+ {job.schedule} +
+
+ + +
+ Priority +
{job.priority}
+
+
+ Tag +
{job.tag || "—"}
+
+
+ Has State Provider +
+ {job.hasStateProvider ? ( + Yes + ) : ( + No + )} +
+
+ + + +
+ + + + + + + +
+ + + ); +}; + +export default CronJobCard; diff --git a/ui/web-app/src/cron/views/cron-jobs.view.tsx b/ui/web-app/src/cron/views/cron-jobs.view.tsx new file mode 100644 index 000000000..f90d9d728 --- /dev/null +++ b/ui/web-app/src/cron/views/cron-jobs.view.tsx @@ -0,0 +1,178 @@ +import { useEffect, useState } from "react"; +import { Container, Row, Col, Card, Badge, Stack, Form, Accordion } from "react-bootstrap"; +import { useServerObject, HttpErrorView, LoadingView, ReloadButton } from "spring-persistent-tasks-ui"; +import { CronTriggerInfo } from "spring-persistent-tasks-ui"; +import { useUrl } from "crossroad"; +import * as Icon from "react-bootstrap-icons"; +import CronJobCard from "./cron-job-card.view"; + +const CronJobsView = () => { + const [_, setUrl] = useUrl(); + const [searchText, setSearchText] = useState(""); + + const cronJobs = useServerObject("/spring-tasks-api/cron-triggers", []); + + useEffect(() => { + cronJobs.doGet(); + }, []); + + const doReload = () => { + cronJobs.doGet(); + }; + + // Filter cron jobs based on search text + const filteredJobs = (cronJobs.data || []).filter((job) => { + if (!searchText) return true; + const search = searchText.toLowerCase(); + return ( + job.id.toLowerCase().includes(search) || + job.taskName.toLowerCase().includes(search) || + job.schedule.toLowerCase().includes(search) || + (job.tag && job.tag.toLowerCase().includes(search)) + ); + }); + + const handleSuspendResume = async (job: CronTriggerInfo) => { + const endpoint = `/spring-tasks-api/cron-triggers/${job.taskName}/${job.id}`; + + try { + if (job.suspended) { + // Resume the cron job + await cronJobs.doCall(endpoint, { method: "POST" }); + } else { + // Suspend the cron job + await cronJobs.doCall(endpoint, { method: "DELETE" }); + } + // Reload the list after successful operation + doReload(); + } catch (error) { + console.error("Failed to suspend/resume cron job:", error); + } + }; + + const navigateToTriggers = (taskName: string, id: string) => { + setUrl(`/task-ui/triggers?search=${encodeURIComponent(id)}&taskName=${encodeURIComponent(taskName)}`); + }; + + const navigateToHistory = (taskName: string, id: string) => { + setUrl(`/task-ui/history?search=${encodeURIComponent(id)}&taskName=${encodeURIComponent(taskName)}`); + }; + + return ( + + + + + {/* Header */} + + +

+ + Cron Jobs +

+

+ Manage scheduled cron triggers. View configurations, suspend/resume jobs, and navigate to their triggers or history. +

+ +
+ + {/* Search and Actions */} + + + setSearchText(e.target.value)} + /> + + + + + {filteredJobs.length} / {cronJobs.data?.length || 0} crons + + + + + + + {/* Loading State */} + {cronJobs.isLoading && !cronJobs.data && } + + {/* Cron Jobs List */} + {!cronJobs.isLoading && filteredJobs.length === 0 && ( + + + +
No Cron Jobs Found
+

+ {searchText + ? "No cron jobs match your search criteria." + : "No cron jobs are currently configured."} +

+
+
+ )} + + {filteredJobs.length > 0 && ( + + {filteredJobs.map((job, index) => ( + + +
+
+
+ {job.id} + + {job.suspended ? ( + <> + + Suspended + + ) : ( + <> + + Active + + )} + + {job.tag && ( + + + {job.tag} + + )} +
+
+ + {job.schedule} + + + {job.taskName} + +
+
+
+
+ + + +
+ ))} +
+ )} +
+
+ ); +}; + +export default CronJobsView; diff --git a/ui/web-app/src/scheduler/views/scheduler.view.tsx b/ui/web-app/src/scheduler/views/scheduler.view.tsx index 2bd27476e..7db8e3e5c 100644 --- a/ui/web-app/src/scheduler/views/scheduler.view.tsx +++ b/ui/web-app/src/scheduler/views/scheduler.view.tsx @@ -75,12 +75,26 @@ const SchedulerStatusView = ({ scheduler }: Props) => { )}
- {renderLoadStatus( - "CPU", - scheduler.systemLoadAverage, - 100, - "cpu" + scheduler.id, - "%" + {scheduler.systemLoadAverage >= 0 ? ( + renderLoadStatus( + "CPU", + scheduler.systemLoadAverage, + 100, + "cpu" + scheduler.id, + "%" + ) + ) : ( +
+
+ CPU + N/A +
+
+
+ System load unavailable +
+
+
)}
diff --git a/ui/web-app/test/cron/cron.page.test.tsx b/ui/web-app/test/cron/cron.page.test.tsx new file mode 100644 index 000000000..8d8404699 --- /dev/null +++ b/ui/web-app/test/cron/cron.page.test.tsx @@ -0,0 +1,33 @@ +import CronJobsPage from "@src/cron/cron.page"; +import CronJobsView from "@src/cron/views/cron-jobs.view"; +import { render, screen } from "@testing-library/react"; +import { describe, expect, it } from "vitest"; +import Router from "crossroad"; + +describe("CronJobsPage Tests", () => { + it("renders page successfully", () => { + // GIVEN / WHEN + const { container } = render( + + + + ); + + // THEN + expect(container).toBeInTheDocument(); + }); + + it("renders view with expected elements", () => { + // GIVEN / WHEN + render( + + + + ); + + // THEN + expect(screen.getByText("Cron Jobs")).toBeInTheDocument(); + expect(screen.getByPlaceholderText("Search by ID, task name, schedule, or tag...")).toBeInTheDocument(); + expect(screen.getByText("Manage scheduled cron triggers. View configurations, suspend/resume jobs, and navigate to their triggers or history.")).toBeInTheDocument(); + }); +}); diff --git a/ui/web-app/test/scheduler/view/scheduler.view.test.tsx b/ui/web-app/test/scheduler/view/scheduler.view.test.tsx index accdffe35..af6e17a17 100644 --- a/ui/web-app/test/scheduler/view/scheduler.view.test.tsx +++ b/ui/web-app/test/scheduler/view/scheduler.view.test.tsx @@ -4,11 +4,11 @@ import { expect, it } from "vitest"; describe("SchedulerStatusView Tests", () => { beforeAll(() => { - const ResizeObserverMock = vi.fn(() => ({ - observe: vi.fn(), - unobserve: vi.fn(), - disconnect: vi.fn(), - })); + class ResizeObserverMock { + observe = vi.fn(); + unobserve = vi.fn(); + disconnect = vi.fn(); + } // Stub the global ResizeObserver vi.stubGlobal("ResizeObserver", ResizeObserverMock); diff --git a/ui/web-app/vite.config.ts b/ui/web-app/vite.config.ts index 9bfd1db51..37f571a16 100644 --- a/ui/web-app/vite.config.ts +++ b/ui/web-app/vite.config.ts @@ -1,5 +1,5 @@ /// -import { defineConfig } from "vite"; +import { defineConfig } from "vitest/config"; import react from "@vitejs/plugin-react-swc"; import path from "path"; @@ -25,13 +25,13 @@ export default defineConfig({ server: { proxy: { "/api": { - target: "http://localhost:8080", + target: "http://localhost:7070", changeOrigin: true, secure: false, ws: true, }, "/spring-tasks-api": { - target: "http://localhost:8080", + target: "http://localhost:7070", changeOrigin: true, secure: false, ws: true,