diff --git a/AGENTS.md b/AGENTS.md index 260b871e8..328e55d76 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,9 +1,19 @@ -This repository is a monorepo with 3 projects: +This repository is a monorepo with 4 projects: -- auth-server → Kotlin OAuth2/OIDC Authorization Server -- management-ui → React admin UI -- helm → Helm chart deployment +- auth-server -> Kotlin OAuth2/OIDC Authorization Server +- management-ui -> React/TypeScript admin UI +- config-manager -> Go service that exposes management-ui runtime configuration +- helm-charts -> Helm chart deployment assets -Always use the closest AGENTS.md file. +Always use the closest AGENTS.md file. Root instructions apply only when a subtree does not have its own guide or when the work is explicitly cross-project. -Never mix changes across projects unless explicitly requested. \ No newline at end of file +Project boundaries: + +- `auth-server`: backend authorization server, local auth-server assets, and auth-server docs +- `management-ui`: standalone React admin UI, nginx local serving config, and UI docs +- `config-manager`: Go runtime configuration API for the management UI +- `helm-charts`: Kubernetes/Helm packaging and chart documentation + +Never mix changes across projects unless explicitly requested. If a contract must change across projects, state that coupling clearly and update the related docs in the same change. + +Do not edit generated outputs by hand, including `auth-server/target`, `management-ui/dist`, `auth-server/dist`, dependency directories, or packaged Helm chart archives. diff --git a/README.md b/README.md index b4b27f816..65eb8f056 100644 --- a/README.md +++ b/README.md @@ -1,60 +1,92 @@ # VAuthenticator -This project is actually a journey. This is a big evolution of the OAuth2 authorization server -developed during my master thesis to an OpenID Connect authentication server. -In this new version I expand the initial project in order to use JWT token embracing OpenID Connect protocol, -all written in Kotlin based on Spring Boot 4.x and more over to the latest spring based oauth2.1/openid connect framework embedded in spring security 7+ +VAuthenticator is an OpenID Connect and OAuth2 authorization server ecosystem. The repository started from an OAuth2 authorization server built during a master thesis and now contains the backend, admin UI, runtime UI configuration service, and Helm deployment assets. -## The Architecture +## Projects + +| Project | Purpose | +| --- | --- | +| `auth-server` | Kotlin/Spring Boot authorization server with OAuth2, OIDC, MFA, account lifecycle, roles, keys, templates, and management APIs. | +| `management-ui` | Standalone React/TypeScript admin UI for managing clients, accounts, roles, keys, and email templates. | +| `config-manager` | Go service that exposes runtime configuration consumed by the management UI. | +| `helm-charts` | Helm chart and chart repository docs for Kubernetes deployment. | + +## Architecture ![](https://github.com/mrFlick72/vauthenticator/blob/main/images/vauthenticator-architecture.png) ## Features -Right now it is based, as said before to the latest version on spring oauth2/open id connect framework spring security 7+ -**API:** +Backend capabilities include: + +- OAuth2 Authorization Server and OpenID Connect support on Spring Boot 4.x and Spring Security 7+ +- JWT access token and ID token customization through Lambda integration +- Client application, role, account, and key management APIs +- Signup, welcome email, email verification, password reset, and password change flows +- MFA with email, SMS, and OTP support +- Post-login workflows, including forced password reset +- RP-initiated logout and OIDC Session Management +- Custom actuator management endpoints for setup and cleanup + +Storage and infrastructure options include: + +- DynamoDB or PostgreSQL persistence profiles +- Redis for authorization code storage, distributed session storage, and cache +- AWS KMS-backed keys or local/plain Java key management +- S3 or filesystem-backed document/static asset loading + +## Local Development + +Add these hostnames to your local hosts file: + +```text +127.0.0.1 local.api.vauthenticator.com +127.0.0.1 local.management.vauthenticator.com +127.0.0.1 local.ui-config-manager.vauthenticator.com +``` -- Client Applications management -- roles management -- account management -- sign up: admin:signup scope is required -- welcome mail: admin:welcome scope is required -- email verification: admin:mail-verify scope is required -- reset password: admin:reset-password scope is required -- access_token/id_token customization via lambda, see [here](auth-server/docs/lambda.md) for more details -- MFA - - mail - - sms - - see [here](auth-server/docs/mfa.md) for more details -- Post login flow - - force to reset password -- OIDC RP-initiated logout and OIDC Session Management -- management api: custom actuator endpoint for more details [look here](auth-server/docs/management.md) +Start the auth-server dependencies and tenant setup from the auth-server local docs: -**Storage:** +- [auth-server/local/readme.md](auth-server/local/readme.md) -- DynamoDB -- Redis: - - authorization code - - distributed session store - - distributed cache -- RSA key pair are created from KMS Customer Master Key stored on Dynamo, private key encrypted via KMS of course stored on Dynamo. +Run the configuration service used by the management UI: +```bash +cd config-manager +export CONFIG_MANAGER_ENV_FILE=.env.example +make run +``` -### auth server local environment +Build and serve the management UI locally: -For more details please follow to this link [readme.md](auth-server/local/readme.md) +```bash +cd management-ui +bash build.sh +docker compose -f local/docker-compose.yml up +``` +The local UI is served from: -### management ui local environment +- `http://local.management.vauthenticator.com:8085/secure/admin/index` -In order to build the application, you can use the build.sh script under managament-ui folder. -The ```docker-compose.yml``` file under the local folder cna be used to sin up a nginx exposed to the port 8085 +The nginx config in `management-ui/local` proxies `GET /api/config` to `config-manager` on `host.docker.internal:8086` and forwards the `Host` header as `local.ui-config-manager.vauthenticator.com`. -To access to the application you can use the following link: **http://local.management.vauthenticator.com:8085/secure/admin/index**, please take care to have the local ip mapped to the ```local.management.vauthenticator.com``` in the host file. +## Build And Test +| Project | Commands | +| --- | --- | +| `auth-server` | `./mvnw test`, `./mvnw package`, `./mvnw spring-boot:run` | +| `auth-server/src/main/frontend` | `npm install`, `npm run build`, `npm run production-build`, `npm run watch` | +| `management-ui/src` | `npm install`, `npm run build`, `npm run production-build`, `npm run watch` | +| `management-ui` | `bash build.sh` | +| `config-manager` | `make test`, `make run`, `make build`, `make tidy` | -### profiling +## Documentation -The application configuration is very versatile and you can decide what persistence and key management provider to use AWS or not AWS native. -For more details please refer to the detailed page [here](auth-server/docs/profiles.md) +- [Auth server profiles](auth-server/docs/profiles.md) +- [Auth server MFA](auth-server/docs/mfa.md) +- [Auth server Lambda token customization](auth-server/docs/lambda.md) +- [Auth server management endpoints](auth-server/docs/management.md) +- [Management UI agent guide](management-ui/AGENTS.md) +- [Config manager README](config-manager/README.md) +- [Helm chart README](helm-charts/README.md) diff --git a/auth-server/AGENTS.md b/auth-server/AGENTS.md index d5f7f439b..3afe6f201 100644 --- a/auth-server/AGENTS.md +++ b/auth-server/AGENTS.md @@ -6,7 +6,7 @@ This repository contains the VAuthenticator authorization server. It is a Spring The main application entry point is [src/main/kotlin/com/vauthenticator/server/VAuthenticatorApplication.kt](src/main/kotlin/com/vauthenticator/server/VAuthenticatorApplication.kt). -Use this guide for any work under `auth-server`. Per the repo root instructions, this file takes precedence over the monorepo-level `Agents.md` for this subtree. +Use this guide for any work under `auth-server`. Per the repo root instructions, this file takes precedence over the monorepo-level `AGENTS.md` for this subtree. The relevant skills if available, for working in this repository include: $kotlin-patterns $kotlin-specialist $kotlin-springboot $kotlin-testing @@ -168,6 +168,5 @@ Lambda-based token customization is supported when `vauthenticator.lambda.aws.en ## Practical Notes For Future Agents -- The git worktree was clean when this guide was created. - `application.yml` at the root resource level is intentionally minimal, so expect most behavior to come from profile-specific or component-specific configuration classes and local overrides. - The local helper scripts and docs are important in this project because a large part of the runtime setup lives outside the default Spring Boot process. diff --git a/auth-server/src/main/kotlin/com/vauthenticator/server/oidc/sessionmanagement/AuthorizeSessionState.kt b/auth-server/src/main/kotlin/com/vauthenticator/server/oidc/sessionmanagement/AuthorizeSessionState.kt index 6a348c8d8..46b3d7e3c 100644 --- a/auth-server/src/main/kotlin/com/vauthenticator/server/oidc/sessionmanagement/AuthorizeSessionState.kt +++ b/auth-server/src/main/kotlin/com/vauthenticator/server/oidc/sessionmanagement/AuthorizeSessionState.kt @@ -8,7 +8,6 @@ import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.beans.factory.annotation.Value import org.springframework.data.redis.core.RedisTemplate -import org.springframework.http.ResponseEntity import org.springframework.security.core.Authentication import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationToken @@ -18,11 +17,9 @@ import org.springframework.stereotype.Controller import org.springframework.ui.Model import org.springframework.util.StringUtils import org.springframework.web.bind.annotation.GetMapping -import org.springframework.web.bind.annotation.RequestParam -import org.springframework.web.bind.annotation.RestController import org.springframework.web.util.UriComponentsBuilder +import java.util.* import java.util.Arrays.stream -import java.util.UUID const val OPBS_COOKIE_NAME = "opbs" const val OPBS_SESSION_ATTRIBUTE = "opbs_session_value" @@ -119,20 +116,4 @@ class SessionManagementIFrameController( return "session/management" } -} - -@RestController -class CheckSessionEndPoint(private val redisTemplate: RedisTemplate) { - - @GetMapping("/check_session") - fun checkSession(@RequestParam state: String): ResponseEntity { - val sessionState = redisTemplate.opsForHash().get(state, state.toSha256()) - ?: return ResponseEntity.notFound().build() - - return ResponseEntity.ok(CheckSessionResponse(sessionState)) - } - -} - - -data class CheckSessionResponse(val state: String?) +} \ No newline at end of file diff --git a/management-ui/environments/.env.development b/config-manager/.env.example similarity index 57% rename from management-ui/environments/.env.development rename to config-manager/.env.example index a069b4770..01c10a30b 100644 --- a/management-ui/environments/.env.development +++ b/config-manager/.env.example @@ -1,5 +1,10 @@ -REDIRECT_URI="http://local.management.vauthenticator.com:8085/callback" -CLIENT_APPLICATION_ID="vauthenticator-management-ui" +SERVER_ADDRESS=":8086" + +MANAGEMENT_UI_SERVER_URL="http://local.management.vauthenticator.com:8085" + IDP_BASE_URL="http://local.api.vauthenticator.com:9090" +CLIENT_APPLICATION_ID="vauthenticator-management-ui" +REDIRECT_URI="http://local.management.vauthenticator.com:8085/callback" AUTHENTICATION_CHECK_INTERVAL="15000" -API_BASE_URL="http://local.api.vauthenticator.com:9090/api" \ No newline at end of file +API_BASE_URL="http://local.api.vauthenticator.com:9090/api" + diff --git a/config-manager/.gitignore b/config-manager/.gitignore new file mode 100644 index 000000000..38a67633b --- /dev/null +++ b/config-manager/.gitignore @@ -0,0 +1,4 @@ +/bin/ +config-manager +/.env +/coverage.out diff --git a/config-manager/AGENTS.md b/config-manager/AGENTS.md new file mode 100644 index 000000000..f8be94e86 --- /dev/null +++ b/config-manager/AGENTS.md @@ -0,0 +1,91 @@ +# VAuthenticator Config Manager Agent Guide + +## Purpose + +`config-manager` is a small Go service that exposes runtime configuration for the standalone VAuthenticator management UI. + +Use this guide for any work under `config-manager`. Per the repo root instructions, this file takes precedence over the monorepo-level `AGENTS.md` for this subtree. + +The relevant skills if available for this project include: $golang-documentation $golang-patterns $golang-testing + +## Stack + +- Go 1.25 +- Gin HTTP router +- Viper configuration loading +- `gin-contrib/cors` for CORS +- Standard `net/http` server with graceful shutdown + +## Repository Layout + +- `cmd/config-manager`: executable entry point +- `internal/api`: Gin router and HTTP endpoint tests +- `internal/config`: environment and `.env` file loading, parsing, and validation +- `.env.example`: local configuration example +- `Makefile`: build, run, test, and tidy shortcuts + +## Runtime Contract + +The service exposes one unauthenticated endpoint: + +- `GET /api/config` + +It returns the configuration consumed by `management-ui/src/config/ConfigLoader.ts`: + +- `idpBaseUrl` +- `clientApplicationId` +- `redirectUri` +- `authenticationCheckInterval` +- `apiBaseUrl` + +The management UI caches this response in `sessionStorage` under `appConfig` and clears it during logout. + +## Configuration + +Configuration is read from environment variables with Viper. If `CONFIG_MANAGER_ENV_FILE` is set, that file is required. Otherwise `.env` is used when present and direct shell environment variables still win. + +Required values: + +- `MANAGEMENT_UI_SERVER_URL` +- `IDP_BASE_URL` +- `CLIENT_APPLICATION_ID` +- `REDIRECT_URI` +- `AUTHENTICATION_CHECK_INTERVAL` +- `API_BASE_URL` + +Optional value: + +- `SERVER_ADDRESS` defaults to `:8086` + +`MANAGEMENT_UI_SERVER_URL` is the allowed CORS origin. Keep this aligned with the management UI host. + +## Build And Test Commands + +- `make test` +- `make run` +- `make build` +- `make tidy` + +Equivalent direct commands: + +- `go test ./...` +- `go run ./cmd/config-manager` +- `go build -o bin/config-manager ./cmd/config-manager` +- `go mod tidy` + +## Conventions For Changes + +- Keep the executable entry point thin; put HTTP behavior in `internal/api` and configuration rules in `internal/config`. +- Keep the `/api/config` response shape aligned with `management-ui/src/config/ConfigLoader.ts`. +- If configuration fields are added, update `.env.example`, `README.md`, tests, and the management UI contract docs together. +- Keep CORS explicit. Do not replace the configured management UI origin with a wildcard without an explicit security decision. +- Do not add authentication to `/api/config` unless the management UI bootstrap flow is changed at the same time. +- Prefer table-driven tests for config parsing and HTTP handler behavior. + +## Files Worth Reading First + +- `cmd/config-manager/main.go` +- `internal/api/router.go` +- `internal/config/config.go` +- `README.md` +- `.env.example` diff --git a/config-manager/Makefile b/config-manager/Makefile new file mode 100644 index 000000000..1d2557343 --- /dev/null +++ b/config-manager/Makefile @@ -0,0 +1,14 @@ +.PHONY: build run test tidy + +build: + go build -o bin/config-manager ./cmd/config-manager + +run: + go run ./cmd/config-manager + +test: + go test ./... + +tidy: + go mod tidy + diff --git a/config-manager/README.md b/config-manager/README.md new file mode 100644 index 000000000..07df60f35 --- /dev/null +++ b/config-manager/README.md @@ -0,0 +1,81 @@ +# config-manager + +`config-manager` exposes the external runtime configuration consumed by the VAuthenticator management UI. + +The service is intentionally small: it reads environment-backed configuration with Viper, validates the fields required by the UI, enables CORS for the configured management UI origin, and serves `GET /api/config`. + +## Stack + +- Go 1.25 +- Gin +- Viper +- `gin-contrib/cors` + +## Configuration + +Configuration is read from environment variables. Set `CONFIG_MANAGER_ENV_FILE` to point the service at a specific `.env` file. If it is not set, a local `.env` file is used when present. Environment variables exported directly in the shell override values from the file. + +```env +SERVER_ADDRESS=":8086" + +MANAGEMENT_UI_SERVER_URL="http://local.management.vauthenticator.com:8085" + +IDP_BASE_URL="http://local.api.vauthenticator.com:9090" +CLIENT_APPLICATION_ID="vauthenticator-management-ui" +REDIRECT_URI="http://local.management.vauthenticator.com:8085/callback" +AUTHENTICATION_CHECK_INTERVAL="15000" +API_BASE_URL="http://local.api.vauthenticator.com:9090/api" +``` + +`MANAGEMENT_UI_SERVER_URL` is used as the allowed CORS origin. Keep it aligned with the host that serves the management UI. + +## Run + +```bash +export CONFIG_MANAGER_ENV_FILE=.env.example +make run +``` + +For local development, you can still use `cp .env.example .env` and then `make run` without exporting `CONFIG_MANAGER_ENV_FILE`. + +## Build And Test + +```bash +make test +make build +make tidy +``` + +The direct Go equivalents are: + +```bash +go test ./... +go build -o bin/config-manager ./cmd/config-manager +go mod tidy +``` + +## API + +### `GET /api/config` + +Returns the management UI configuration. No authentication is required. + +```json +{ + "idpBaseUrl": "http://local.api.vauthenticator.com:9090", + "clientApplicationId": "vauthenticator-management-ui", + "redirectUri": "http://local.management.vauthenticator.com:8085/callback", + "authenticationCheckInterval": "15000", + "apiBaseUrl": "http://local.api.vauthenticator.com:9090/api" +} +``` + +The management UI caches this response in `sessionStorage` under the `appConfig` key and clears it during logout. + +## Project Layout + +- `cmd/config-manager`: executable entry point and graceful shutdown +- `internal/api`: Gin router and `/api/config` endpoint +- `internal/config`: environment loading, parsing, defaults, and validation +- `.env.example`: local configuration example +- `Makefile`: development commands diff --git a/config-manager/cmd/config-manager/main.go b/config-manager/cmd/config-manager/main.go new file mode 100644 index 000000000..9dd056554 --- /dev/null +++ b/config-manager/cmd/config-manager/main.go @@ -0,0 +1,50 @@ +package main + +import ( + "context" + "errors" + "log" + "net/http" + "os/signal" + "syscall" + "time" + + "github.com/mrFlick72/vauthenticator/config-manager/internal/api" + "github.com/mrFlick72/vauthenticator/config-manager/internal/config" +) + +func main() { + cfg, err := config.Load() + if err != nil { + log.Fatalf("load configuration: %v", err) + } + + server := &http.Server{ + Addr: cfg.ServerAddress, + Handler: api.NewRouter(cfg), + ReadHeaderTimeout: 5 * time.Second, + } + + ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) + defer stop() + + errCh := make(chan error, 1) + go func() { + log.Printf("config-manager listening on %s", cfg.ServerAddress) + errCh <- server.ListenAndServe() + }() + + select { + case <-ctx.Done(): + shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + if err := server.Shutdown(shutdownCtx); err != nil { + log.Fatalf("shutdown server: %v", err) + } + case err := <-errCh: + if err != nil && !errors.Is(err, http.ErrServerClosed) { + log.Fatalf("run server: %v", err) + } + } +} diff --git a/config-manager/go.mod b/config-manager/go.mod new file mode 100644 index 000000000..5cb7115fc --- /dev/null +++ b/config-manager/go.mod @@ -0,0 +1,50 @@ +module github.com/mrFlick72/vauthenticator/config-manager + +go 1.25.0 + +require ( + github.com/gin-contrib/cors v1.7.7 + github.com/gin-gonic/gin v1.12.0 + github.com/spf13/viper v1.21.0 +) + +require ( + github.com/bytedance/gopkg v0.1.3 // indirect + github.com/bytedance/sonic v1.15.0 // indirect + github.com/bytedance/sonic/loader v0.5.0 // indirect + github.com/cloudwego/base64x v0.1.6 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.12 // indirect + github.com/gin-contrib/sse v1.1.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.30.1 // indirect + github.com/go-viper/mapstructure/v2 v2.4.0 // indirect + github.com/goccy/go-json v0.10.5 // indirect + github.com/goccy/go-yaml v1.19.2 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.3.0 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/quic-go/qpack v0.6.0 // indirect + github.com/quic-go/quic-go v0.59.0 // indirect + github.com/sagikazarmark/locafero v0.11.0 // indirect + github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect + github.com/spf13/afero v1.15.0 // indirect + github.com/spf13/cast v1.10.0 // indirect + github.com/spf13/pflag v1.0.10 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.3.1 // indirect + go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/arch v0.23.0 // indirect + golang.org/x/crypto v0.48.0 // indirect + golang.org/x/net v0.51.0 // indirect + golang.org/x/sys v0.41.0 // indirect + golang.org/x/text v0.35.0 // indirect + google.golang.org/protobuf v1.36.10 // indirect +) diff --git a/config-manager/go.sum b/config-manager/go.sum new file mode 100644 index 000000000..bc1b03175 --- /dev/null +++ b/config-manager/go.sum @@ -0,0 +1,121 @@ +github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= +github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= +github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE= +github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k= +github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE= +github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= +github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= +github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw= +github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= +github.com/gin-contrib/cors v1.7.7 h1:Oh9joP463x7Mw72vhvJ61YQm8ODh9b04YR7vsOErD0Q= +github.com/gin-contrib/cors v1.7.7/go.mod h1:K5tW0RkzJtWSiOdikXloy8VEZlgdVNpHNw8FpjUPNrE= +github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= +github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= +github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8= +github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w= +github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM= +github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= +github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= +github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= +github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= +github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= +github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw= +github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= +github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= +github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= +github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= +github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= +github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= +github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY= +github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= +go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE= +go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0= +go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= +go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg= +golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= +golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= +golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= +golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= +golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= +golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= +golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/config-manager/internal/api/router.go b/config-manager/internal/api/router.go new file mode 100644 index 000000000..561079d50 --- /dev/null +++ b/config-manager/internal/api/router.go @@ -0,0 +1,27 @@ +package api + +import ( + "net/http" + "time" + + "github.com/gin-contrib/cors" + "github.com/gin-gonic/gin" + "github.com/mrFlick72/vauthenticator/config-manager/internal/config" +) + +func NewRouter(cfg config.Config) *gin.Engine { + router := gin.New() + router.Use(gin.Recovery()) + router.Use(cors.New(cors.Config{ + AllowOrigins: []string{cfg.ManagementUIServerURL}, + AllowMethods: []string{http.MethodGet, http.MethodOptions}, + AllowHeaders: []string{"Accept", "Content-Type", "Origin"}, + MaxAge: 12 * time.Hour, + })) + + router.GET("/api/config", func(ctx *gin.Context) { + ctx.JSON(http.StatusOK, cfg.Application) + }) + + return router +} diff --git a/config-manager/internal/api/router_test.go b/config-manager/internal/api/router_test.go new file mode 100644 index 000000000..4148d0099 --- /dev/null +++ b/config-manager/internal/api/router_test.go @@ -0,0 +1,77 @@ +package api + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/gin-gonic/gin" + "github.com/mrFlick72/vauthenticator/config-manager/internal/config" +) + +func TestConfigEndpointReturnsApplicationConfig(t *testing.T) { + gin.SetMode(gin.TestMode) + + cfg := testConfig() + router := NewRouter(cfg) + + req := httptest.NewRequest(http.MethodGet, "/api/config", nil) + req.Header.Set("Origin", cfg.ManagementUIServerURL) + + rec := httptest.NewRecorder() + router.ServeHTTP(rec, req) + + if rec.Code != http.StatusOK { + t.Fatalf("status = %d; want %d", rec.Code, http.StatusOK) + } + + if got := rec.Header().Get("Access-Control-Allow-Origin"); got != cfg.ManagementUIServerURL { + t.Fatalf("Access-Control-Allow-Origin = %q; want %q", got, cfg.ManagementUIServerURL) + } + + var got config.ApplicationConfig + if err := json.Unmarshal(rec.Body.Bytes(), &got); err != nil { + t.Fatalf("decode response: %v", err) + } + + if got != cfg.Application { + t.Fatalf("response = %+v; want %+v", got, cfg.Application) + } +} + +func TestConfigEndpointAllowsCorsPreflight(t *testing.T) { + gin.SetMode(gin.TestMode) + + cfg := testConfig() + router := NewRouter(cfg) + + req := httptest.NewRequest(http.MethodOptions, "/api/config", nil) + req.Header.Set("Origin", cfg.ManagementUIServerURL) + req.Header.Set("Access-Control-Request-Method", http.MethodGet) + + rec := httptest.NewRecorder() + router.ServeHTTP(rec, req) + + if rec.Code != http.StatusNoContent { + t.Fatalf("status = %d; want %d", rec.Code, http.StatusNoContent) + } + + if got := rec.Header().Get("Access-Control-Allow-Origin"); got != cfg.ManagementUIServerURL { + t.Fatalf("Access-Control-Allow-Origin = %q; want %q", got, cfg.ManagementUIServerURL) + } +} + +func testConfig() config.Config { + return config.Config{ + ServerAddress: ":8086", + ManagementUIServerURL: "http://local.management.vauthenticator.com:8085", + Application: config.ApplicationConfig{ + IDPBaseURL: "http://local.api.vauthenticator.com:9090", + ClientApplicationID: "vauthenticator-management-ui", + RedirectURI: "http://local.management.vauthenticator.com:8085/callback", + AuthenticationCheckInterval: "15000", + APIBaseURL: "http://local.api.vauthenticator.com:9090/api", + }, + } +} diff --git a/config-manager/internal/config/config.go b/config-manager/internal/config/config.go new file mode 100644 index 000000000..abe8320c2 --- /dev/null +++ b/config-manager/internal/config/config.go @@ -0,0 +1,122 @@ +package config + +import ( + "errors" + "fmt" + "os" + "strings" + + "github.com/spf13/viper" +) + +const ( + envConfigFile = "CONFIG_MANAGER_ENV_FILE" + envServerAddress = "SERVER_ADDRESS" + envManagementUIServerURL = "MANAGEMENT_UI_SERVER_URL" + envIDPBaseURL = "IDP_BASE_URL" + envClientApplicationID = "CLIENT_APPLICATION_ID" + envRedirectURI = "REDIRECT_URI" + envAuthenticationCheckInterval = "AUTHENTICATION_CHECK_INTERVAL" + envAPIBaseURL = "API_BASE_URL" +) + +const defaultConfigFile = ".env" + +type Config struct { + ServerAddress string + ManagementUIServerURL string + Application ApplicationConfig +} + +type ApplicationConfig struct { + IDPBaseURL string `json:"idpBaseUrl"` + ClientApplicationID string `json:"clientApplicationId"` + RedirectURI string `json:"redirectUri"` + AuthenticationCheckInterval string `json:"authenticationCheckInterval"` + APIBaseURL string `json:"apiBaseUrl"` +} + +func Load() (Config, error) { + configFile, configured := configFilePath() + + v := viper.New() + v.SetConfigFile(configFile) + v.SetConfigType("env") + v.SetDefault(envServerAddress, ":8086") + v.AutomaticEnv() + + if err := v.ReadInConfig(); err != nil { + if configured { + return Config{}, fmt.Errorf("read config file from %s %q: %w", envConfigFile, configFile, err) + } + + var notFound viper.ConfigFileNotFoundError + if !errors.As(err, ¬Found) && !os.IsNotExist(err) { + return Config{}, fmt.Errorf("read config file %q: %w", configFile, err) + } + } + + return Parse(v) +} + +func configFilePath() (path string, configured bool) { + path = strings.TrimSpace(os.Getenv(envConfigFile)) + if path == "" { + return defaultConfigFile, false + } + + return path, true +} + +func Parse(v *viper.Viper) (Config, error) { + cfg := Config{ + ServerAddress: strings.TrimSpace(v.GetString(envServerAddress)), + ManagementUIServerURL: strings.TrimSpace(v.GetString(envManagementUIServerURL)), + Application: ApplicationConfig{ + IDPBaseURL: strings.TrimSpace(v.GetString(envIDPBaseURL)), + ClientApplicationID: strings.TrimSpace(v.GetString(envClientApplicationID)), + RedirectURI: strings.TrimSpace(v.GetString(envRedirectURI)), + AuthenticationCheckInterval: strings.TrimSpace(v.GetString(envAuthenticationCheckInterval)), + APIBaseURL: strings.TrimSpace(v.GetString(envAPIBaseURL)), + }, + } + + if cfg.ServerAddress == "" { + cfg.ServerAddress = ":8086" + } + + if err := cfg.Validate(); err != nil { + return Config{}, err + } + + return cfg, nil +} + +func (c Config) Validate() error { + missing := make([]string, 0) + + if c.ManagementUIServerURL == "" { + missing = append(missing, envManagementUIServerURL) + } + if c.Application.IDPBaseURL == "" { + missing = append(missing, envIDPBaseURL) + } + if c.Application.ClientApplicationID == "" { + missing = append(missing, envClientApplicationID) + } + if c.Application.RedirectURI == "" { + missing = append(missing, envRedirectURI) + } + if c.Application.AuthenticationCheckInterval == "" { + missing = append(missing, envAuthenticationCheckInterval) + } + if c.Application.APIBaseURL == "" { + missing = append(missing, envAPIBaseURL) + } + + if len(missing) > 0 { + return fmt.Errorf("missing required configuration: %s", strings.Join(missing, ", ")) + } + + return nil +} diff --git a/config-manager/internal/config/config_test.go b/config-manager/internal/config/config_test.go new file mode 100644 index 000000000..3a9772ec9 --- /dev/null +++ b/config-manager/internal/config/config_test.go @@ -0,0 +1,178 @@ +package config + +import ( + "os" + "path/filepath" + "strings" + "testing" + + "github.com/spf13/viper" +) + +func TestParseMapsEnvironmentConfiguration(t *testing.T) { + v := viper.New() + v.Set(envServerAddress, ":8090") + v.Set(envManagementUIServerURL, " http://local.management.vauthenticator.com:8085 ") + v.Set(envIDPBaseURL, " http://local.api.vauthenticator.com:9090 ") + v.Set(envClientApplicationID, " vauthenticator-management-ui ") + v.Set(envRedirectURI, " http://local.management.vauthenticator.com:8085/callback ") + v.Set(envAuthenticationCheckInterval, " 15000 ") + v.Set(envAPIBaseURL, " http://local.api.vauthenticator.com:9090/api ") + + cfg, err := Parse(v) + if err != nil { + t.Fatalf("Parse() error = %v", err) + } + + if cfg.ServerAddress != ":8090" { + t.Fatalf("ServerAddress = %q; want %q", cfg.ServerAddress, ":8090") + } + if cfg.ManagementUIServerURL != "http://local.management.vauthenticator.com:8085" { + t.Fatalf("ManagementUIServerURL = %q", cfg.ManagementUIServerURL) + } + + want := ApplicationConfig{ + IDPBaseURL: "http://local.api.vauthenticator.com:9090", + ClientApplicationID: "vauthenticator-management-ui", + RedirectURI: "http://local.management.vauthenticator.com:8085/callback", + AuthenticationCheckInterval: "15000", + APIBaseURL: "http://local.api.vauthenticator.com:9090/api", + } + if cfg.Application != want { + t.Fatalf("Application = %+v; want %+v", cfg.Application, want) + } +} + +func TestParseDefaultsServerAddress(t *testing.T) { + v := viper.New() + v.Set(envManagementUIServerURL, "http://local.management.vauthenticator.com:8085") + v.Set(envIDPBaseURL, "http://local.api.vauthenticator.com:9090") + v.Set(envClientApplicationID, "vauthenticator-management-ui") + v.Set(envRedirectURI, "http://local.management.vauthenticator.com:8085/callback") + v.Set(envAuthenticationCheckInterval, "15000") + v.Set(envAPIBaseURL, "http://local.api.vauthenticator.com:9090/api") + + cfg, err := Parse(v) + if err != nil { + t.Fatalf("Parse() error = %v", err) + } + + if cfg.ServerAddress != ":8086" { + t.Fatalf("ServerAddress = %q; want %q", cfg.ServerAddress, ":8086") + } +} + +func TestParseRejectsMissingRequiredConfiguration(t *testing.T) { + _, err := Parse(viper.New()) + if err == nil { + t.Fatal("Parse() error = nil; want missing configuration error") + } + + msg := err.Error() + for _, name := range []string{ + envManagementUIServerURL, + envIDPBaseURL, + envClientApplicationID, + envRedirectURI, + envAuthenticationCheckInterval, + envAPIBaseURL, + } { + if !strings.Contains(msg, name) { + t.Fatalf("error %q does not include missing key %s", msg, name) + } + } +} + +func TestLoadReadsEnvironmentVariablesWithoutEnvFile(t *testing.T) { + t.Chdir(t.TempDir()) + setRequiredEnv(t) + + cfg, err := Load() + if err != nil { + t.Fatalf("Load() error = %v", err) + } + + if cfg.Application.ClientApplicationID != "vauthenticator-management-ui" { + t.Fatalf("ClientApplicationID = %q", cfg.Application.ClientApplicationID) + } +} + +func TestLoadReadsEnvFile(t *testing.T) { + dir := t.TempDir() + t.Chdir(dir) + + if err := os.WriteFile(filepath.Join(dir, ".env"), []byte(testEnvFile()), 0o600); err != nil { + t.Fatalf("write .env: %v", err) + } + + cfg, err := Load() + if err != nil { + t.Fatalf("Load() error = %v", err) + } + + if cfg.ManagementUIServerURL != "http://local.management.vauthenticator.com:8085" { + t.Fatalf("ManagementUIServerURL = %q", cfg.ManagementUIServerURL) + } +} + +func TestLoadReadsConfiguredEnvFile(t *testing.T) { + dir := t.TempDir() + t.Chdir(dir) + + configPath := filepath.Join(dir, "config", "config-manager.env") + if err := os.MkdirAll(filepath.Dir(configPath), 0o700); err != nil { + t.Fatalf("create config directory: %v", err) + } + if err := os.WriteFile(configPath, []byte(testEnvFile()), 0o600); err != nil { + t.Fatalf("write configured env file: %v", err) + } + t.Setenv(envConfigFile, configPath) + + cfg, err := Load() + if err != nil { + t.Fatalf("Load() error = %v", err) + } + + if cfg.Application.APIBaseURL != "http://local.api.vauthenticator.com:9090/api" { + t.Fatalf("APIBaseURL = %q", cfg.Application.APIBaseURL) + } +} + +func TestLoadFailsWhenConfiguredEnvFileIsMissing(t *testing.T) { + dir := t.TempDir() + t.Chdir(dir) + t.Setenv(envConfigFile, filepath.Join(dir, "missing.env")) + setRequiredEnv(t) + + _, err := Load() + if err == nil { + t.Fatal("Load() error = nil; want configured file read error") + } + + if !strings.Contains(err.Error(), envConfigFile) && !strings.Contains(err.Error(), "missing.env") { + t.Fatalf("error %q does not mention configured file", err) + } +} + +func setRequiredEnv(t *testing.T) { + t.Helper() + + t.Setenv(envManagementUIServerURL, "http://local.management.vauthenticator.com:8085") + t.Setenv(envIDPBaseURL, "http://local.api.vauthenticator.com:9090") + t.Setenv(envClientApplicationID, "vauthenticator-management-ui") + t.Setenv(envRedirectURI, "http://local.management.vauthenticator.com:8085/callback") + t.Setenv(envAuthenticationCheckInterval, "15000") + t.Setenv(envAPIBaseURL, "http://local.api.vauthenticator.com:9090/api") +} + +func testEnvFile() string { + return strings.Join([]string{ + `MANAGEMENT_UI_SERVER_URL="http://local.management.vauthenticator.com:8085"`, + `IDP_BASE_URL="http://local.api.vauthenticator.com:9090"`, + `CLIENT_APPLICATION_ID="vauthenticator-management-ui"`, + `REDIRECT_URI="http://local.management.vauthenticator.com:8085/callback"`, + `AUTHENTICATION_CHECK_INTERVAL="15000"`, + `API_BASE_URL="http://local.api.vauthenticator.com:9090/api"`, + "", + }, "\n") +} diff --git a/helm-charts/README.md b/helm-charts/README.md index 369fa7252..e95dcf897 100644 --- a/helm-charts/README.md +++ b/helm-charts/README.md @@ -1,24 +1,40 @@ -# VAuthenticator Helm Chart +# VAuthenticator Helm Charts -It is the official helm chart for VAuthenticator ecosystem. +This directory contains the Helm chart repository assets for the VAuthenticator ecosystem. -# Usage +The current chart deploys: -Helm must be installed to use the charts. Please refer to Helm's documentation to get started. -Once Helm is set up properly, add the repo as follows: +- VAuthenticator authorization server (`application`) +- VAuthenticator management UI workload (`managementUi`) +- optional in-namespace Redis dependency from Bitnami -```helm repo add vauthenticator https://vauthenticator.github.io/helm-charts``` +`config-manager` is a separate Go project in this monorepo and is not currently rendered by the chart. -You can then run helm search repo vauthenticator to see the charts. +## Usage -> In order to use the repo run the dependency update command in order to update all the needed dependencies with this -command -> -> ```helm dependency update vauthenticator``` +Helm must be installed to use the charts. Once Helm is available, add the chart repository: -if you want to use redis in kubernetes you can configure the helm charts to use a redis installation in the same -namespace -The embedded installation is the same coming from bitnami and for further redis configuration you can see on -the [bitnami github repository](https://github.com/bitnami/charts/tree/main/bitnami/redis/) +```bash +helm repo add vauthenticator https://vauthenticator.github.io/helm-charts +helm repo update +helm search repo vauthenticator +``` -Helm Chart documentation details can be reached on [this link](charts/README.md) \ No newline at end of file +For local chart development from this repository: + +```bash +cd helm-charts +helm dependency update charts/vauthenticator +helm lint charts/vauthenticator --set application.ingress.host=localhost --set managementUi.ingress.host=localhost +helm template vauthenticator charts/vauthenticator --set application.ingress.host=localhost --set managementUi.ingress.host=localhost +``` + +## Redis + +The chart can install Redis in the same namespace when `in-namespace.redis.enabled=true`. The dependency is the Bitnami Redis chart. For advanced Redis settings, refer to the Bitnami chart documentation: + +- https://github.com/bitnami/charts/tree/main/bitnami/redis + +## Documentation + +Detailed chart values are documented in [charts/README.md](charts/README.md). diff --git a/helm-charts/charts/README.md b/helm-charts/charts/README.md index 41d112161..7dd59c6bc 100644 --- a/helm-charts/charts/README.md +++ b/helm-charts/charts/README.md @@ -1,42 +1,46 @@ -# VAuthenticator Helm Chart +# VAuthenticator Chart Values -It is the official helm chart for VAuthenticator ecosystem. +This document describes the values for the `charts/vauthenticator` Helm chart. -# Global Components +The chart currently renders: -We discuss how configure global and general available properties apart from that AWS and Redis section, -Deployment, pod, lables and selectors section are applied on -`application`, `managementUi` and root field. -To make it simple we cover configuration without root but keep in mind that those generic stuff are available for all -application root tag +- `application`: the VAuthenticator authorization server +- `managementUi`: the management UI workload configured by the chart +- optional Redis dependency when `in-namespace.redis.enabled=true` -## Redis +`config-manager` is not currently part of this chart. -It is possible to enable the installation via helm of a namespace scoped redis. -In order to enable it have a look to the yaml snippet below and refers to the -official [bitnami details](https://github.com/bitnami/charts/tree/main/bitnami/redis/) +## Development Commands -#### yaml section +Run these commands from `helm-charts`: + +```bash +helm dependency update charts/vauthenticator +helm lint charts/vauthenticator --set application.ingress.host=localhost --set managementUi.ingress.host=localhost +helm template vauthenticator charts/vauthenticator --set application.ingress.host=localhost --set managementUi.ingress.host=localhost +``` + +## Redis Dependency ```yaml in-namespace: redis: enabled: true +redis: + auth: + enabled: false + replica: + replicaCount: 1 ``` -#### Properties description - -| Name | Description | Value | -|----------------------------|-------------------------------------|-------| -| in-namespace.redis.enabled | if install a redis in k8s namespace | true | +| Name | Description | Default | +| --- | --- | --- | +| `in-namespace.redis.enabled` | Install the Bitnami Redis dependency in the release namespace. | `true` | +| `redis.*` | Values passed to the Bitnami Redis subchart. | See `values.yaml` | ## AWS -VAuthenticator is designed with AWS and kubernetes in mind. In order to configure AWS credentials have a look below: - -#### yaml section - ```yaml aws: region: xxxxxxxxx @@ -49,63 +53,50 @@ aws: enabled: false iamRole: arn: arn:aws:iam::ACCOUNT_ID:role/IAM_ROLE_NAME - ``` -#### Properties description +| Name | Description | Default | +| --- | --- | --- | +| `aws.region` | AWS region. | `xxxxxxxxx` | +| `aws.iamUser.enabled` | Use explicit AWS access key and secret key environment variables. Useful outside EKS. | `false` | +| `aws.iamUser.accessKey` | AWS access key when `aws.iamUser.enabled=true`. | `xxxxxxxxx` | +| `aws.iamUser.secretKey` | AWS secret key when `aws.iamUser.enabled=true`. | `xxxxxxxxx` | +| `aws.eks.serviceAccount.enabled` | Use a Kubernetes service account intended for EKS IAM role integration. | `false` | +| `aws.eks.serviceAccount.iamRole.arn` | IAM role ARN used by the service account template. | `arn:aws:iam::ACCOUNT_ID:role/IAM_ROLE_NAME` | -| Name | Description | Value | -|------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------| -| aws.region | AWS Region like eu-central-1 | xxxxxxxxx | -| aws.iamUser.accessKey | AWS IAM User Access Key Id | xxxxxxxxx | -| aws.iamUser.secretKey | AWS IAM User Access Key secret | xxxxxxxxx | -| aws.iamUser.enabled | Identify that we want use a dedicated AWS IAM user. This strategy is required if VAuthenticator is deployed on premise | false | -| aws.eks.serviceAccount.enabled | Identify that we want use a dedicated Service Account linked to an IAM identity Provider.
This strategy is the best practice if VAuthenticator is deployed on AWS EKS | false | -| aws.eks.serviceAccount.iamRole.arn | It is the arn of the role used from STS to gain permissions | arn:aws:iam::ACCOUNT_ID:role/IAM_ROLE_NAME | +## Common Workload Values -## KEDA (application scoped) +These groups exist under both `application` and `managementUi` unless noted. -In order to apply autoscaling in VAuthenticator authorization server is possible configure [keda](https://keda.sh/) with -prometheus metrics. - -#### yaml section +### KEDA ```yaml -application: - keda: - enabled: false - spec: - minReplicaCount: 1 - maxReplicaCount: 1 - pollingInterval: 1 - cooldownPeriod: 300 - prometheus: - serverAddress: "" - metricName: "" - threshold: "" - query: "" +keda: + enabled: false + spec: + minReplicaCount: 1 + maxReplicaCount: 1 + pollingInterval: 1 + cooldownPeriod: 300 + prometheus: + serverAddress: "" + metricName: "" + threshold: "" + query: "" ``` -#### Properties description +| Name | Description | Default | +| --- | --- | --- | +| `*.keda.enabled` | Render a KEDA `ScaledObject`. | `false` | +| `*.keda.spec.minReplicaCount` | Minimum replica count. | `1` | +| `*.keda.spec.maxReplicaCount` | Maximum replica count. | `1` | +| `*.keda.spec.pollingInterval` | Metric polling interval. | `1` | +| `*.keda.spec.cooldownPeriod` | Cooldown period after scaling. | `300` | +| `*.keda.prometheus.*` | Prometheus trigger settings. | empty | -| Name | Description | Value | -|-------------------------------------------|-----------------------------------------------------------------------------------------------------------------------|-------| -| application.keda.enabled | define if the keda autoscaling is enabled | false | -| application.keda.spec.minReplicaCount | minReplicaCount autoscaling parameter | 1 | -| application.keda.spec.maxReplicaCount | maxReplicaCount autoscaling parameter | 1 | -| application.keda.spec.pollingInterval | polling Interval of metric evaluation in order to decide if apply autoscaling or not | 1 | -| application.keda.spec.cooldownPeriod | specifies how long any alarm-triggered scaling action will be disallowed after a previous scaling action is complete. | 300 | -| application.keda.prometheus.serverAddress | prometheus address | "" | -| application.keda.prometheus.metricName | prometheus metric name | "" | -| application.keda.prometheus.threshold | prometheus metric threshold to apply for autoscaling | "" | -| application.keda.prometheus.query | prometheus query to evaluate autoscaling | "" | - -## POD (application scoped) - -#### yaml section +### Pod, Service, Ingress, Image, Resources ```yaml - pod: probes: liveness: @@ -114,45 +105,16 @@ pod: rediness: initialDelaySeconds: 10 periodSeconds: 30 -``` -#### Properties description - -| Name | Description | Value | -|-----------------------------------------|------------------------------------------------------------------------------------------------|-------| -| pod.probes.liveness.initialDelaySeconds | define for liveness probe the initial delay in seconds to wait to start to evaluate the probes | 10 | -| pod.probes.liveness.periodSeconds | define for liveness probe period in seconds to wait for the next evaluation | 30 | -| pod.probes.rediness.initialDelaySeconds | define for rediness probe the initial delay in seconds to wait to start to evaluate the probes | 10 | -| pod.probes.rediness.periodSeconds | define for rediness probe period in seconds to wait for the next evaluation | 30 | - -## Ingress (application scoped) - -#### yaml section +service: + type: ClusterIP -```yaml ingress: host: "*" - annotations: { } - tls: { } + annotations: {} + tls: {} enabled: true class: nginx -``` - -#### Properties description - -| Name | Description | Value | -|---------------------|---------------------------------------------------------------------------------|-------| -| ingress.host | define on what host the ingress resource should be configured to answer | * | -| ingress.annotations | define annotations for the ingress resource | { } | -| ingress.tls | define tls configuration for the ingress resource | { } | -| ingress.enabled | define if the ingress should be included in the resources | true | -| ingress.class | define what kind of ingress class should be configured for teh ingress resource | nginx | - -## Pod Resources (application scoped) - -#### yaml section - -```yaml resources: requests: @@ -162,77 +124,84 @@ resources: cpu: "512m" memory: "512Mi" -``` - -#### Properties description - -| Name | Description | Value | -|---------------------------|-------------------------------------------|---------| -| resources.requests.cpu | usual cpu kubernetes request parameter | "256m" | -| resources.requests.memory | usual memory kubernetes request parameter | "256Mi" | -| resources.limits.cpu | usual cpu kubernetes limits parameter | "512m" | -| resources.limits.memory | usual memory kubernetes limits parameter | "512Mi" | - -## Image (application scoped) - -#### yaml section - -```yaml +replicaCount: 1 image: repository: mrflick72/vauthenticator-k8s pullPolicy: Always - tag: "latest" + tag: "0.8" +lables: {} +selectorLabels: + app: vauthenticator +podAnnotations: {} ``` -#### Properties description +| Name | Description | Default | +| --- | --- | --- | +| `*.pod.probes.liveness.*` | Liveness probe timing. | `10`, `30` | +| `*.pod.probes.rediness.*` | Readiness probe timing. The value name is currently spelled `rediness` in the chart API. | `10`, `30` | +| `*.service.type` | Kubernetes service type. | `ClusterIP` | +| `*.ingress.host` | Ingress host. | `"*"` | +| `*.ingress.annotations` | Extra ingress annotations. | `{}` | +| `*.ingress.tls` | Ingress TLS block. | `{}` | +| `*.ingress.class` | Ingress class annotation value. | `nginx` | +| `*.ingress.enabled` | Present in defaults, but current templates always render Ingress resources. | `true` | +| `*.resources` | Container resource requests and limits. | See `values.yaml` | +| `*.replicaCount` | Deployment replica count. | `1` | +| `*.image.repository` | Container image repository. | workload-specific | +| `*.image.pullPolicy` | Container image pull policy. | `Always` | +| `*.image.tag` | Container image tag. | `0.8` | +| `*.lables` | Extra pod labels. The value name is currently spelled `lables` in the chart API. | `{}` | +| `*.selectorLabels` | Selector labels used by Deployment and Service. Change with care. | workload-specific | +| `*.podAnnotations` | Extra pod annotations. | `{}` | -| Name | Description | Value | -|------------------|-------------------------------------------|------------| -| image.repository | usual cpu kubernetes request parameter | it depends | -| image.pullPolicy | usual memory kubernetes request parameter | Always | -| image.tag | usual cpu kubernetes limits parameter | "latest" | - -## Replicas count, Lables and selectors (application scoped) - -#### yaml section +## Authorization Server ```yaml -service: - type: ClusterIP -replicaCount: 1 - -lables: { } - -selectorLabels: - app: vauthenticator - -podAnnotations: { } +application: + sessionTimeout: 24h + profiles: dynamo,kms + masterKey: ACCOUNT_KMS_KEY + baseUrl: http://application-example-host.com + backChannelBaseUrl: http://vauthenticator:8080 + server: + port: 8080 + redis: + database: 0 + host: vauthenticator-redis-master.auth.svc.cluster.local ``` -#### Properties description +| Name | Description | Default | +| --- | --- | --- | +| `application.sessionTimeout` | Server session timeout as a Spring duration. | `24h` | +| `application.profiles` | Active Spring profiles, for example `dynamo,kms` or `database`. | `dynamo,kms` | +| `application.masterKey` | Master key identifier used by key management configuration. | `ACCOUNT_KMS_KEY` | +| `application.baseUrl` | Public authorization server base URL. | `http://application-example-host.com` | +| `application.backChannelBaseUrl` | Internal service URL used for back-channel calls. | `http://vauthenticator:8080` | +| `application.server.port` | Application container port. | `8080` | +| `application.redis.database` | Redis database index. | `0` | +| `application.redis.host` | Redis host. This host is also used by the current management UI template. | `vauthenticator-redis-master.auth.svc.cluster.local` | -| Name | Description | Value | -|----------------|-----------------------------------------------------------------------------------------------|------------| -| replicaCount | define how many pod replicas you want | 1 | -| service.type | **do not touch this this** it is used internally | ClusterIP | -| selectorLabels | **do not touch this this** it is used internally as common selector for pod service and so on | it depends | -| lables | define pod lables if requred | { } | -| podAnnotations | define pod annotations if requred | { } | +### Application AWS Endpoint Overrides -# VAuthenticator Authorization Server - -## Authorization Server +```yaml +application: + aws: + s3: + endpointOverride: + kms: + endpointOverride: + dynamodb: + endpointOverride: +``` -Authorization server backend application configuration +Use these values for LocalStack or non-default AWS endpoints. -#### yaml section +### Password Policy ```yaml application: - sessionTimeout: 24h - profiles: dynamo,kms password: history: evaluationLimit: 1 @@ -246,204 +215,154 @@ application: minSize: 1 minSpecialSymbol: 1 enablePasswordReusePrevention: true +``` - masterKey: ACCOUNT_KMS_KEY - - redis: - database: 0 - host: vauthenticator-redis-master.auth.svc.cluster.local - - server: - port: 8080 - - baseUrl: http://application-example-host.com - backChannelBaseUrl: http://vauthenticator:8080 - +### Email - mailProvider: +```yaml +application: + emailProvider: enabled: false host: localhost port: 587 username: "" password: "" - properties: { } - - mail: + properties: {} + email: from: "" - welcomeMailSubject: "" - verificationMailSubject: "" - resetPasswordMailSubject: "" - mfaMailSubject: "" + welcomeEMailSubject: "" + verificationEMailSubject: "" + resetPasswordEMailSubject: "" + mfaEMailSubject: "" +``` +`emailProvider` controls Spring mail configuration. The `email` block provides default sender and subject values used by communication flows when email is enabled. + +### DynamoDB Tables + +```yaml +application: dynamoDb: account: tableName: your_VAuthenticator_Account_table_name + cache: + ttl: 1h + name: account_cache role: tableName: your_VAuthenticator_Account_Role_table_name role: tableName: your_VAuthenticator_Role_table_name + cache: + ttl: 1h + name: role_cache + protectedFromDelete: ROLE_USER,VAUTHENTICATOR_ADMIN clientApplication: tableName: your_VAuthenticator_ClientApplication_table_name + cache: + ttl: 1h + name: client_application mfaAccountMethods: tableName: your_VAuthenticator_mfaAccountMethods_table_name + defaultMfaAccountMethods: + tableName: your_VAuthenticator_defaultMfaAccountMethods_table_name keys: mfa: - tableName: your_VAuthenticator_Mfa-Keys_table_name + tableName: your_VAuthenticator_Mfa_Keys_table_name signature: tableName: your_VAuthenticator_Signature_Keys_table_name - tableName: your_VAuthenticator_Keys_table_name ticket: - tableName: your_VAuthenticator_tICKET_table_name - - documentRepository: - engine: s3 - bucketName: test - fsBasePath: dist - documentType: - mail: - cacheName: - cacheTtl: 1m - staticAsset: - cacheName: - cacheTtl: 1m + tableName: your_VAuthenticator_Ticket_table_name + passwordHistory: + tableName: your_VAuthenticator_Password_History_table_name + historyEvaluationLimit: 1 + maxHistoryAllowedSize: 3 +``` + +These values configure the DynamoDB-backed repositories used by the `dynamo` profile. +### Documents, MFA, Assets, Events + +```yaml +application: + documentRepository: + engine: s3 + bucketName: test + fsBasePath: dist + documentType: + email: + cacheName: + cacheTtl: 1m + staticAsset: + cacheName: + cacheTtl: 1m mfa: otp: otpLength: 6 otpTimeToLiveInSeconds: 30 - assetServer: onS3: enabled: false + bundleVersion: "" baseUrl: http://localhost:8080 - events: enableLoggerConsumer: false - ``` -#### Properties description - -| Name | Description | Value | -|-----------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------| -| application.sessionTimeout | Server Session Timeout expressed as java duration | 24h | -| application.masterKey | KMS Master key as starting key to generate data key data key pair to sign tokens, MFA and data encryption | your kms key id | -| application.redis.database | Redis database used by vauthenticator authorization server | 0 | -| application.host | Redis database host used by vauthenticator authorization server | vauthenticator-redis-master.auth.svc.cluster.local | -| application.server.port | standard port in which the main tomcat is exposed
**do not touch it!** it is useless lets to use ingress to access to the main tomcat | 8080 | -| application.baseUrl | public url to reach the authorization server | | -| application.backChannelBaseUrl | machine to machine url to reach the authorization server typically it should be kubernetes service | http://vauthenticator:8080 | -| application.emailProvider.enabled | define if enable mail communication support | false | -| application.emailProvider.host | mail server host | localhost | -| application.emailProvider.port | mail server used port | 587 | -| application.emailProvider.username | mail server account username | "" | -| application.emailProvider.password | mail server account password | "" | -| application.emailProvider.properties | mail server additional properties | { } | -| application.email.from | mail form default field | "" | -| application.email.welcomeEMailSubject | subject for welcome mail | "" | -| application.email.verificationEMailSubject | subject for account verification mail | "" | -| application.email.resetPasswordEMailSubject | subject for account password reset | "" | -| application.email.mfaEMailSubject | subject for account mfa verification0 | "" | -| application.dynamoDb.account.tableName | Account Table Name | your_VAuthenticator_Account_table_name | -| application.dynamoDb.account.cache.ttl | Account Table Redis Cache TTL | 1h | -| application.dynamoDb.account.cache.name | Account Table Redis Cache Region Name | account_cache | -| application.dynamoDb.account.role.tableName | Account Roles Table Name | your_VAuthenticator_Account_Role_table_name | -| application.dynamoDb.role.tableName | Roles Table Name | your_VAuthenticator_Role_table_name | -| application.dynamoDb.role.cache.ttl | Role Table Redis Cache TTL | 1h | -| application.dynamoDb.role.cache.name | Role Table Redis Cache Region Name | role_cache | -| application.dynamoDb.clientApplication.tableName | Client Applications Table Name | your_VAuthenticator_ClientApplication_table_name | -| application.dynamoDb.clientApplication.cache.ttl | Client Applications Table Redis Cache | 1k | -| application.dynamoDb.clientApplication.cache.name | Client Applications Table Redis Region Name | client_application | -| application.dynamoDb.mfaAccountMethods.tableName | MFA Account Methods Table Name | your_VAuthenticator_mfaAccountMethods_table_name | -| application.dynamoDb.defaultMfaAccountMethods.tableName | MFA Default Account Methods Table Name | your_VAuthenticator_defaultMfaAccountMethods_table_name | -| application.dynamoDb.keys.mfa.tableName | MFA keys Table Name | your_VAuthenticator_Mfa_Keys_table_name | -| application.dynamoDb.keys.signature.tableName | Token Signature Keys Table Name | your_VAuthenticator_Signature_Keys_table_name | -| application.dynamoDb.ticket.tableName | Ticket Table Name | your_VAuthenticator_Ticket_table_name | -| application.dynamoDb.passwordHistory.tableName | Password History Table Name | your_VAuthenticator_Password_History_table_name | -| application.dynamoDb.passwordHistory.historyEvaluationLimit | Password History evaluation limit | 1 | -| application.dynamoDb.passwordHistory.maxHistoryAllowedSize | Password History max history entry allowed | 3 | -| application.mfa.otp.otpLength | mfa otp code length | 6 | -| application.mfa.otp.otpTimeToLiveInSeconds | mfa otp ttl | 30 | -| application.assetServer.baseUrl | asset server for login, mfa and other pages | http://localhost:8080/asset | -| application.assetServer.onS3.enabled | enable asset serving form S3 | true/false | -| application.documentRepository.engine | what engine use to get documents S3 or FileSystem | s3/file-system | -| application.documentRepository.bucketName | bucket used to store documents when S3 document engine is enabled | your-bucket | -| application.documentRepository.fsBasePath | file system base path to store documents when FileSystem document engine is enabled | your-base-path | -| application.documentRepository.documentType.email.cacheName | file local cache region name for mail documents | your-base-path | -| application.documentRepository.documentType.email.cacheTtl | file local cache region ttl for mail documents | your-base-path | -| application.documentRepository.documentType.staticAsset.cacheName | file local cache region name for static asset | your-base-path | -| application.documentRepository.documentType.staticAsset.cacheTtl | file local cache region ttl for static asset | your-base-path | -| application.events.enableLoggerConsumer | enable the default logger consumer for the vauthenticator events | your-base-path | -| application.events.profiles | defines the activated spring profiles, useful to switch from dynamo to db or local java to kms implementation | dynamo,kms | -| application.events.password.history.evaluationLimit | it defines how many stored historical password evaluate | 1 | -| application.events.password.history.maxHistoryAllowedSize | it defines the max number of stored historical password for a specific user | 3 | -| application.events.password.generatorCriteria.upperCaseCharactersSize | it defines how many upper case characters to use to generate a ramdom password | 2 | -| application.events.password.generatorCriteria.lowerCaseCharactersSize | it defines how many lower case characters to use to generate a ramdom password | 2 | -| application.events.password.generatorCriteria.specialCharactersSize | it defines how many special characters to use to generate a ramdom password | 2 | -| application.events.password.generatorCriteria.numberCharactersSize | it defines how characters to use to generate a ramdom password | 2 | -| application.events.password.policy.minSize | it defines what is the minimum accepted password length | 1 | -| application.events.password.policy.minSpecialSymbol | it defines what is the minimum number of special characters to use for a password | 1 | -| application.events.password.policy.enablePasswordReusePrevention | enable the password reuse prevention feature | true | - -# VAuthenticator Authorization Server Management UI - -Authorization server Management UI backend application configuration - -#### yaml section +## Management UI Workload + +The chart currently always renders the management UI manifests; there is no `managementUi.enabled` value in `values.yaml`. ```yaml managementUi: - enabled: false redis: database: 1 host: vauthenticator-redis-master.auth.svc.cluster.local - server: port: 8080 - sso: clientApp: clientId: vauthenticator-management-ui clientSecret: secret - baseUrl: http://application-example-host.com +``` + +| Name | Description | Default | +| --- | --- | --- | +| `managementUi.redis.database` | Redis database index for the management UI workload. | `1` | +| `managementUi.server.port` | Management UI container port. | `8080` | +| `managementUi.sso.clientApp.clientId` | OAuth2 client ID used by the management UI workload. | `vauthenticator-management-ui` | +| `managementUi.sso.clientApp.clientSecret` | OAuth2 client secret used by the management UI workload. | `secret` | +| `managementUi.baseUrl` | Public management UI base URL. | `http://application-example-host.com` | +### Management UI Documents And Assets + +```yaml +managementUi: documentRepository: engine: s3 bucketName: test fsBasePath: dist documentType: mail: - cacheName: + cacheName: mail-document-local-cache cacheTtl: 1m staticAsset: - cacheName: + cacheName: static-asset-document-local-cache cacheTtl: 1m - assetServer: onS3: enabled: false + bundleVersion: "" baseUrl: http://localhost:8080 - ``` -#### Properties description - -| Name | Description | Value | -|--------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------| -| managementUi.enabled | define if management ui have to be deployed | false | -| managementUi.redis.database | Redis database used by vauthenticator authorization server | 0 | -| managementUi.host | Redis database host used by vauthenticator authorization server | vauthenticator-redis-master.auth.svc.cluster.local | -| managementUi.server.port | standard port in which the main tomcat is exposed
**do not touch it!** it is useless lets to use ingress to access to the main tomcat | 8080 | -| managementUi.sso.clientApp.clientId | client application id used for sso login | authenticator-management-ui | -| managementUi.sso.clientApp.clientSecret | client application secret used for sso login | secret | -| managementUi.baseUrl | base url used to compute redirect uri and other stuff it is the base url in which you will publish the management app | http://application-example-host.com | -| managementUi.assetServer.baseUrl | asset server for login, mfa and other pages | http://localhost:8080/asset | -| managementUi.assetServer.onS3.enabled | enable asset serving form S3 | true/false | -| managementUi.documentRepository.engine | what engine use to get documents S3 or FileSystem | s3/file-system | -| managementUi.documentRepository.bucketName | bucket used to store documents when S3 document engine is enabled | your-bucket | -| managementUi.documentRepository.fsBasePath | file system base path to store documents when FileSystem document engine is enabled | your-base-path | -| managementUi.documentRepository.documentType.mail.cacheName | file local cache region name for mail documents | your-base-path | -| managementUi.documentRepository.documentType.mail.cacheTtl | file local cache region ttl for mail documents | your-base-path | -| managementUi.documentRepository.documentType.staticAsset.cacheName | file local cache region name for static asset | your-base-path | -| managementUi.documentRepository.documentType.staticAsset.cacheTtl | file local cache region ttl for static asset | your-base-path | +| Name | Description | Default | +| --- | --- | --- | +| `managementUi.documentRepository.engine` | Document repository engine. | `s3` | +| `managementUi.documentRepository.bucketName` | S3 bucket when the S3 document engine is used. | `test` | +| `managementUi.documentRepository.fsBasePath` | Filesystem base path when filesystem documents are used. | `dist` | +| `managementUi.documentRepository.documentType.mail.*` | Mail document cache settings. | See `values.yaml` | +| `managementUi.documentRepository.documentType.staticAsset.*` | Static asset cache settings. | See `values.yaml` | +| `managementUi.assetServer.onS3.enabled` | Serve assets from S3. | `false` | +| `managementUi.assetServer.onS3.bundleVersion` | Optional S3 bundle version. | `""` | +| `managementUi.assetServer.baseUrl` | Asset server base URL. | `http://localhost:8080` | diff --git a/management-ui/AGENTS.md b/management-ui/AGENTS.md index a80f38a17..5fbba6499 100644 --- a/management-ui/AGENTS.md +++ b/management-ui/AGENTS.md @@ -4,28 +4,27 @@ This project is the standalone admin UI for VAuthenticator. It is a React and TypeScript application that builds static bundles for the management experience, including the admin SPA plus OAuth callback and RP-initiated logout entry points. -Use this guide for any work under `management-ui`. Per the repo root instructions, this file takes precedence over the monorepo-level `Agents.md` for this subtree. +Use this guide for any work under `management-ui`. Per the repo root instructions, this file takes precedence over the monorepo-level `AGENTS.md` for this subtree. ## Stack -- React 18 -- TypeScript +- React 19 +- TypeScript 6 - webpack 5 -- Material UI 5 -- React Router 6 -- `dotenv-webpack` for build-time environment injection +- Material UI 9 +- React Router 7 +- Runtime auth/API settings are loaded from `config-manager` The package manifest is `src/package.json` and the webpack config is `src/webpack.config.js`. ## Repository Layout - `src/admin`: admin SPA routes and feature pages -- `src/auth`: OAuth callback, logout, and authentication helpers +- `src/auth`: OAuth callback, logout, authentication helpers, and OIDC session-management iframe integration - `src/components`: shared UI components used across pages -- `src/config`: runtime config loading from build-time env vars +- `src/config`: runtime config loading from `GET /api/config` - `src/theme`: MUI theme setup - `src/utils`: shared frontend utilities -- `environments`: `.env.*` files consumed by webpack builds - `local`: nginx and docker-compose files for local static serving - `dist`: generated frontend output - `changelog`: project release notes @@ -55,12 +54,18 @@ Authentication is handled in `src/auth/Authenticator.ts` using an OAuth2 authori Logout in this UI is RP-initiated OIDC logout against the authorization server. It is not an OIDC Front-Channel Logout receiver. +Runtime configuration is loaded by `src/config/ConfigLoader.ts` from `GET /api/config`, which is expected to be served by the `config-manager` service. The response is cached in `window.sessionStorage` under `appConfig` and cleared on logout. + +OIDC Session Management is implemented in `src/auth/SessionManagement.tsx`. It uses the OP iframe at `${idpBaseUrl}/session/management`, stores the current `SESSION_STATE`, and shows a relogin dialog when the OP reports `changed` or `error` and the silent session check fails. + Important session keys: - `ID_TOKEN` - `ACCESS_TOKEN` +- `SESSION_STATE` - `codeVerifier` - `returnTo` +- `appConfig` The UI depends on the VAuthenticator backend for: @@ -68,15 +73,18 @@ The UI depends on the VAuthenticator backend for: - `/oauth2/token` - `/userinfo` - `/connect/logout` +- `/session/management` - `/api/...` management endpoints used by repositories under `src/admin` When changing admin pages, always check the corresponding repository class to confirm the backend contract rather than inferring request and response shapes from UI usage alone. ## Environment And Local Runtime -The development build reads `environments/.env.development`. +Runtime configuration comes from `GET /api/config`, which local nginx proxies to `config-manager`. + +The local nginx config proxies `/api/config` to `config-manager` on the host at port `8086`. -Current development variables include: +Current `config-manager` variables for local development include: - `REDIRECT_URI` - `CLIENT_APPLICATION_ID` @@ -88,6 +96,7 @@ Local defaults point to: - management UI: `http://local.management.vauthenticator.com:8085` - auth server/API: `http://local.api.vauthenticator.com:9090` +- config manager: `http://local.ui-config-manager.vauthenticator.com:8086` Local serving is nginx-based through `local/docker-compose.yml`, which mounts `dist/` into an nginx container and exposes port `8085`. @@ -108,22 +117,24 @@ Notes: - `build.sh` removes `dist/`, reinstalls `src/node_modules`, and runs the development webpack build. - The generated output is written to `management-ui/dist`. +- For the local UI to start authentication correctly, run `config-manager` with matching local values before opening the UI. ## Conventions For Changes - Keep feature logic inside the existing admin domains such as `account`, `clientapp`, `communication`, `key`, and `roles`. - Reuse shared components from `src/components` before introducing new one-off widgets. - Preserve the existing routing style with `HashRouter` unless a broader routing migration is explicitly requested. -- Keep environment-driven values in `src/config/ConfigLoader.ts`; do not hardcode backend hosts or client IDs into feature components. -- When changing authentication or token usage, verify the callback, logout, and session-storage flow together. +- Keep runtime values in the `config-manager` response and `src/config/ConfigLoader.ts`; do not hardcode backend hosts or client IDs into feature components. +- When changing authentication, token usage, or OIDC session management, verify the callback, logout, config cache, and session-storage flow together. - If a page fetches backend data, inspect the matching repository file first and keep request/response changes aligned with backend APIs. -- Avoid mixing `management-ui` edits with `auth-server` or Helm changes unless explicitly requested. +- Avoid mixing `management-ui` edits with `auth-server`, `config-manager`, or Helm changes unless explicitly requested. ## Files Worth Reading First - `src/webpack.config.js` - `src/admin/index.tsx` - `src/auth/Authenticator.ts` +- `src/auth/SessionManagement.tsx` - `src/config/ConfigLoader.ts` - `local/docker-compose.yml` - `local/conf.d/default.conf` diff --git a/management-ui/local/conf.d/default.conf b/management-ui/local/conf.d/default.conf index 29a2aa671..d5f130dc7 100644 --- a/management-ui/local/conf.d/default.conf +++ b/management-ui/local/conf.d/default.conf @@ -4,6 +4,12 @@ server { server_name localhost; #access_log /var/log/nginx/host.access.log main; + location /api/config { + proxy_pass http://host.docker.internal:8086; + proxy_set_header Host local.ui-config-manager.vauthenticator.com; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } location / { default_type text/html; @@ -19,6 +25,7 @@ server { try_files $uri.html $uri =404; } + #error_page 404 /404.html; # redirect server error pages to the static page /50x.html diff --git a/management-ui/local/docker-compose.yml b/management-ui/local/docker-compose.yml index 53c543c0b..7cdb02fd8 100644 --- a/management-ui/local/docker-compose.yml +++ b/management-ui/local/docker-compose.yml @@ -2,6 +2,8 @@ version: "3" services: vauthenticator-management-ui: image: nginx + extra_hosts: + - "host.docker.internal:host-gateway" volumes: - ../dist:/usr/share/nginx/html - ./conf.d/default.conf:/etc/nginx/conf.d/default.conf diff --git a/management-ui/src/admin/key/KeyManagementPage.tsx b/management-ui/src/admin/key/KeyManagementPage.tsx index 3692d7fe2..6b8f15fc7 100644 --- a/management-ui/src/admin/key/KeyManagementPage.tsx +++ b/management-ui/src/admin/key/KeyManagementPage.tsx @@ -6,7 +6,6 @@ import {Alert, Snackbar} from "@mui/material"; import {Delete} from "@mui/icons-material"; import AutorenewIcon from '@mui/icons-material/Autorenew'; import KeyDialog from "./KeyDialog"; -import moment from "moment"; const columns: StickyHeadTableColumn[] = [ {id: 'masterKey', label: 'Maser Key', minWidth: 170}, @@ -16,6 +15,13 @@ const columns: StickyHeadTableColumn[] = [ {id: 'expireIn', label: 'Expire In', minWidth: 170} ]; +const padDatePart = (value: number) => value.toString().padStart(2, "0") + +const formatUnixTimestamp = (timestamp: number) => { + const date = new Date(timestamp * 1000) + return `${date.getFullYear()}-${padDatePart(date.getMonth() + 1)}-${padDatePart(date.getDate())}:${padDatePart(date.getHours())}:${padDatePart(date.getMinutes())}` +} + const KeysManagementPage = () => { const pageTitle = "Keys Management" @@ -62,7 +68,7 @@ const KeysManagementPage = () => { kid: value.kid, delete: getDeleteLinkFor(value.kid), rotate: getRotateLinkFor(value.kid, value.ttl), - expireIn: value.ttl == 0 ? "Never" : moment.unix(value.ttl).format("YYYY-MM-DD:HH:mm") + expireIn: value.ttl == 0 ? "Never" : formatUnixTimestamp(value.ttl) } }) @@ -135,4 +141,4 @@ const KeysManagementPage = () => { ) } -export default KeysManagementPage \ No newline at end of file +export default KeysManagementPage diff --git a/management-ui/src/components/TabPanel.tsx b/management-ui/src/components/TabPanel.tsx index 37af54c29..e4879e4ae 100644 --- a/management-ui/src/components/TabPanel.tsx +++ b/management-ui/src/components/TabPanel.tsx @@ -1,9 +1,8 @@ import React from 'react'; -import PropTypes from 'prop-types'; import {Box, Typography} from "@mui/material"; type TabPanelProps = { - children: any + children: React.ReactNode value: string index: string } diff --git a/management-ui/src/config/ConfigLoader.ts b/management-ui/src/config/ConfigLoader.ts index e5ca58019..c848267e0 100644 --- a/management-ui/src/config/ConfigLoader.ts +++ b/management-ui/src/config/ConfigLoader.ts @@ -3,22 +3,114 @@ type ApplicationConfig = { redirectUri: string, clientApplicationId: string, idpBaseUrl: string + apiBaseUrl: string authenticationCheckInterval: number } + +type ConfigManagerResponse = { + redirectUri: string, + clientApplicationId: string, + idpBaseUrl: string + apiBaseUrl: string + authenticationCheckInterval: string | number +} + +const appConfigStorageKey = "appConfig" +const defaultScope = "openid email profile" +const configManagerEndpoint = "/api/config" +let applicationConfigRequest: Promise | null = null + +const isLogoutRequest = () => { + const pathname = window.location.pathname + return pathname.endsWith("/logout") || pathname.endsWith("/logout.html") +} + +const toApplicationConfig = (configData: ConfigManagerResponse): ApplicationConfig => ({ + scope: defaultScope, + redirectUri: configData.redirectUri, + clientApplicationId: configData.clientApplicationId, + idpBaseUrl: configData.idpBaseUrl, + apiBaseUrl: configData.apiBaseUrl, + authenticationCheckInterval: Number(configData.authenticationCheckInterval), +}) + +const loadApplicationConfigFromCache = (): ApplicationConfig | null => { + const cachedConfig = window.sessionStorage.getItem(appConfigStorageKey) + + if (!cachedConfig) { + return null + } + + try { + return toApplicationConfig(JSON.parse(cachedConfig) as ConfigManagerResponse) + } catch (e) { + window.sessionStorage.removeItem(appConfigStorageKey) + return null + } +} + +const loadApplicationConfigFromConfigManager = async (): Promise => { + const response = await fetch(configManagerEndpoint, { + method: "GET", + headers: { + "Accept": "application/json", + }, + mode: "cors", + }) + + if (!response.ok) { + throw new Error(`Unable to load application configuration: ${response.status}`) + } + + return toApplicationConfig(await response.json() as ConfigManagerResponse) +} + +const cacheApplicationConfig = (configData: ApplicationConfig) => { + window.sessionStorage.setItem(appConfigStorageKey, JSON.stringify(configData)) +} + +export const clearApplicationConfigCache = () => { + window.sessionStorage.removeItem(appConfigStorageKey) + applicationConfigRequest = null +} + export const applicationConfigLoader = async () => { - const configData = { - scope: "openid email profile", - redirectUri: process.env.REDIRECT_URI, - clientApplicationId: process.env.CLIENT_APPLICATION_ID, - idpBaseUrl: process.env.IDP_BASE_URL, - authenticationCheckInterval: Number(process.env.AUTHENTICATION_CHECK_INTERVAL), + const logoutRequest = isLogoutRequest() + const cachedConfig = loadApplicationConfigFromCache() - }; - return configData as ApplicationConfig + if (cachedConfig) { + if (logoutRequest) { + clearApplicationConfigCache() + } + return cachedConfig + } + + if (!applicationConfigRequest) { + applicationConfigRequest = loadApplicationConfigFromConfigManager() + .catch((e) => { + applicationConfigRequest = null + throw e + }) + } + + const configData = await applicationConfigRequest + + if (!logoutRequest) { + cacheApplicationConfig(configData) + } else { + clearApplicationConfigCache() + } + + return configData } export async function getIdpBaseUrl() { const appConfig = await applicationConfigLoader() return appConfig.idpBaseUrl; } + +export async function getApiBaseUrl() { + const appConfig = await applicationConfigLoader() + return appConfig.apiBaseUrl; +} diff --git a/management-ui/src/package-lock.json b/management-ui/src/package-lock.json index b0a11f603..c201c6fda 100644 --- a/management-ui/src/package-lock.json +++ b/management-ui/src/package-lock.json @@ -9,23 +9,13 @@ "version": "0.8", "license": "ISC", "dependencies": { - "@date-io/moment": "^3.2.0", "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.1", "@mui/icons-material": "^9.0.1", "@mui/material": "^9.0.1", - "@types/crypto-js": "^4.2.2", - "@types/node": "^25.8.0", - "@types/react": "^19.2.14", - "@types/react-dom": "^19.2.3", "copy-to-clipboard": "^4.0.2", "crypto-js": "^4.2.0", - "moment": "2.30.1", - "pkce-challenge": "^6.0.0", - "prop-types": "15.8.1", "react": "19.2.6", - "react-contenteditable": "^3.3.7", - "react-datetime": "3.3.1", "react-dom": "19.2.6", "react-router": "^7.15.1", "react-router-dom": "^7.15.1", @@ -34,14 +24,10 @@ "uuid": "^14.0.0" }, "devDependencies": { - "@babel/core": "7.29.0", - "@babel/preset-env": "7.29.5", - "@babel/preset-react": "7.28.5", - "@types/uuid": "^11.0.0", - "babel-loader": "10.1.1", - "clean-webpack-plugin": "4.0.0", + "@types/crypto-js": "^4.2.2", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", "css-loader": "7.1.4", - "dotenv-webpack": "^9.0.0", "html-webpack-plugin": "^5.6.7", "style-loader": "4.0.0", "ts-loader": "^9.5.7", @@ -63,47 +49,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/compat-data": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", - "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", - "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, "node_modules/@babel/generator": { "version": "7.29.1", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", @@ -120,1516 +65,59 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", - "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.3" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.3.tgz", - "integrity": "sha512-RpLYy2sb51oNLjuu1iD3bwBqCBWUzjO0ocp+iaCP/lJtb2CPLcnC2Fftw+4sAzaMELGeWTgExSKADbdo0GFVzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-member-expression-to-functions": "^7.28.5", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/helper-replace-supers": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.29.0", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz", - "integrity": "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "regexpu-core": "^6.3.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.8.tgz", - "integrity": "sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "debug": "^4.4.3", - "lodash.debounce": "^4.0.8", - "resolve": "^1.22.11" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, "node_modules/@babel/helper-globals": { "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", "license": "MIT", "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", - "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.28.5", - "@babel/types": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", - "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", - "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", - "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-wrap-function": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz", - "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.28.5", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", - "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-wrap-function": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.6.tgz", - "integrity": "sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", - "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", - "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.29.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz", - "integrity": "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz", - "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", - "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array/-/plugin-bugfix-safari-rest-destructuring-rhs-array-7.29.3.tgz", - "integrity": "sha512-SRS46DFR4HqzUzCVgi90/xMoL+zeBDBvWdKYXSEzh79kXswNFEglUpMKxR04//dPqwYXWUBJ3mpUd933ru9Kmg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", - "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-transform-optional-chaining": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.13.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.6.tgz", - "integrity": "sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.28.6.tgz", - "integrity": "sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", - "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", - "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-unicode-sets-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", - "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", - "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.0.tgz", - "integrity": "sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-remap-async-to-generator": "^7.27.1", - "@babel/traverse": "^7.29.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.28.6.tgz", - "integrity": "sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-remap-async-to-generator": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", - "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.6.tgz", - "integrity": "sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.28.6.tgz", - "integrity": "sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz", - "integrity": "sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.6.tgz", - "integrity": "sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-globals": "^7.28.0", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-replace-supers": "^7.28.6", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.28.6.tgz", - "integrity": "sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/template": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz", - "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.28.6.tgz", - "integrity": "sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", - "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.0.tgz", - "integrity": "sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", - "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-explicit-resource-management": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.6.tgz", - "integrity": "sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/plugin-transform-destructuring": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.6.tgz", - "integrity": "sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", - "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", - "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", - "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.28.6.tgz", - "integrity": "sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", - "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.6.tgz", - "integrity": "sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", - "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", - "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz", - "integrity": "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.29.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.4.tgz", - "integrity": "sha512-N7QmZ0xRZfjHOfZeQLJjwgX2zS9pdGHSVl/cjSGlo4dXMqvurfxXDMKY4RqEKzPozV78VMcd0lxyG13mlbKc4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.29.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", - "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.0.tgz", - "integrity": "sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-new-target": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", - "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.28.6.tgz", - "integrity": "sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.28.6.tgz", - "integrity": "sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.6.tgz", - "integrity": "sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/plugin-transform-destructuring": "^7.28.5", - "@babel/plugin-transform-parameters": "^7.27.7", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", - "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.28.6.tgz", - "integrity": "sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.6.tgz", - "integrity": "sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.27.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", - "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.28.6.tgz", - "integrity": "sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.28.6.tgz", - "integrity": "sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", - "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.28.0.tgz", - "integrity": "sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.27.1.tgz", - "integrity": "sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/plugin-syntax-jsx": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.27.1.tgz", - "integrity": "sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.27.1.tgz", - "integrity": "sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.0.tgz", - "integrity": "sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regexp-modifiers": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.28.6.tgz", - "integrity": "sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", - "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", - "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.28.6.tgz", - "integrity": "sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", - "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", - "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", - "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", - "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.28.6.tgz", - "integrity": "sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", - "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=6.9.0" } }, - "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "node_modules/@babel/helper-module-imports": { "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.28.6.tgz", - "integrity": "sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==", - "dev": true, + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" } }, - "node_modules/@babel/preset-env": { - "version": "7.29.5", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.5.tgz", - "integrity": "sha512-/69t2aEzGKHD76DyLbHysF/QH2LJOB8iFnYO37unDTKBTubzcMRv0f3H5EiN1Q6ajOd/eB7dAInF0qdFVS06kA==", - "dev": true, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.29.3", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", - "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", - "@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": "^7.29.3", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.6", - "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-import-assertions": "^7.28.6", - "@babel/plugin-syntax-import-attributes": "^7.28.6", - "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.27.1", - "@babel/plugin-transform-async-generator-functions": "^7.29.0", - "@babel/plugin-transform-async-to-generator": "^7.28.6", - "@babel/plugin-transform-block-scoped-functions": "^7.27.1", - "@babel/plugin-transform-block-scoping": "^7.28.6", - "@babel/plugin-transform-class-properties": "^7.28.6", - "@babel/plugin-transform-class-static-block": "^7.28.6", - "@babel/plugin-transform-classes": "^7.28.6", - "@babel/plugin-transform-computed-properties": "^7.28.6", - "@babel/plugin-transform-destructuring": "^7.28.5", - "@babel/plugin-transform-dotall-regex": "^7.28.6", - "@babel/plugin-transform-duplicate-keys": "^7.27.1", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.29.0", - "@babel/plugin-transform-dynamic-import": "^7.27.1", - "@babel/plugin-transform-explicit-resource-management": "^7.28.6", - "@babel/plugin-transform-exponentiation-operator": "^7.28.6", - "@babel/plugin-transform-export-namespace-from": "^7.27.1", - "@babel/plugin-transform-for-of": "^7.27.1", - "@babel/plugin-transform-function-name": "^7.27.1", - "@babel/plugin-transform-json-strings": "^7.28.6", - "@babel/plugin-transform-literals": "^7.27.1", - "@babel/plugin-transform-logical-assignment-operators": "^7.28.6", - "@babel/plugin-transform-member-expression-literals": "^7.27.1", - "@babel/plugin-transform-modules-amd": "^7.27.1", - "@babel/plugin-transform-modules-commonjs": "^7.28.6", - "@babel/plugin-transform-modules-systemjs": "^7.29.4", - "@babel/plugin-transform-modules-umd": "^7.27.1", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.0", - "@babel/plugin-transform-new-target": "^7.27.1", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.28.6", - "@babel/plugin-transform-numeric-separator": "^7.28.6", - "@babel/plugin-transform-object-rest-spread": "^7.28.6", - "@babel/plugin-transform-object-super": "^7.27.1", - "@babel/plugin-transform-optional-catch-binding": "^7.28.6", - "@babel/plugin-transform-optional-chaining": "^7.28.6", - "@babel/plugin-transform-parameters": "^7.27.7", - "@babel/plugin-transform-private-methods": "^7.28.6", - "@babel/plugin-transform-private-property-in-object": "^7.28.6", - "@babel/plugin-transform-property-literals": "^7.27.1", - "@babel/plugin-transform-regenerator": "^7.29.0", - "@babel/plugin-transform-regexp-modifiers": "^7.28.6", - "@babel/plugin-transform-reserved-words": "^7.27.1", - "@babel/plugin-transform-shorthand-properties": "^7.27.1", - "@babel/plugin-transform-spread": "^7.28.6", - "@babel/plugin-transform-sticky-regex": "^7.27.1", - "@babel/plugin-transform-template-literals": "^7.27.1", - "@babel/plugin-transform-typeof-symbol": "^7.27.1", - "@babel/plugin-transform-unicode-escapes": "^7.27.1", - "@babel/plugin-transform-unicode-property-regex": "^7.28.6", - "@babel/plugin-transform-unicode-regex": "^7.27.1", - "@babel/plugin-transform-unicode-sets-regex": "^7.28.6", - "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.15", - "babel-plugin-polyfill-corejs3": "^0.14.0", - "babel-plugin-polyfill-regenerator": "^0.6.6", - "core-js-compat": "^3.48.0", - "semver": "^6.3.1" - }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/preset-modules": { - "version": "0.1.6-no-external-plugins", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", - "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", - "dev": true, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@babel/preset-react": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.28.5.tgz", - "integrity": "sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ==", - "dev": true, + "node_modules/@babel/parser": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-transform-react-display-name": "^7.28.0", - "@babel/plugin-transform-react-jsx": "^7.27.1", - "@babel/plugin-transform-react-jsx-development": "^7.27.1", - "@babel/plugin-transform-react-pure-annotations": "^7.27.1" + "@babel/types": "^7.29.0" }, - "engines": { - "node": ">=6.9.0" + "bin": { + "parser": "bin/babel-parser.js" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=6.0.0" } }, "node_modules/@babel/runtime": { @@ -1686,29 +174,6 @@ "node": ">=6.9.0" } }, - "node_modules/@date-io/core": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@date-io/core/-/core-3.2.0.tgz", - "integrity": "sha512-hqwXvY8/YBsT9RwQITG868ZNb1MVFFkF7W1Ecv4P472j/ZWa7EFcgSmxy8PUElNVZfvhdvfv+a8j6NWJqOX5mA==", - "license": "MIT" - }, - "node_modules/@date-io/moment": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@date-io/moment/-/moment-3.2.0.tgz", - "integrity": "sha512-/Tm+/VGvM0cOxvBeft2ZDw/xkdDRqBj2mxqGJz1TqbVpbDgfvq0tTCe/v3iSRJSHlY48lkWRO6LM/yoxBvUNpA==", - "license": "MIT", - "dependencies": { - "@date-io/core": "^3.2.0" - }, - "peerDependencies": { - "moment": "^2.24.0" - }, - "peerDependenciesMeta": { - "moment": { - "optional": true - } - } - }, "node_modules/@discoveryjs/json-ext": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-1.1.0.tgz", @@ -1906,17 +371,6 @@ "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -2200,6 +654,7 @@ "version": "4.2.2", "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.2.2.tgz", "integrity": "sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==", + "dev": true, "license": "MIT" }, "node_modules/@types/eslint": { @@ -2231,17 +686,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/minimatch": "*", - "@types/node": "*" - } - }, "node_modules/@types/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", @@ -2256,17 +700,11 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/minimatch": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", - "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/node": { "version": "25.8.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-25.8.0.tgz", "integrity": "sha512-TCFSk8IZh+iLX1xtksoBVtdmgL+1IX0fC9BeU4QqFSuNdN/K+HUlhqOzEmSYYpZUVsLYcPqc9KX+60iDuninSQ==", + "dev": true, "license": "MIT", "dependencies": { "undici-types": ">=7.24.0 <7.24.7" @@ -2297,6 +735,7 @@ "version": "19.2.3", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, "license": "MIT", "peerDependencies": { "@types/react": "^19.2.0" @@ -2311,17 +750,6 @@ "@types/react": "*" } }, - "node_modules/@types/uuid": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-11.0.0.tgz", - "integrity": "sha512-HVyk8nj2m+jcFRNazzqyVKiZezyhDKrGUA3jlEcg/nZ6Ms+qHwocba1Y/AaVaznJTAM9xpdFSh+ptbNrhOGvZA==", - "deprecated": "This is a stub types definition. uuid provides its own type definitions, so you do not need this installed.", - "dev": true, - "license": "MIT", - "dependencies": { - "uuid": "*" - } - }, "node_modules/@webassemblyjs/ast": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", @@ -2597,55 +1025,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-uniq": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/babel-loader": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-10.1.1.tgz", - "integrity": "sha512-JwKSzk2kjIe7mgPK+/lyZ2QAaJcpahNAdM+hgR2HI8D0OJVkdj8Rl6J3kaLYki9pwF7P2iWnD8qVv80Lq1ABtg==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^5.0.0" - }, - "engines": { - "node": "^18.20.0 || ^20.10.0 || >=22.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0 || ^8.0.0-beta.1", - "@rspack/core": "^1.0.0 || ^2.0.0-0", - "webpack": ">=5.61.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, "node_modules/babel-plugin-macros": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", @@ -2661,55 +1040,6 @@ "npm": ">=6" } }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.17", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz", - "integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-define-polyfill-provider": "^0.6.8", - "semver": "^6.3.1" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.2.tgz", - "integrity": "sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.8", - "core-js-compat": "^3.48.0" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz", - "integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.8" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, "node_modules/baseline-browser-mapping": { "version": "2.9.11", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz", @@ -2727,17 +1057,6 @@ "dev": true, "license": "ISC" }, - "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", @@ -2883,22 +1202,6 @@ "node": ">=0.10.0" } }, - "node_modules/clean-webpack-plugin": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-4.0.0.tgz", - "integrity": "sha512-WuWE1nyTNAyW5T7oNyys2EN0cfP2fdRxhxnIQWiAp0bMabPdHhoGxM8A6YL2GhqwgrPnnaemVE7nv5XJ2Fhh2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "del": "^4.1.1" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "webpack": ">=4.0.0 <6.0.0" - } - }, "node_modules/clone-deep": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", @@ -2953,20 +1256,6 @@ "node": ">= 12" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, "node_modules/cookie": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", @@ -2986,20 +1275,6 @@ "integrity": "sha512-gklSft7IuhriZKHKpuoA1fpJSLPNgvUMWMo5BlnzAJm0zNKnznoSv23IjtNqclx8eKi6ZcdvFFzYEER/+U1LoQ==", "license": "MIT" }, - "node_modules/core-js-compat": { - "version": "3.49.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.49.0.tgz", - "integrity": "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==", - "dev": true, - "license": "MIT", - "dependencies": { - "browserslist": "^4.28.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, "node_modules/cosmiconfig": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", @@ -3152,25 +1427,6 @@ } } }, - "node_modules/del": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", - "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/glob": "^7.1.1", - "globby": "^6.1.0", - "is-path-cwd": "^2.0.0", - "is-path-in-cwd": "^2.0.0", - "p-map": "^2.0.0", - "pify": "^4.0.1", - "rimraf": "^2.6.3" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/dom-converter": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", @@ -3261,42 +1517,6 @@ "tslib": "^2.0.3" } }, - "node_modules/dotenv": { - "version": "14.3.2", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-14.3.2.tgz", - "integrity": "sha512-vwEppIphpFdvaMCaHfCEv9IgwcxMljMw2TnAQBB4VWPvzXQLTb82jwmdOKzlEVUL3gNFT4l4TPKO+Bn+sqcrVQ==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - } - }, - "node_modules/dotenv-defaults": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/dotenv-defaults/-/dotenv-defaults-5.0.2.tgz", - "integrity": "sha512-y5z4NhblzwNk8XBIYVzjLcFkANK0rxbRDO6kGOfH9QrVYIGVEX52IqwSprKVsaLHM9pnNkCSxazZF/JPydDPvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "dotenv": "^14.0.0" - } - }, - "node_modules/dotenv-webpack": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/dotenv-webpack/-/dotenv-webpack-9.0.0.tgz", - "integrity": "sha512-uUtvrfEt+wXXadiZ/VvBYmLmd7KffMhsdaJLZmTTGaixFBZqD2emBwLsWKP4mdJ7bj11gxEcmH6EBFAoWwHrqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "dotenv-defaults": "^5.0.2" - }, - "engines": { - "node": ">=18.18.0" - }, - "peerDependencies": { - "webpack": "^4 || ^5" - } - }, "node_modules/electron-to-chromium": { "version": "1.5.267", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", @@ -3426,16 +1646,6 @@ "node": ">=4.0" } }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -3450,6 +1660,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, "license": "MIT" }, "node_modules/fast-uri": { @@ -3498,23 +1709,6 @@ "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", "license": "MIT" }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/flat": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", @@ -3525,13 +1719,6 @@ "flat": "cli.js" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -3541,71 +1728,12 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/glob-to-regexp": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/globby/node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true, + "license": "BSD-2-Clause" }, "node_modules/graceful-fs": { "version": "4.2.11", @@ -3854,25 +1982,6 @@ "node": ">=8" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "license": "ISC" - }, "node_modules/interpret": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", @@ -3914,42 +2023,6 @@ "node": ">=0.12.0" } }, - "node_modules/is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-path-in-cwd": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", - "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-path-inside": "^2.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-path-inside": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", - "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-is-inside": "^1.0.2" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -4042,19 +2115,6 @@ "dev": true, "license": "MIT" }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -4085,22 +2145,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/lodash": { "version": "4.17.23", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", @@ -4108,13 +2152,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true, - "license": "MIT" - }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -4137,16 +2174,6 @@ "tslib": "^2.0.3" } }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, "node_modules/memoize-one": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", @@ -4184,28 +2211,6 @@ "node": ">= 0.6" } }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/moment": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", - "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", - "license": "MIT", - "engines": { - "node": "*" - } - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -4278,58 +2283,6 @@ "node": ">=0.10.0" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -4402,23 +2355,6 @@ "node": ">=8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", - "dev": true, - "license": "(WTFPL OR MIT)" - }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -4463,48 +2399,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "pinkie": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pkce-challenge": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-6.0.0.tgz", - "integrity": "sha512-KrEAOa2aGlEyV+gN2jMXwSQhZ5gUR4WwivtPjSktYzh7srb70pYvVGV0uLfmoCx75fUQtqTairuoxayHLtvo9w==", - "license": "MIT", - "engines": { - "node": ">=16.20.0" - } - }, "node_modules/postcss": { "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", @@ -4655,32 +2549,6 @@ "node": ">=0.10.0" } }, - "node_modules/react-contenteditable": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/react-contenteditable/-/react-contenteditable-3.3.7.tgz", - "integrity": "sha512-GA9NbC0DkDdpN3iGvib/OMHWTJzDX2cfkgy5Tt98JJAbA3kLnyrNbBIpsSpPpq7T8d3scD39DHP+j8mAM7BIfQ==", - "license": "Apache-2.0", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "prop-types": "^15.7.1" - }, - "peerDependencies": { - "react": ">=16.3" - } - }, - "node_modules/react-datetime": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/react-datetime/-/react-datetime-3.3.1.tgz", - "integrity": "sha512-CMgQFLGidYu6CAlY6S2Om2UZiTfZsjC6j4foXcZ0kb4cSmPomdJ2S1PhK0v3fwflGGVuVARGxwkEUWtccHapJA==", - "license": "MIT", - "dependencies": { - "prop-types": "^15.5.7" - }, - "peerDependencies": { - "moment": "^2.16.0", - "react": "^16.5.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, "node_modules/react-dom": { "version": "19.2.6", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.6.tgz", @@ -4787,64 +2655,6 @@ "node": ">= 10.13.0" } }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true, - "license": "MIT" - }, - "node_modules/regenerate-unicode-properties": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz", - "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "regenerate": "^1.4.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regexpu-core": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz", - "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==", - "dev": true, - "license": "MIT", - "dependencies": { - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.2.2", - "regjsgen": "^0.8.0", - "regjsparser": "^0.13.0", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.2.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/regjsparser": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.0.tgz", - "integrity": "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "jsesc": "~3.1.0" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, "node_modules/relateurl": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", @@ -4931,20 +2741,6 @@ "node": ">=4" } }, - "node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", @@ -4971,16 +2767,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/set-cookie-parser": { "version": "2.7.2", "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", @@ -5306,51 +3092,8 @@ "version": "7.24.6", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", - "license": "MIT" - }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", - "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz", - "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz", - "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } + "license": "MIT" }, "node_modules/update-browserslist-db": { "version": "1.2.3", @@ -5585,20 +3328,6 @@ "dev": true, "license": "MIT" }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - }, "node_modules/yaml": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", @@ -5607,19 +3336,6 @@ "engines": { "node": ">= 6" } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } } } } diff --git a/management-ui/src/package.json b/management-ui/src/package.json index 9373c1d76..53537137e 100644 --- a/management-ui/src/package.json +++ b/management-ui/src/package.json @@ -1,29 +1,19 @@ { "name": "vauthenticator-management-ui", "version": "0.8", - "description": "Family Budget UI library", - "babel": { - "presets": [ - "@babel/react", - "@babel/env" - ] - }, + "description": "VAuthenticator Management UI", "scripts": { - "build": "ENV=development webpack --config webpack.config.js --mode=development", - "production-build": "ENV=production webpack --config webpack.config.js --mode=production", - "watch": "webpack --watch" + "build": "webpack --config webpack.config.js --mode=development", + "production-build": "webpack --config webpack.config.js --mode=production", + "watch": "webpack --config webpack.config.js --mode=development --watch" }, "author": "mrFlick72", "license": "ISC", "devDependencies": { - "@babel/core": "7.29.0", - "@babel/preset-env": "7.29.5", - "@babel/preset-react": "7.28.5", - "@types/uuid": "^11.0.0", - "babel-loader": "10.1.1", - "clean-webpack-plugin": "4.0.0", + "@types/crypto-js": "^4.2.2", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", "css-loader": "7.1.4", - "dotenv-webpack": "^9.0.0", "html-webpack-plugin": "^5.6.7", "style-loader": "4.0.0", "ts-loader": "^9.5.7", @@ -31,23 +21,13 @@ "webpack-cli": "7.0.2" }, "dependencies": { - "@date-io/moment": "^3.2.0", "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.1", "@mui/icons-material": "^9.0.1", "@mui/material": "^9.0.1", - "@types/crypto-js": "^4.2.2", - "@types/node": "^25.8.0", - "@types/react": "^19.2.14", - "@types/react-dom": "^19.2.3", "copy-to-clipboard": "^4.0.2", "crypto-js": "^4.2.0", - "moment": "2.30.1", - "pkce-challenge": "^6.0.0", - "prop-types": "15.8.1", "react": "19.2.6", - "react-contenteditable": "^3.3.7", - "react-datetime": "3.3.1", "react-dom": "19.2.6", "react-router": "^7.15.1", "react-router-dom": "^7.15.1", diff --git a/management-ui/src/tsconfig.json b/management-ui/src/tsconfig.json index cec9d7a26..89f8a03ac 100644 --- a/management-ui/src/tsconfig.json +++ b/management-ui/src/tsconfig.json @@ -6,7 +6,6 @@ "dom.iterable", "esnext" ], - "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, @@ -20,7 +19,6 @@ "noEmit": false, "jsx": "react-jsx", "types": [ - "node", "react", "react-dom" ] diff --git a/management-ui/src/webpack.config.js b/management-ui/src/webpack.config.js index f5186fb4c..6b744405c 100644 --- a/management-ui/src/webpack.config.js +++ b/management-ui/src/webpack.config.js @@ -1,10 +1,8 @@ const path = require('path'); -const BUID_DIR = path.resolve("../dist"); -const Dotenv = require('dotenv-webpack'); +const BUILD_DIR = path.resolve(__dirname, "../dist"); const HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = { - mode: 'development', entry: { callback: path.resolve(__dirname, './auth/Callback.tsx'), logout: path.resolve(__dirname, './auth/Logout.tsx'), @@ -13,7 +11,6 @@ module.exports = { resolve: { extensions: ['.tsx', '.ts', ".js", ".jsx"] }, - plugins: [], module: { rules: [ { @@ -25,24 +22,9 @@ module.exports = { use: ['ts-loader'], exclude: /node_modules$/, }, - { - test: /\.js?$/, - exclude: path.resolve(__dirname, "node_modules"), - use: { - loader: "babel-loader", - options: { - presets: ['@babel/env', '@babel/react'] - } - } - - } ] }, plugins: [ - new Dotenv({ - path: `../environments/.env.${process.env.ENV}` - }), - new HtmlWebpackPlugin({ title: 'VAuthenticator', filename: 'logout.html', @@ -64,6 +46,6 @@ module.exports = { ], output: { filename: '[name].[fullhash].js', - path: BUID_DIR + path: BUILD_DIR } -}; \ No newline at end of file +};