diff --git a/.agents/skills/backend-architecture/SKILL.md b/.agents/skills/backend-architecture/SKILL.md index 4eef461149..0ac880fffc 100644 --- a/.agents/skills/backend-architecture/SKILL.md +++ b/.agents/skills/backend-architecture/SKILL.md @@ -9,6 +9,14 @@ description: > # Backend Architecture +## Quick Start + +Run `Exceptionless.AppHost` from your IDE, or start everything from the repo root: + +```bash +aspire run --project src/Exceptionless.AppHost +``` + ## Project Layering ```text @@ -119,7 +127,7 @@ After any API change (new endpoint, changed status codes, modified request/respo ```bash # Requires the API to be running (aspire run --project src/Exceptionless.AppHost) -Invoke-WebRequest -Uri "http://localhost:5200/docs/v2/openapi.json" \ +Invoke-WebRequest -Uri "http://localhost:7110/docs/v2/openapi.json" \ -OutFile "tests/Exceptionless.Tests/Controllers/Data/openapi.json" ``` diff --git a/.agents/skills/frontend-architecture/SKILL.md b/.agents/skills/frontend-architecture/SKILL.md index d0568be717..dd32ed13bb 100644 --- a/.agents/skills/frontend-architecture/SKILL.md +++ b/.agents/skills/frontend-architecture/SKILL.md @@ -1,15 +1,21 @@ --- name: frontend-architecture description: > - Use this skill when working on the Svelte SPA's project structure — adding routes, creating + Use this skill when working on the Svelte 5 app in ClientApp — adding routes, creating feature slices, organizing shared components, or understanding the ClientApp directory layout. Covers route groups, $lib conventions, barrel exports, API client organization, and vertical - slice architecture. Apply when deciding where to place new files or components. + slice architecture. Apply when deciding where to place new files or components. The legacy + Angular app that still powers most of the site lives beside it in ClientApp.angular. --- # Frontend Architecture -Located in `src/Exceptionless.Web/ClientApp`. The Svelte SPA is the primary client. +Exceptionless.Web currently has two frontend codebases: + +- `src/Exceptionless.Web/ClientApp.angular` is the legacy Angular UI and still powers most of the site. The main folders there are `app/`, `components/`, `less/`, `img/`, `lang/`, and `grunt/`. +- `src/Exceptionless.Web/ClientApp` is the Svelte 5 app that is still under development. + +Use this skill for `ClientApp` work. ## Directory Structure diff --git a/.aspire/settings.json b/.aspire/settings.json deleted file mode 100644 index 68a4733ba2..0000000000 --- a/.aspire/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "appHostPath": "../src/Exceptionless.AppHost/Exceptionless.AppHost.csproj" -} \ No newline at end of file diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 63ebf80619..50d1f9bd96 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,13 +1,9 @@ -FROM mcr.microsoft.com/devcontainers/dotnet:0-9.0 +FROM mcr.microsoft.com/devcontainers/base:ubuntu RUN apt-get clean -y && rm -rf /var/lib/apt/lists/* /tmp/library-scripts RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ - && apt-get -y install --no-install-recommends libnss3-tools htop nano curl jq - -RUN sudo dotnet dev-certs https \ - && sudo -E dotnet dev-certs https -ep /usr/local/share/ca-certificates/aspnet/https.crt --format PEM \ - && sudo update-ca-certificates + && apt-get -y install --no-install-recommends libnss3-tools htop nano curl jq zsh # Change shell to zsh RUN chsh -s $(which zsh) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 89f2b50ec5..dc54d55c32 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,34 +1,77 @@ { - "name": "C#, Elasticsearch, Kibana, Redis", - "dockerComposeFile": "docker-compose.yml", - "service": "app", + "name": "Exceptionless Aspire Dev Container", + "build": { + "dockerfile": "Dockerfile", + "context": ".." + }, "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", + "remoteEnv": { + "PATH": "${containerEnv:PATH}:/home/vscode/.aspire/bin", + "SSL_CERT_DIR": "/home/vscode/.aspnet/dev-certs/trust:/etc/ssl/certs", + "ASPIRE_CLI_NOLOGO": "true" + }, "customizations": { "vscode": { "extensions": [ + "microsoft-aspire.aspire-vscode", "ms-dotnettools.csharp", + "ms-dotnettools.csdevkit", + "ms-azuretools.vscode-containers", "tintoy.msbuild-project-tools", "streetsidesoftware.code-spell-checker", "humao.rest-client" ] } }, - "forwardPorts": [5003, 5100, 5200, 5201, 9200, 5601], + "forwardPorts": [5601, 7101, 7111, 7121, 7131, 8025], + "otherPortsAttributes": { + "onAutoForward": "ignore" + }, "portsAttributes": { - "7049": { + "5601": { + "label": "Kibana" + }, + "7101": { + "label": "Aspire Dashboard", "protocol": "https" }, - "5100": { + "7111": { + "label": "API", "protocol": "https" + }, + "7121": { + "label": "OldApp", + "protocol": "https" + }, + "7131": { + "label": "App", + "protocol": "https" + }, + "8025": { + "label": "Mail UI" } }, + "hostRequirements": { + "cpus": 4, + "memory": "16gb", + "storage": "32gb" + }, "postCreateCommand": "bash .devcontainer/postCreateCommand.sh", "features": { - "ghcr.io/devcontainers/features/powershell:1": {}, - "ghcr.io/devcontainers/features/dotnet:1": {}, + "ghcr.io/devcontainers/features/powershell:1": { + "version": "latest" + }, + "ghcr.io/devcontainers/features/dotnet:2": { + "version": "10.0" + }, "ghcr.io/devcontainers/features/node:1": { "nodeGypDependencies": true, "version": "lts" + }, + "ghcr.io/devcontainers/features/docker-in-docker:2": { + "version": "latest", + "enableNonRootDocker": true, + "moby": true } } } diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml deleted file mode 100644 index 24168af788..0000000000 --- a/.devcontainer/docker-compose.yml +++ /dev/null @@ -1,60 +0,0 @@ -version: "3" - -services: - app: - build: - context: . - dockerfile: Dockerfile - - volumes: - - ../..:/workspaces:cached - - # Overrides default command so things don't shut down after the process ends. - command: sleep infinity - - # Runs app on the same network as the database container, allows "forwardPorts" in devcontainer.json function. - network_mode: service:elasticsearch - - # Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. - # user: root - - # Use "forwardPorts" in **devcontainer.json** to forward an app port locally. - # (Adding the "ports" property to this file will not forward from a Codespace.) - - elasticsearch: - image: exceptionless/elasticsearch:8.19.14 - environment: - node.name: elasticsearch - cluster.name: exceptionless - discovery.type: single-node - xpack.security.enabled: "false" - action.destructive_requires_name: false - ES_JAVA_OPTS: -Xms1g -Xmx1g - ports: - - 9200:9200 - volumes: - - esdata:/usr/share/elasticsearch/data - - kibana: - depends_on: - - elasticsearch - image: docker.elastic.co/kibana/kibana:8.19.14 - environment: - xpack.security.enabled: "false" - ports: - - 5601:5601 - - redis: - image: redis:7.4-alpine - ports: - - 6379:6379 - - mail: - image: axllent/mailpit:v1.27.10 - ports: - - 8025:8025 - - 1025:1025 - -volumes: - esdata: - driver: local diff --git a/.devcontainer/postCreateCommand.sh b/.devcontainer/postCreateCommand.sh index 94f44e055d..aa9382d359 100644 --- a/.devcontainer/postCreateCommand.sh +++ b/.devcontainer/postCreateCommand.sh @@ -1,4 +1,22 @@ #!/bin/bash +set -euo pipefail + +export PATH="$HOME/.dotnet/tools:$PATH" + +if dotnet tool list --global | grep -Eiq '^aspire\.cli[[:space:]]'; then + dotnet tool update --global Aspire.Cli --version 13.2.4 +else + dotnet tool install --global Aspire.Cli --version 13.2.4 +fi + +export SSL_CERT_DIR="$HOME/.aspnet/dev-certs/trust:/etc/ssl/certs${SSL_CERT_DIR:+:$SSL_CERT_DIR}" +aspire --version +dotnet dev-certs https + +if ! dotnet dev-certs https --trust; then + echo "dotnet dev-certs https --trust could not fully configure trust in this devcontainer; continuing." +fi -dotnet dev-certs https --trust dotnet restore Exceptionless.slnx +npm ci --prefix src/Exceptionless.Web/ClientApp +npm ci --prefix src/Exceptionless.Web/ClientApp.angular diff --git a/.gitattributes b/.gitattributes index ba6606be63..62ecfaeae4 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,6 +2,9 @@ * text=auto *.sh text eol=lf +# Test baselines must use LF on disk so cross-platform string comparisons work +tests/Exceptionless.Tests/Controllers/Data/openapi.json text eol=lf + # Custom for Visual Studio *.cs diff=csharp *.slnx merge=union diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 579cee91ea..88dad7b377 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -12,13 +12,8 @@ permissions: pull-requests: write env: - TERM: xterm - DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION: true - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true DOTNET_NOLOGO: true - MSBUILDTERMINALLOGGER: auto DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} - CAN_PUBLISH_IMAGES: ${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && secrets.DOCKER_USERNAME != '' && secrets.DOCKER_PASSWORD != '' }} jobs: version: @@ -26,6 +21,9 @@ jobs: timeout-minutes: 30 outputs: version: ${{ steps.version.outputs.version }} + should_publish: ${{ (github.event_name != 'pull_request' || (github.head_ref == vars.DEV_DEPLOY_BRANCH && vars.DEV_DEPLOY_BRANCH != 'main' && vars.DEV_DEPLOY_BRANCH != '')) && secrets.DOCKER_USERNAME != '' && secrets.DOCKER_PASSWORD != '' }} + is_prod_deploy: ${{ startsWith(github.ref, 'refs/tags/v') && github.event_name != 'pull_request' }} + is_dev_deploy: ${{ (github.event_name == 'push' && github.ref == format('refs/heads/{0}', vars.DEV_DEPLOY_BRANCH || 'main')) || (github.event_name == 'pull_request' && github.head_ref == vars.DEV_DEPLOY_BRANCH && vars.DEV_DEPLOY_BRANCH != 'main' && vars.DEV_DEPLOY_BRANCH != '') }} steps: - name: Checkout @@ -76,8 +74,6 @@ jobs: steps: - name: Checkout uses: actions/checkout@v6 - with: - fetch-depth: 0 - name: Setup .NET Core uses: actions/setup-dotnet@v5 @@ -85,10 +81,6 @@ jobs: dotnet-version: 10.0.* dotnet-quality: ga - - name: Start Services - working-directory: docker - run: docker compose up -d elasticsearch & - - uses: actions/cache@v5 with: path: ~/.nuget/packages @@ -97,14 +89,10 @@ jobs: nuget-${{ runner.os }}- - name: Nuget Restore - run: dotnet restore + run: dotnet restore ./Exceptionless.slnx - name: Build - run: dotnet build --no-restore --configuration Release - - - name: Wait for Elasticsearch - working-directory: docker - run: docker compose up --wait elasticsearch + run: dotnet build ./Exceptionless.slnx --no-restore --configuration Release - name: Run .NET Tests with Coverage run: dotnet test --no-restore --no-build --configuration Release --results-directory coverage --max-parallel-test-modules 1 -- --report-github --report-xunit-trx --report-xunit-trx-filename test-results.trx --coverage --coverage-output coverage.cobertura.xml --coverage-output-format cobertura @@ -137,8 +125,6 @@ jobs: steps: - name: Checkout uses: actions/checkout@v6 - with: - fetch-depth: 0 - name: Setup Node.js environment uses: actions/setup-node@v6 @@ -176,14 +162,13 @@ jobs: needs: [version] timeout-minutes: 30 env: - VERSION: ${{needs.version.outputs.version}} + VERSION: ${{ needs.version.outputs.version }} STAGING_TAG: build-${{ github.run_id }}-${{ github.run_attempt }} + SHOULD_PUBLISH: ${{ needs.version.outputs.should_publish }} steps: - name: Checkout uses: actions/checkout@v6 - with: - fetch-depth: 0 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 @@ -191,7 +176,7 @@ jobs: platforms: linux/amd64 - name: Login to GitHub Container Registry - if: ${{ env.CAN_PUBLISH_IMAGES == 'true' }} + if: ${{ env.SHOULD_PUBLISH == 'true' }} uses: docker/login-action@v4 with: registry: ghcr.io @@ -201,7 +186,7 @@ jobs: - name: Build api docker image run: | echo "::remove-matcher owner=csc::" - if [ "$CAN_PUBLISH_IMAGES" = "true" ]; then + if [ "$SHOULD_PUBLISH" = "true" ]; then docker buildx build . --build-arg MinVerVersionOverride=$VERSION --target api --platform linux/amd64 --tag ghcr.io/exceptionless/exceptionless/api-ci:$STAGING_TAG --cache-from type=registry,ref=ghcr.io/exceptionless/exceptionless/api-ci:buildcache --cache-to type=registry,ref=ghcr.io/exceptionless/exceptionless/api-ci:buildcache,mode=max --push else docker buildx build . --build-arg MinVerVersionOverride=$VERSION --target api --platform linux/amd64 --tag exceptionless/api-ci:latest --cache-from type=gha,scope=api-ci --cache-to type=gha,scope=api-ci,mode=max --load @@ -210,7 +195,7 @@ jobs: - name: Build job docker image run: | echo "::remove-matcher owner=csc::" - if [ "$CAN_PUBLISH_IMAGES" = "true" ]; then + if [ "$SHOULD_PUBLISH" = "true" ]; then docker buildx build . --build-arg MinVerVersionOverride=$VERSION --target job --platform linux/amd64 --tag ghcr.io/exceptionless/exceptionless/job-ci:$STAGING_TAG --cache-from type=registry,ref=ghcr.io/exceptionless/exceptionless/job-ci:buildcache --cache-to type=registry,ref=ghcr.io/exceptionless/exceptionless/job-ci:buildcache,mode=max --push else docker buildx build . --build-arg MinVerVersionOverride=$VERSION --target job --platform linux/amd64 --tag exceptionless/job-ci:latest --cache-from type=gha,scope=job-ci --cache-to type=gha,scope=job-ci,mode=max --load @@ -219,32 +204,33 @@ jobs: - name: Build app docker image run: | echo "::remove-matcher owner=csc::" - if [ "$CAN_PUBLISH_IMAGES" = "true" ]; then + if [ "$SHOULD_PUBLISH" = "true" ]; then docker buildx build . --build-arg MinVerVersionOverride=$VERSION --target app --platform linux/amd64 --tag ghcr.io/exceptionless/exceptionless/app-ci:$STAGING_TAG --cache-from type=registry,ref=ghcr.io/exceptionless/exceptionless/app-ci:buildcache --cache-to type=registry,ref=ghcr.io/exceptionless/exceptionless/app-ci:buildcache,mode=max --push else docker buildx build . --build-arg MinVerVersionOverride=$VERSION --target app --platform linux/amd64 --tag exceptionless/app-ci:latest --cache-from type=gha,scope=app-ci --cache-to type=gha,scope=app-ci,mode=max --load fi - name: Build all-in-one docker image + if: ${{ needs.version.outputs.is_prod_deploy == 'true' }} run: | echo "::remove-matcher owner=csc::" - if [ "$CAN_PUBLISH_IMAGES" = "true" ]; then + if [ "$SHOULD_PUBLISH" = "true" ]; then docker buildx build . --build-arg MinVerVersionOverride=$VERSION --target exceptionless --platform linux/amd64 --tag ghcr.io/exceptionless/exceptionless/exceptionless-ci:$STAGING_TAG --cache-from type=registry,ref=ghcr.io/exceptionless/exceptionless/exceptionless-ci:buildcache --cache-to type=registry,ref=ghcr.io/exceptionless/exceptionless/exceptionless-ci:buildcache,mode=max --push else docker buildx build . --build-arg MinVerVersionOverride=$VERSION --target exceptionless --platform linux/amd64 --tag exceptionless/exceptionless-ci:latest --cache-from type=gha,scope=exceptionless-ci --cache-to type=gha,scope=exceptionless-ci,mode=max --load fi docker-publish: + if: ${{ needs.version.outputs.should_publish == 'true' }} runs-on: ubuntu-latest needs: [version, docker-build, test-api, test-client] timeout-minutes: 30 env: - VERSION: ${{needs.version.outputs.version}} + VERSION: ${{ needs.version.outputs.version }} STAGING_TAG: build-${{ github.run_id }}-${{ github.run_attempt }} steps: - name: Login to GitHub Container Registry - if: ${{ env.CAN_PUBLISH_IMAGES == 'true' }} uses: docker/login-action@v4 with: registry: ghcr.io @@ -252,26 +238,33 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Login to DockerHub - if: ${{ env.CAN_PUBLISH_IMAGES == 'true' }} uses: docker/login-action@v4 with: username: ${{ env.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Pull staged docker images - if: ${{ env.CAN_PUBLISH_IMAGES == 'true' }} run: | - for image in api job app exceptionless; do + images=(api job app) + if [ "${{ needs.version.outputs.is_prod_deploy }}" = "true" ]; then + images+=(exceptionless) + fi + + for image in "${images[@]}"; do docker pull ghcr.io/exceptionless/exceptionless/$image-ci:$STAGING_TAG done - name: Publish CI Packages - if: ${{ env.CAN_PUBLISH_IMAGES == 'true' }} run: | echo "::remove-matcher owner=csc::" # tag and push docker images - for image in {"api","job","app","exceptionless"}; do + images=(api job app) + if [ "${{ needs.version.outputs.is_prod_deploy }}" = "true" ]; then + images+=(exceptionless) + fi + + for image in "${images[@]}"; do docker image tag ghcr.io/exceptionless/exceptionless/$image-ci:$STAGING_TAG exceptionless/$image-ci:$VERSION docker image tag ghcr.io/exceptionless/exceptionless/$image-ci:$STAGING_TAG exceptionless/$image-ci:latest docker image tag ghcr.io/exceptionless/exceptionless/$image-ci:$STAGING_TAG ghcr.io/exceptionless/exceptionless/$image-ci:$VERSION @@ -282,49 +275,47 @@ jobs: done - name: Publish Release Packages - if: ${{ env.CAN_PUBLISH_IMAGES == 'true' && startsWith(github.ref, 'refs/tags/v') && github.event_name != 'pull_request' }} + if: ${{ needs.version.outputs.is_prod_deploy == 'true' }} run: | echo "::remove-matcher owner=csc::" # tag and push docker images - for image in {"api","job","app","exceptionless"}; do + images=(api job app exceptionless) + + for image in "${images[@]}"; do docker image tag ghcr.io/exceptionless/exceptionless/$image-ci:$STAGING_TAG exceptionless/$image:$VERSION docker image tag ghcr.io/exceptionless/exceptionless/$image-ci:$STAGING_TAG exceptionless/$image:latest docker image push --all-tags exceptionless/$image done + - name: Summary + if: ${{ always() }} + run: | + echo "### Build Complete" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **Version**: $VERSION" >> $GITHUB_STEP_SUMMARY + echo "- **CI Images**: Published ✅" >> $GITHUB_STEP_SUMMARY + echo "- **Release Images**: ${{ needs.version.outputs.is_prod_deploy == 'true' && 'Published ✅' || 'Skipped' }}" >> $GITHUB_STEP_SUMMARY + echo "- **Deploy**: ${{ needs.version.outputs.is_dev_deploy == 'true' && 'Development queued' || (needs.version.outputs.is_prod_deploy == 'true' && 'Production queued' || 'Skipped') }}" >> $GITHUB_STEP_SUMMARY + deploy: - if: >- - ${{ - (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) - && github.event_name != 'pull_request' - || (github.event_name == 'pull_request' && github.head_ref == vars.DEV_DEPLOY_BRANCH && vars.DEV_DEPLOY_BRANCH != 'main' && vars.DEV_DEPLOY_BRANCH != '') - }} + if: ${{ needs.version.outputs.should_publish == 'true' }} needs: [version, docker-publish] runs-on: ubuntu-latest timeout-minutes: 30 env: - VERSION: ${{needs.version.outputs.version}} + VERSION: ${{ needs.version.outputs.version }} steps: - name: Checkout uses: actions/checkout@v6 - with: - fetch-depth: 0 - name: Install Helm - if: ${{ env.CAN_PUBLISH_IMAGES == 'true' }} uses: azure/setup-helm@v5 - name: Deploy Changes to Development Environment - if: >- - ${{ - env.CAN_PUBLISH_IMAGES == 'true' && ( - (github.event_name == 'push' && github.ref == format('refs/heads/{0}', vars.DEV_DEPLOY_BRANCH || 'main')) - || (github.event_name == 'pull_request' && github.head_ref == vars.DEV_DEPLOY_BRANCH && vars.DEV_DEPLOY_BRANCH != 'main' && vars.DEV_DEPLOY_BRANCH != '') - ) - }} + if: ${{ needs.version.outputs.is_dev_deploy == 'true' }} run: | az login --service-principal --username ${{ secrets.AZ_USERNAME }} --password ${{ secrets.AZ_PASSWORD }} --tenant ${{ secrets.AZ_TENANT }} --output none az aks get-credentials --resource-group exceptionless-v6 --name ex-k8s-v6 @@ -332,9 +323,27 @@ jobs: helm upgrade --set "version=${VERSION}" --reuse-values --values ./k8s/ex-dev-values.yaml ex-dev --namespace ex-dev ./k8s/exceptionless - name: Deploy Changes to Production Environment - if: ${{ env.CAN_PUBLISH_IMAGES == 'true' && startsWith(github.ref, 'refs/tags/v') && github.event_name != 'pull_request' }} + if: ${{ needs.version.outputs.is_prod_deploy == 'true' }} run: | az login --service-principal --username ${{ secrets.AZ_USERNAME }} --password ${{ secrets.AZ_PASSWORD }} --tenant ${{ secrets.AZ_TENANT }} --output none az aks get-credentials --resource-group exceptionless-v6 --name ex-k8s-v6 sed -i "s/^appVersion:.*$/appVersion: '${VERSION}'/" ./k8s/exceptionless/Chart.yaml helm upgrade --set "version=${VERSION}" --reuse-values --values ./k8s/ex-prod-values.yaml ex-prod --namespace ex-prod ./k8s/exceptionless + + - name: Summary + if: ${{ always() }} + run: | + echo "### Deployment Complete" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- **Version**: $VERSION" >> $GITHUB_STEP_SUMMARY + if [ "${{ needs.version.outputs.is_dev_deploy }}" = "true" ]; then + echo "- **Environment**: Development" >> $GITHUB_STEP_SUMMARY + echo "- **App**: https://dev-app.exceptionless.io" >> $GITHUB_STEP_SUMMARY + echo "- **Collector**: https://dev-collector.exceptionless.io" >> $GITHUB_STEP_SUMMARY + elif [ "${{ needs.version.outputs.is_prod_deploy }}" = "true" ]; then + echo "- **Environment**: Production" >> $GITHUB_STEP_SUMMARY + echo "- **App**: https://be.exceptionless.io" >> $GITHUB_STEP_SUMMARY + echo "- **Collector**: https://collector.exceptionless.io" >> $GITHUB_STEP_SUMMARY + else + echo "- **Deployment**: Skipped" >> $GITHUB_STEP_SUMMARY + fi diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index f31ef7c8a5..99de9e2946 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -28,16 +28,23 @@ jobs: dotnet-version: 10.0.* dotnet-quality: ga - - name: Start Services - working-directory: docker - run: docker compose up -d elasticsearch & + - name: Install Aspire CLI + run: | + export PATH="$HOME/.dotnet/tools:$PATH" + if dotnet tool list --global | grep -Eiq '^aspire\.cli[[:space:]]'; then + dotnet tool update --global Aspire.Cli --version 13.2.4 + else + dotnet tool install --global Aspire.Cli --version 13.2.4 + fi + echo "$HOME/.dotnet/tools" >> "$GITHUB_PATH" + aspire --version - uses: actions/cache@v5 with: - path: ~/.nuget/packages - key: nuget-${{ runner.os }}-${{ hashFiles('**/packages.lock.json') }} - restore-keys: | - nuget-${{ runner.os }}- + path: ~/.nuget/packages + key: nuget-${{ runner.os }}-${{ hashFiles('**/packages.lock.json') }} + restore-keys: | + nuget-${{ runner.os }}- - name: Nuget Restore run: dotnet restore diff --git a/.github/workflows/dev-environment-start.yml b/.github/workflows/dev-environment-start.yml index 556b3a83d1..3220c2bb5f 100644 --- a/.github/workflows/dev-environment-start.yml +++ b/.github/workflows/dev-environment-start.yml @@ -3,17 +3,17 @@ name: Start Dev Environment on: workflow_dispatch: inputs: - auto_stop_hours: - description: "Automatically stop after N hours (0 to disable)" + auto_stop_days: + description: "Automatically stop after N days" required: false - default: "8" + default: "1" type: choice options: - - "0" - - "4" - - "8" - - "24" - - "48" + - "1" + - "2" + - "3" + - "5" + - "7" permissions: contents: read @@ -79,7 +79,8 @@ jobs: - name: Record Start Time run: | kubectl annotate namespace $NAMESPACE exceptionless.io/dev-started-at="$(date -u +%Y-%m-%dT%H:%M:%SZ)" --overwrite - kubectl annotate namespace $NAMESPACE exceptionless.io/dev-auto-stop-hours="${{ inputs.auto_stop_hours }}" --overwrite + kubectl annotate namespace $NAMESPACE exceptionless.io/dev-auto-stop-days="${{ inputs.auto_stop_days }}" --overwrite + kubectl annotate namespace $NAMESPACE exceptionless.io/dev-auto-stop-hours- --overwrite 2>/dev/null || true - name: Summary run: | @@ -88,4 +89,4 @@ jobs: echo "- **App**: https://dev-app.exceptionless.io" >> $GITHUB_STEP_SUMMARY echo "- **Collector**: https://dev-collector.exceptionless.io" >> $GITHUB_STEP_SUMMARY echo "- **Kibana**: \`kubectl port-forward -n ex-dev svc/ex-dev-kb-http 5601\` → http://localhost:5601" >> $GITHUB_STEP_SUMMARY - echo "- **Auto-stop**: ${{ inputs.auto_stop_hours }}h (0 = disabled)" >> $GITHUB_STEP_SUMMARY + echo "- **Auto-stop**: ${{ inputs.auto_stop_days }} day(s)" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/dev-environment-stop.yml b/.github/workflows/dev-environment-stop.yml index dbad0b4e0a..ef7f60fef6 100644 --- a/.github/workflows/dev-environment-stop.yml +++ b/.github/workflows/dev-environment-stop.yml @@ -3,8 +3,11 @@ name: Stop Dev Environment on: workflow_dispatch: schedule: - # Run every 2 hours — checks if auto-stop threshold has been exceeded - - cron: "0 */2 * * *" + # GitHub Actions schedules are UTC-only. These entries keep the workflow aligned + # with 5am America/Chicago while limiting duplicate runs to DST transition months. + - cron: "0 10 * 4-10 *" + - cron: "0 11 * 12,1,2 *" + - cron: "0 10,11 * 3,11 *" permissions: contents: read @@ -13,7 +16,7 @@ env: CLUSTER_NAME: ex-k8s-v6 RESOURCE_GROUP: exceptionless-v6 NAMESPACE: ex-dev - AUTO_STOP_HOURS: 8 + AUTO_STOP_DAYS: 1 jobs: stop: @@ -22,14 +25,30 @@ jobs: - name: Checkout uses: actions/checkout@v6 + - name: Check Schedule Window + id: schedule_window + if: ${{ github.event_name == 'schedule' }} + run: | + CURRENT_HOUR=$(TZ=America/Chicago date +%H) + if [ "$CURRENT_HOUR" = "05" ]; then + echo "Running scheduled stop during the 5am America/Chicago window." + echo "should_run=true" >> $GITHUB_OUTPUT + else + echo "Skipping scheduled stop outside the 5am America/Chicago window." + echo "should_run=false" >> $GITHUB_OUTPUT + fi + - name: Azure Login + if: ${{ github.event_name == 'workflow_dispatch' || steps.schedule_window.outputs.should_run == 'true' }} run: az login --service-principal --username ${{ secrets.AZ_USERNAME }} --password ${{ secrets.AZ_PASSWORD }} --tenant ${{ secrets.AZ_TENANT }} --output none - name: Get AKS Credentials + if: ${{ github.event_name == 'workflow_dispatch' || steps.schedule_window.outputs.should_run == 'true' }} run: az aks get-credentials --resource-group $RESOURCE_GROUP --name $CLUSTER_NAME --overwrite-existing - name: Check if Environment is Running id: check + if: ${{ github.event_name == 'workflow_dispatch' || steps.schedule_window.outputs.should_run == 'true' }} run: | REPLICAS=$(kubectl get deployment/ex-dev-app --namespace $NAMESPACE -o jsonpath='{.spec.replicas}' 2>/dev/null || echo "0") echo "app_replicas=$REPLICAS" >> $GITHUB_OUTPUT @@ -44,7 +63,7 @@ jobs: - name: Check Auto-Stop Threshold id: auto_stop - if: ${{ github.event_name == 'schedule' && steps.check.outputs.running == 'true' }} + if: ${{ github.event_name == 'schedule' && steps.schedule_window.outputs.should_run == 'true' && steps.check.outputs.running == 'true' }} run: | STARTED_AT=$(kubectl get namespace $NAMESPACE -o jsonpath='{.metadata.annotations.exceptionless\.io/dev-started-at}' 2>/dev/null || echo "") @@ -56,29 +75,46 @@ jobs: STARTED_EPOCH=$(date -d "$STARTED_AT" +%s) NOW_EPOCH=$(date +%s) - ELAPSED_HOURS=$(( (NOW_EPOCH - STARTED_EPOCH) / 3600 )) - - echo "Started at: $STARTED_AT ($ELAPSED_HOURS hours ago)" + ELAPSED_DAYS=$(( (NOW_EPOCH - STARTED_EPOCH) / 86400 )) + + echo "Started at: $STARTED_AT ($ELAPSED_DAYS days ago)" + + # Check the retention days from the start annotation, fall back to default. + AUTO_DAYS=$(kubectl get namespace $NAMESPACE -o jsonpath='{.metadata.annotations.exceptionless\.io/dev-auto-stop-days}' 2>/dev/null || echo "") + if [ -z "$AUTO_DAYS" ]; then + LEGACY_AUTO_HOURS=$(kubectl get namespace $NAMESPACE -o jsonpath='{.metadata.annotations.exceptionless\.io/dev-auto-stop-hours}' 2>/dev/null || echo "") + if [ -n "$LEGACY_AUTO_HOURS" ]; then + if [ "$LEGACY_AUTO_HOURS" = "0" ]; then + AUTO_DAYS="$AUTO_STOP_DAYS" + else + AUTO_DAYS=$(( (LEGACY_AUTO_HOURS + 23) / 24 )) + fi + else + AUTO_DAYS="$AUTO_STOP_DAYS" + fi + fi - # Check the auto-stop hours from the start annotation, fall back to default - AUTO_HOURS=$(kubectl get namespace $NAMESPACE -o jsonpath='{.metadata.annotations.exceptionless\.io/dev-auto-stop-hours}' 2>/dev/null || echo "$AUTO_STOP_HOURS") - if [ -z "$AUTO_HOURS" ] || [ "$AUTO_HOURS" = "0" ]; then - echo "Auto-stop disabled for this session." - echo "should_stop=false" >> $GITHUB_OUTPUT - exit 0 + if ! [[ "$AUTO_DAYS" =~ ^[0-9]+$ ]] || [ "$AUTO_DAYS" -lt 1 ]; then + echo "Invalid auto-stop retention '$AUTO_DAYS'. Falling back to default of $AUTO_STOP_DAYS day(s)." + AUTO_DAYS="$AUTO_STOP_DAYS" fi - if [ "$ELAPSED_HOURS" -ge "$AUTO_HOURS" ]; then - echo "Auto-stop threshold exceeded ($ELAPSED_HOURS >= $AUTO_HOURS hours). Stopping." + if [ "$ELAPSED_DAYS" -ge "$AUTO_DAYS" ]; then + echo "Auto-stop threshold exceeded ($ELAPSED_DAYS >= $AUTO_DAYS days). Stopping." echo "should_stop=true" >> $GITHUB_OUTPUT else - echo "Within auto-stop window ($ELAPSED_HOURS < $AUTO_HOURS hours). Keeping alive." + echo "Within auto-stop window ($ELAPSED_DAYS < $AUTO_DAYS days). Keeping alive." echo "should_stop=false" >> $GITHUB_OUTPUT fi - name: Skip (Not Time Yet) - if: ${{ github.event_name == 'schedule' && steps.auto_stop.outputs.should_stop != 'true' }} - run: echo "Skipping — environment not running or within auto-stop window." + if: ${{ github.event_name == 'schedule' && (steps.schedule_window.outputs.should_run != 'true' || steps.auto_stop.outputs.should_stop != 'true') }} + run: | + if [ "${{ steps.schedule_window.outputs.should_run }}" != "true" ]; then + echo "Skipping — outside the 5am America/Chicago window." + else + echo "Skipping — environment not running or within auto-stop window." + fi - name: Stop Deployments if: ${{ github.event_name == 'workflow_dispatch' || steps.auto_stop.outputs.should_stop == 'true' }} @@ -116,6 +152,8 @@ jobs: if: ${{ github.event_name == 'workflow_dispatch' || steps.auto_stop.outputs.should_stop == 'true' }} run: | kubectl annotate namespace $NAMESPACE exceptionless.io/dev-started-at- --overwrite 2>/dev/null || true + kubectl annotate namespace $NAMESPACE exceptionless.io/dev-auto-stop-days- --overwrite 2>/dev/null || true + kubectl annotate namespace $NAMESPACE exceptionless.io/dev-auto-stop-hours- --overwrite 2>/dev/null || true - name: Summary if: ${{ github.event_name == 'workflow_dispatch' || steps.auto_stop.outputs.should_stop == 'true' }} diff --git a/.vscode/launch.json b/.vscode/launch.json index 90a926b9f8..c36c3d60d8 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -61,7 +61,7 @@ "type": "msedge", "request": "launch", "name": "frontend: Web (Edge)", - "url": "http://localhost:5173/next", + "url": "https://web-ex.dev.localhost:7131/next", "webRoot": "${workspaceFolder}/src/Exceptionless.Web/ClientApp", "preLaunchTask": "npm run dev" }, @@ -69,7 +69,7 @@ "type": "msedge", "request": "launch", "name": "frontend: Web Dev Api (Edge)", - "url": "http://localhost:5173/next", + "url": "https://web-ex.dev.localhost:7131/next", "webRoot": "${workspaceFolder}/src/Exceptionless.Web/ClientApp", "preLaunchTask": "npm run dev:api" }, @@ -85,7 +85,7 @@ "type": "chrome", "request": "launch", "name": "frontend: Web (Chrome)", - "url": "http://localhost:5173/next", + "url": "https://web-ex.dev.localhost:7131/next", "webRoot": "${workspaceFolder}/src/Exceptionless.Web/ClientApp", "preLaunchTask": "npm run dev" }, @@ -93,7 +93,7 @@ "type": "chrome", "request": "launch", "name": "frontend: Web Dev Api (Chrome)", - "url": "http://localhost:5173/next", + "url": "https://web-ex.dev.localhost:7131/next", "webRoot": "${workspaceFolder}/src/Exceptionless.Web/ClientApp", "preLaunchTask": "npm run dev:api" }, diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 7357d3923e..9518b7c25a 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -55,36 +55,6 @@ }, "problemMatcher": "$msCompile" }, - { - "label": "Start Elasticsearch", - "command": "docker", - "type": "shell", - "args": [ - "compose", - "up", - "elasticsearch", - "kibana" - ], - "options": { - "cwd": "docker" - }, - "isBackground": true, - "problemMatcher": [] - }, - { - "label": "Stop Elasticsearch", - "command": "docker", - "type": "shell", - "args": [ - "compose", - "down" - ], - "options": { - "cwd": "docker" - }, - "isBackground": true, - "problemMatcher": [] - }, { "label": "build", "command": "dotnet", diff --git a/AGENTS.md b/AGENTS.md index c454ef32d2..0743ad6e17 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,22 +2,30 @@ Real-time error monitoring platform handling billions of requests (ASP.NET Core 10 + Svelte 5). Act as a distinguished engineer focusing on readability, performance while maintaining backwards compatibility. -## Quick Start +## Start Here -Start everything with the Aspire CLI: `aspire run --project src/Exceptionless.AppHost`. This launches all services (Elasticsearch, Redis, API, Job worker) and opens the Aspire dashboard. Alternatively, run `Exceptionless.AppHost` directly from your IDE. +- Start everything with the Aspire CLI: `aspire run`, or run `Exceptionless.AppHost` directly from your IDE. +- The AppHost launches required services (Elasticsearch, Redis, API, Job worker) and opens the Aspire dashboard. Integration tests bootstrap their own infrastructure. -## Build & Test +## Common Commands -| Task | Command | -| -------------- | --------------------------------------------------------------- | -| Run (Aspire) | `aspire run --project src/Exceptionless.AppHost` | -| Backend build | `dotnet build` | -| Backend test | `dotnet test` | -| Frontend build | `cd src/Exceptionless.Web/ClientApp && npm ci && npm run build` | -| Frontend test | `npm run test:unit` | -| E2E test | `npm run test:e2e` | +| Task | Command | +| ------------------- | --------------------------------------------------------------- | +| Run (Aspire) | `aspire run` | +| Backend build | `dotnet build` | +| Backend test | `dotnet test` | +| Frontend build | `cd src/Exceptionless.Web/ClientApp && npm ci && npm run build` | +| Frontend unit tests | `cd src/Exceptionless.Web/ClientApp && npm run test:unit` | +| Frontend E2E tests | `cd src/Exceptionless.Web/ClientApp && npm run test:e2e` | -Test filtering note: the backend test project uses Microsoft Testing Platform, so targeted runs use test-app options after `--`, for example `dotnet test -- --filter-class Exceptionless.Tests.Controllers.EventControllerTests`. +## Repo-Specific Notes + +- Backend test filtering uses Microsoft Testing Platform test-app options after `--`, for example `dotnet test -- --filter-class Exceptionless.Tests.Controllers.EventControllerTests`. +- Elasticsearch-backed repository or job tests should derive from `IntegrationTestsBase`, not `TestWithServices`. +- The current main site UI is the legacy Angular app in `src/Exceptionless.Web/ClientApp.angular`; the folders you will usually touch there are `app/`, `components/`, `less/`, `img/`, `lang/`, and `grunt/`. +- The Svelte 5 UI in `src/Exceptionless.Web/ClientApp` is still under development. +- Standard pull requests build `api`, `job`, and `app` images. The all-in-one `exceptionless` image is only built for tags. +- If you touch Docker publish stages that use `dotnet publish --no-build`, make sure the stage still has the build output and NuGet package cache available. ## Project Structure @@ -26,7 +34,9 @@ src/ ├── Exceptionless.AppHost # Aspire orchestrator (start here) ├── Exceptionless.Core # Domain logic ├── Exceptionless.Insulation # Infrastructure (Elasticsearch, Redis, Azure) -├── Exceptionless.Web # API + Svelte SPA (ClientApp/) +├── Exceptionless.Web # API host +│ ├── ClientApp.angular/ # Legacy Angular UI that still powers the main site +│ └── ClientApp/ # Svelte 5 UI that is still under development └── Exceptionless.Job # Background workers tests/ # C# tests + HTTP samples ``` @@ -49,4 +59,4 @@ Available in `.claude/agents/`. Use `@agent-name` to invoke: - **Backwards compatibility:** Never break existing public APIs, WebSocket message formats, config keys, or exported library interfaces without explicit user approval. Call out any breaking change as a BLOCKER in reviews. - **API test files:** Update `tests/http/*.http` files whenever endpoints change (new, modified, or removed). - **PR descriptions:** When creating a PR, fill out any existing PR template. Provide concise context: what changed, why, new APIs/features/behaviors, and any breaking changes. No essays — just enough for reviewers to understand the value and impact. -- **App URL for QA:** `http://localhost:5200` — probe `/api/v2/about` for health check. +- **App URL for QA:** `http://localhost:7110` — probe `/api/v2/about` for health check. diff --git a/Dockerfile b/Dockerfile index 4032759fc9..cc7554fee1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,24 +2,28 @@ FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build ARG MinVerVersionOverride WORKDIR /app -COPY ./*.slnx ./NuGet.Config ./ +COPY ./NuGet.Config ./ COPY ./src/*.props ./src/ -COPY ./tests/*.props ./tests/ COPY ./build/packages/* ./build/packages/ -# Copy the main source project files +# Copy the source project files needed for the runtime images. COPY src/*/*.csproj ./ RUN for file in $(ls *.csproj); do mkdir -p src/${file%.*}/ && mv $file src/${file%.*}/; done -# Copy the test project files -COPY tests/*/*.csproj ./ -RUN for file in $(ls *.csproj); do mkdir -p tests/${file%.*}/ && mv $file tests/${file%.*}/; done +RUN dotnet restore ./src/Exceptionless.Web/Exceptionless.Web.csproj \ + && dotnet restore ./src/Exceptionless.Job/Exceptionless.Job.csproj -RUN dotnet restore - -# Copy everything else and build app +# Copy everything else and build the source projects once. COPY . . -RUN dotnet build -c Release /p:MinVerVersionOverride=${MinVerVersionOverride} +RUN dotnet build ./src/Exceptionless.Web/Exceptionless.Web.csproj -c Release --no-restore /p:MinVerVersionOverride=${MinVerVersionOverride} \ + && dotnet build ./src/Exceptionless.Job/Exceptionless.Job.csproj -c Release --no-restore /p:MinVerVersionOverride=${MinVerVersionOverride} + +FROM build AS build-node +RUN apt-get update -yq \ + && apt-get install -yq curl ca-certificates \ + && curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \ + && apt-get install -yq nodejs \ + && rm -rf /var/lib/apt/lists/* # testrunner @@ -32,7 +36,7 @@ ENTRYPOINT ["dotnet", "test", "--results-directory", "/app/artifacts", "--logger FROM build AS job-publish WORKDIR /app/src/Exceptionless.Job -RUN dotnet publish -c Release -o out +RUN dotnet publish -c Release -o out --no-build # job @@ -49,7 +53,7 @@ ENTRYPOINT [ "dotnet", "Exceptionless.Job.dll" ] FROM build AS api-publish WORKDIR /app/src/Exceptionless.Web -RUN dotnet publish -c Release -o out /p:SkipSpaPublish=true +RUN dotnet publish -c Release -o out --no-build /p:SkipSpaPublish=true # api @@ -63,13 +67,9 @@ ENTRYPOINT [ "dotnet", "Exceptionless.Web.dll" ] # app-publish -FROM build AS app-publish +FROM build-node AS app-publish WORKDIR /app/src/Exceptionless.Web - -RUN apt-get update -yq -RUN curl -sL https://deb.nodesource.com/setup_22.x | bash - && apt-get install -yq nodejs - -RUN dotnet publish -c Release -o out +RUN dotnet publish -c Release -o out --no-build # app diff --git a/README.md b/README.md index b34487973a..6bc613c881 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,8 @@ We provide very reasonably priced hosting at [Exceptionless](https://exceptionle Exceptionless can easily be run locally using Docker: -- `docker run --rm -it -p 5200:8080 exceptionless/exceptionless:latest` -- Open `http://localhost:5200` +- `docker run --rm -it -p 7110:8080 exceptionless/exceptionless:latest` +- Open `http://localhost:7110` - Create an account. The first account in the system will automatically be an admin. This will run a completely self-contained simple instance of Exceptionless. It is only suitable for testing purposes since it will not persist data. For more complete setups, check out the [self hosting documentation](https://exceptionless.com/docs/self-hosting/). Also, if you want to support the project while self hosting you can send us a pull request or [donation](https://github.com/sponsors/exceptionless). @@ -29,33 +29,35 @@ This will run a completely self-contained simple instance of Exceptionless. It i _In appreciation for anyone who submits a non-trivial pull request, we will give you a free [Exceptionless](https://exceptionless.com) paid plan for a year. After your pull request is accepted, simply send an email to with the name of your organization and we will upgrade you to a paid plan._ -- Please read the [contributing document](https://github.com/exceptionless/Exceptionless/blob/main/CONTRIBUTING.md) -- Requirements - - [Docker](https://www.docker.com/get-docker) - - [.NET 10.0](https://dotnet.microsoft.com/) - - [Node 24+](https://nodejs.org/) -- Visual Studio Code - - Open Visual Studio Code and then open the Exceptionless root folder - - Go to the `Terminal` menu and select `Run Task...` and then select `Start Elasticsearch` (you can stop the service when you are done using the `Stop Elasticsearch` task) - - Go to the `Debug` menu and select the `Web` launch configuration then click the `Start Debugging` button - - A browser window should be automatically opened to `https://localhost:5100/` - - When running locally in `Development` mode, a global administrator user `test@localhost` is automatically created with password `tester`. You can also click the `Signup` button to create a new account -- Visual Studio - - Open Visual Studio and then open the `Exceptionless.slnx` solution in the root folder - - Start Elasticsearch by either configuring multiple startup projects for the `docker-compose` and `Exceptionless.Web` projects or by running the `start-services.ps1` script in the root folder - - Run the `Exceptionless.Web` project - - A browser window should be automatically opened to `https://localhost:5100/` - - When running locally in `Development` mode, a global administrator user `test@localhost` is automatically created with password `tester`. You can also click the `Signup` button to create a new account +Start here: + +1. Read the [contributing document](https://github.com/exceptionless/Exceptionless/blob/main/CONTRIBUTING.md). +2. Install [Docker](https://www.docker.com/get-docker), [.NET 10.0](https://dotnet.microsoft.com/), and [Node 24+](https://nodejs.org/). +3. Run the app with one of these entry points: + - Visual Studio Code: open the repo root and start the `Aspire` launch configuration. + - Visual Studio: open `Exceptionless.slnx`, set `Exceptionless.AppHost` as the startup project, and run it. + - CLI or Dev Container: run `aspire run` from the repo root. + +After startup: + +1. Open `https://localhost:7121/` if a browser does not open automatically. +2. In `Development` mode, a global administrator user `test@localhost` with password `tester` is created automatically. + +Notes: + +1. Running `Exceptionless.AppHost` starts the app and required infrastructure together. +2. Backend tests bootstrap required infrastructure automatically. ![image](https://user-images.githubusercontent.com/282584/223168564-6518d509-d292-4078-a61f-ab493d2bb812.png) -## UI Only Development +## UI Development -The UI is a SPA application that runs against the Exceptionless API. The source is located in the `src/Exceptionless.Web/ClientApp` folder. The UI will automatically be started when running the whole project, but if you want to work on just the UI, then open Visual Studio Code to the `src/Exceptionless.Web/ClientApp` folder and run the `npm run serve (use exceptionless api)` task to start the UI pointing at the official Exceptionless API. You will need to login to your actual Exceptionless account. +Frontend work currently spans two apps: -## API Only Development +1. The legacy Angular UI in `src/Exceptionless.Web/ClientApp.angular` is still the main site UI. Most of that app lives in `app/`, `components/`, `less/`, `img/`, `lang/`, and `grunt/`. +2. The Svelte 5 UI in `src/Exceptionless.Web/ClientApp` is still under development. -You can work on just the API without running the SPA UI by selecting the `Exceptionless API` launch configuration in Visual Studio. You can then run requests using the `exceptionless.http` file. Make sure that you have the [REST Client](https://marketplace.visualstudio.com/items?itemName=humao.rest-client) extension installed. +For examples of API requests, see `exceptionless.http`. If you use that file in Visual Studio Code, install the [REST Client](https://marketplace.visualstudio.com/items?itemName=humao.rest-client) extension. ## Thanks diff --git a/aspire.config.json b/aspire.config.json new file mode 100644 index 0000000000..5404ab86a6 --- /dev/null +++ b/aspire.config.json @@ -0,0 +1,5 @@ +{ + "appHost": { + "path": "src/Exceptionless.AppHost/Exceptionless.AppHost.csproj" + } +} \ No newline at end of file diff --git a/build/Apache/Stress.ps1 b/build/Apache/Stress.ps1 index 5a23874812..8fe4aaa1ce 100644 --- a/build/Apache/Stress.ps1 +++ b/build/Apache/Stress.ps1 @@ -1 +1 @@ -.\ab.exe -c 10 -n 50000 -p events.json -T application/json -A client:LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw http://localhost:5200/api/v2/events \ No newline at end of file +.\ab.exe -c 10 -n 50000 -p events.json -T application/json -A client:LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw http://localhost:7110/api/v2/events \ No newline at end of file diff --git a/build/Apache/StressHeartbeat.sh b/build/Apache/StressHeartbeat.sh index d326149b3f..429b202577 100644 --- a/build/Apache/StressHeartbeat.sh +++ b/build/Apache/StressHeartbeat.sh @@ -1 +1 @@ -wrk -t 12 -c 400 -d 30s -H "Authorization: Bearer LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw" http://localhost:5200/api/v2/events/session/heartbeat?id=test \ No newline at end of file +wrk -t 12 -c 400 -d 30s -H "Authorization: Bearer LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw" http://localhost:7110/api/v2/events/session/heartbeat?id=test \ No newline at end of file diff --git a/build/Apache/StressProjectConfig.sh b/build/Apache/StressProjectConfig.sh index c6a5832af6..99a9b2d24e 100644 --- a/build/Apache/StressProjectConfig.sh +++ b/build/Apache/StressProjectConfig.sh @@ -1 +1 @@ -wrk -t 12 -c 400 -d 30s -H "Authorization: Bearer LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw" http://localhost:5200/api/v2/projects/config\?v\=1 \ No newline at end of file +wrk -t 12 -c 400 -d 30s -H "Authorization: Bearer LhhP1C9gijpSKCslHHCvwdSIz298twx271n1l6xw" http://localhost:7110/api/v2/projects/config\?v\=1 \ No newline at end of file diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index 5941a23499..a774f40b6c 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -18,13 +18,13 @@ services: EX_ConnectionStrings__Queue: provider=redis EX_ConnectionStrings__Redis: redis,abortConnect=false #ASPNETCORE_URLS: http://+;https://+ - #ASPNETCORE_HTTPS_PORT: 5201 + #ASPNETCORE_HTTPS_PORT: 7111 #ASPNETCORE_Kestrel__Certificates__Default__Password: password #ASPNETCORE_Kestrel__Certificates__Default__Path: /https/aspnetapp.pfx EX_RunJobsInProcess: "false" ports: - - 5200:80 - - 5201:443 + - 7110:80 + - 7111:443 volumes: - appdata:/app/storage - ssldata:/https @@ -38,7 +38,7 @@ services: target: job environment: EX_AppMode: Production - EX_BaseURL: http://localhost:5200 + EX_BaseURL: http://localhost:7110 EX_ConnectionStrings__Cache: provider=redis EX_ConnectionStrings__Elasticsearch: server=http://elasticsearch:9200 EX_ConnectionStrings__Email: smtp://localhost:1025 @@ -60,6 +60,16 @@ services: - 9300:9300 volumes: - esdata7:/usr/share/elasticsearch/data + healthcheck: + test: + [ + "CMD-SHELL", + 'curl -fsS "http://localhost:9200/_cluster/health?wait_for_status=yellow&timeout=1s" > /dev/null', + ] + interval: 10s + timeout: 10s + retries: 30 + start_period: 20s kibana: depends_on: diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 7cd75e1f57..4ff6a5a1ef 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -12,6 +12,16 @@ services: - 9200:9200 volumes: - esdata:/usr/share/elasticsearch/data + healthcheck: + test: + [ + "CMD-SHELL", + 'curl -fsS "http://localhost:9200/_cluster/health?wait_for_status=yellow&timeout=1s" > /dev/null', + ] + interval: 10s + timeout: 10s + retries: 30 + start_period: 20s kibana: depends_on: diff --git a/exceptionless.http b/exceptionless.http index d1e044d5d6..04240e626e 100644 --- a/exceptionless.http +++ b/exceptionless.http @@ -1,6 +1,6 @@ # Additonal http collections can be found in the tests/http folder. -@apiUrl = http://localhost:5200/api/v2 +@apiUrl = http://localhost:7110/api/v2 @email = test@localhost @password = tester diff --git a/global.json b/global.json index ceedb4c7af..ab000ad016 100644 --- a/global.json +++ b/global.json @@ -1,9 +1,10 @@ { - "sdk": { - "version": "10.0.100", - "rollForward": "latestMinor" - }, - "test": { - "runner": "Microsoft.Testing.Platform" - } + "sdk": { + "version": "10.0.100", + "rollForward": "latestMinor", + "allowPrerelease": false + }, + "test": { + "runner": "Microsoft.Testing.Platform" + } } diff --git a/samples/docker-compose.all-in-one.yml b/samples/docker-compose.all-in-one.yml index f660acfe1f..f67515eff1 100644 --- a/samples/docker-compose.all-in-one.yml +++ b/samples/docker-compose.all-in-one.yml @@ -4,12 +4,12 @@ services: exceptionless: image: exceptionless/exceptionless:latest ports: - - 5200:8080 + - 7110:8080 - 9200:9200 environment: EX_AppMode: Production #ASPNETCORE_URLS: http://+;https://+ - #ASPNETCORE_HTTPS_PORT: 5201 + #ASPNETCORE_HTTPS_PORT: 7111 #ASPNETCORE_Kestrel__Certificates__Default__Password: password #ASPNETCORE_Kestrel__Certificates__Default__Path: /https/aspnetapp.pfx volumes: diff --git a/samples/docker-compose.yml b/samples/docker-compose.yml index 8b4dd544f4..907da0eeb0 100644 --- a/samples/docker-compose.yml +++ b/samples/docker-compose.yml @@ -14,14 +14,14 @@ services: EX_ConnectionStrings__Queue: provider=redis EX_ConnectionStrings__Redis: redis,abortConnect=false EX_ConnectionStrings__Storage: provider=folder;path=/app/storage - #ASPNETCORE_HTTPS_PORT: 5201 + #ASPNETCORE_HTTPS_PORT: 7111 #ASPNETCORE_URLS: http://+ #ASPNETCORE_URLS: http://+;https://+ #ASPNETCORE_Kestrel__Certificates__Default__Password: password #ASPNETCORE_Kestrel__Certificates__Default__Path: /https/aspnetapp.pfx EX_RunJobsInProcess: "false" ports: - - 5200:8080 + - 7110:8080 volumes: - ex_appdata:/app/storage - ex_ssldata:/https @@ -32,7 +32,7 @@ services: image: exceptionless/job:latest environment: EX_AppMode: Production - EX_BaseURL: http://localhost:5200 + EX_BaseURL: http://localhost:7110 EX_ConnectionStrings__Cache: provider=redis EX_ConnectionStrings__Elasticsearch: server=http://elasticsearch:9200 #EX_ConnectionStrings__Email: smtps://user:password@smtp.host.com:587 diff --git a/samples/hosting-samples.sh b/samples/hosting-samples.sh index ebaf11d643..da6061bf66 100644 --- a/samples/hosting-samples.sh +++ b/samples/hosting-samples.sh @@ -1,18 +1,18 @@ #!/bin/bash # simple instance for testing -docker run --rm -it -p 5200:8080 exceptionless/exceptionless:latest +docker run --rm -it -p 7110:8080 exceptionless/exceptionless:latest # persist data -docker run --rm -it -p 5200:8080 \ +docker run --rm -it -p 7110:8080 \ -v ~/esdata:/usr/share/elasticsearch/data \ exceptionless/exceptionless:latest # persist data, use ssl, enable mail sending -docker run --rm -it -p 5200:8080 -p 5201:443 \ +docker run --rm -it -p 7110:8080 -p 7111:443 \ -e EX_ConnectionStrings__Email=smtps://user:password@smtp.host.com:587 \ -e ASPNETCORE_URLS="https://+;http://+" \ - -e ASPNETCORE_HTTPS_PORT=5201 \ + -e ASPNETCORE_HTTPS_PORT=7111 \ -e ASPNETCORE_Kestrel__Certificates__Default__Password="password" \ -e ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx \ -v ~/.aspnet/https:/https/ \ diff --git a/src/Exceptionless.AppHost/Exceptionless.AppHost.csproj b/src/Exceptionless.AppHost/Exceptionless.AppHost.csproj index fe03d3bb18..a5c4e99bcf 100644 --- a/src/Exceptionless.AppHost/Exceptionless.AppHost.csproj +++ b/src/Exceptionless.AppHost/Exceptionless.AppHost.csproj @@ -1,15 +1,16 @@ - + Exe net10.0 enable enable a9c2ddcc-e51d-4cd1-9782-96e1d74eec87 + $(NoWarn);ASPIRECERTIFICATES001 - - - + + + diff --git a/src/Exceptionless.AppHost/Extensions/AspireExtensions.cs b/src/Exceptionless.AppHost/Extensions/AspireExtensions.cs new file mode 100644 index 0000000000..f042d42a7b --- /dev/null +++ b/src/Exceptionless.AppHost/Extensions/AspireExtensions.cs @@ -0,0 +1,22 @@ +using Aspire.Hosting.JavaScript; +using Microsoft.Extensions.Hosting; + +public static class AspireExtensions +{ + // TODO: Remove this once the upstream Aspire bug that attaches a + // SupportsDebuggingAnnotation to JavaScript resources (causing the dashboard + // to surface a non-functional "Debug" command) is fixed. + // See https://github.com/microsoft/aspire/issues/16468 + public static IResourceBuilder RemoveJavaScriptDebuggingAnnotation(this IResourceBuilder resourceBuilder) + where TResource : IResource + { + foreach (var annotation in resourceBuilder.Resource.Annotations + .Where(a => a.GetType().Name == "SupportsDebuggingAnnotation") + .ToArray()) + { + resourceBuilder.Resource.Annotations.Remove(annotation); + } + + return resourceBuilder; + } +} diff --git a/src/Exceptionless.AppHost/Program.cs b/src/Exceptionless.AppHost/Program.cs index 54f136ad95..b4a70b6c47 100644 --- a/src/Exceptionless.AppHost/Program.cs +++ b/src/Exceptionless.AppHost/Program.cs @@ -1,17 +1,26 @@ +using System.Reflection; +using Aspire.Hosting.JavaScript; +using Microsoft.Extensions.Hosting; + var builder = DistributedApplication.CreateBuilder(args); +var servicesOnly = args.Any(arg => StringComparer.OrdinalIgnoreCase.Equals(arg, "--services-only") || StringComparer.OrdinalIgnoreCase.Equals(arg, "services-only")); var elastic = builder.AddElasticsearch("Elasticsearch", port: 9200) - .WithLifetime(ContainerLifetime.Persistent) - .WithContainerName("Exceptionless-Elasticsearch") - .WithDataVolume("exceptionless.data.v1") - .WithKibana(b => b.WithLifetime(ContainerLifetime.Persistent).WithContainerName("Exceptionless-Kibana")); + .WithDataVolume(servicesOnly ? null : "exceptionless.data.v1"); var storage = builder.AddAzureStorage("Storage") .RunAsEmulator(c => { - c.WithLifetime(ContainerLifetime.Persistent); - c.WithContainerName("Exceptionless-Storage"); - c.WithDataVolume(); + c.WithUrlForEndpoint("blob", u => { u.DisplayText = "Blobs"; u.DisplayLocation = UrlDisplayLocation.DetailsOnly; }); + c.WithUrlForEndpoint("queue", u => { u.DisplayText = "Queues"; u.DisplayLocation = UrlDisplayLocation.DetailsOnly; }); + c.WithUrlForEndpoint("table", u => { u.DisplayText = "Tables"; u.DisplayLocation = UrlDisplayLocation.DetailsOnly; }); + + if (!servicesOnly) + { + c.WithLifetime(ContainerLifetime.Persistent); + c.WithContainerName("Exceptionless-Storage"); + c.WithDataVolume(); + } }); var storageBlobs = storage.AddBlobs("StorageBlobs"); @@ -19,58 +28,110 @@ var cache = builder.AddRedis("Redis", port: 6379) .WithImageTag("8.6") - .WithLifetime(ContainerLifetime.Persistent) - .WithContainerName("Exceptionless-Redis") .WithClearCommand() - .WithRedisInsight(b => b.WithLifetime(ContainerLifetime.Persistent).WithContainerName("Exceptionless-RedisInsight").WithUrlForEndpoint("http", u => u.DisplayText = "Cache")); + .WithUrls(c => + { + foreach (var url in c.Urls) + { + url.DisplayLocation = UrlDisplayLocation.DetailsOnly; + } + }); var mail = builder.AddContainer("Mail", "axllent/mailpit") .WithImageTag("v1.27.10") - .WithLifetime(ContainerLifetime.Persistent) - .WithContainerName("Exceptionless-Mail") .WithHttpEndpoint(8025, 8025, "http") - .WithUrlForEndpoint("http", u => u.DisplayText = "Mail") - .WithEndpoint(1025, 1025); + .WithUrlForEndpoint("http", u => { u.DisplayText = "Mail"; u.DisplayOrder = 100; }) + .WithHttpHealthCheck("/readyz") + .WithEndpoint(1025, 1025) + .WithUrlForEndpoint("tcp", u => u.DisplayLocation = UrlDisplayLocation.DetailsOnly); -builder.AddProject("Jobs", "AllJobs") - .WithReference(cache) - .WithReference(elastic) - .WithReference(storageBlobs, "AzureStorage") - .WithReference(storageQueues, "AzureQueues") - .WithEnvironment("ConnectionStrings:Email", "smtp://localhost:1025") - .WaitFor(elastic) - .WaitFor(cache) - .WaitFor(mail) - .WithUrlForEndpoint("http", u => u.DisplayText = "Jobs") - .WithHttpHealthCheck("/health"); +if (!servicesOnly) +{ + elastic = elastic + .WithLifetime(ContainerLifetime.Persistent) + .WithContainerName("Exceptionless-Elasticsearch") + .WithKibana(b => b + .WithLifetime(ContainerLifetime.Persistent) + .WithContainerName("Exceptionless-Kibana") + .WithParentRelationship(elastic)); -var api = builder.AddProject("Api", "Exceptionless") - .WithReference(cache) - .WithReference(elastic) - .WithReference(storageBlobs, "AzureStorage") - .WithReference(storageQueues, "AzureQueues") - .WithEnvironment("ConnectionStrings:Email", "smtp://localhost:1025") - .WithEnvironment("RunJobsInProcess", "false") - .WaitFor(elastic) - .WaitFor(cache) - .WaitFor(mail) - .WithUrlForEndpoint("http", u => u.DisplayText = "Api") - .WithHttpHealthCheck("/health"); + cache = cache + .WithLifetime(ContainerLifetime.Persistent) + .WithContainerName("Exceptionless-Redis") + .WithRedisInsight(b => b + .WithLifetime(ContainerLifetime.Persistent) + .WithContainerName("Exceptionless-RedisInsight") + .WithUrlForEndpoint("http", u => u.DisplayText = "Redis") + .WithParentRelationship(cache), containerName: "Redis-insight"); -builder.AddViteApp("Web", "../../src/Exceptionless.Web/ClientApp") - .WithReference(api) - .WithEnvironment("ASPNETCORE_URLS", "http://localhost:5200") - .WithEndpoint("http", e => - { - e.Port = 5173; - e.IsProxied = false; - }) - .WithUrlForEndpoint("http", u => { u.DisplayText = "Web"; u.Url = "/next/"; }); + mail = mail + .WithLifetime(ContainerLifetime.Persistent) + .WithContainerName("Exceptionless-Mail"); + + var api = builder.AddProject("Api") + .WithReference(cache) + .WithReference(elastic) + .WithReference(storageBlobs, "AzureStorage") + .WithReference(storageQueues, "AzureQueues") + .WithEnvironment("ConnectionStrings:Email", "smtp://localhost:1025") + .WithEnvironment("RunJobsInProcess", "false") + .WaitFor(elastic) + .WaitFor(cache) + .WaitFor(mail) + .WithExternalHttpEndpoints() + .WithUrlForEndpoint("https", u => { u.DisplayText = "Open API"; u.DisplayOrder = 100; }) + .WithUrlForEndpoint("http", u => u.DisplayLocation = UrlDisplayLocation.DetailsOnly) + .WithHttpHealthCheck("/health"); + + builder.AddProject("Jobs", "AllJobs") + .WithReference(cache) + .WithReference(elastic) + .WithReference(storageBlobs, "AzureStorage") + .WithReference(storageQueues, "AzureQueues") + .WithEnvironment("ConnectionStrings:Email", "smtp://localhost:1025") + .WaitFor(elastic) + .WaitFor(cache) + .WaitFor(mail) + .WithUrlForEndpoint("http", u => { u.DisplayText = "Jobs"; u.DisplayLocation = UrlDisplayLocation.DetailsOnly; }) + .WithUrlForEndpoint("https", u => { u.DisplayText = "Jobs"; u.DisplayLocation = UrlDisplayLocation.DetailsOnly; }) + .WithHttpHealthCheck("/health") + .WithParentRelationship(api); + + var oldApp = builder.AddJavaScriptApp("OldApp", "../../src/Exceptionless.Web/ClientApp.angular", "serve") + .WithReference(api) + .RemoveJavaScriptDebuggingAnnotation() + .WithEnvironment("ASPNETCORE_URLS", "http://localhost:7120") + .WithEnvironment("USE_HTTPS", "true") + .WithHttpEndpoint(port: 7121, targetPort: 7121, name: "https", env: "PORT", isProxied: false) + .WithEndpoint("https", e => + { + e.TargetHost = "angular-ex.dev.localhost"; + e.UriScheme = "https"; + }) + .WithHttpsDeveloperCertificate() + .WithUrlForEndpoint("https", u => { u.DisplayText = "Open App (Old)"; u.DisplayOrder = 100; }) + .WithParentRelationship(api); -builder.AddJavaScriptApp("AngularWeb", "../../src/Exceptionless.Web/ClientApp.angular", "serve") - .WithReference(api) - .WithEnvironment("ASPNETCORE_URLS", "http://localhost:5200") - .WithUrlForEndpoint("AngularWeb", u => u.DisplayText = "Angular Web") - .WithHttpEndpoint(port: 5100, targetPort: 5100, name: "AngularWeb", env: "PORT", isProxied: false); + builder.AddViteApp("App", "../Exceptionless.Web/ClientApp") + .WithReference(api) + .WithReference(oldApp) + .RemoveJavaScriptDebuggingAnnotation() + .WithEndpoint("http", e => + { + // 7131 (HTTPS via Aspire dev cert) instead of Vite's default 5173 to avoid clashing with other local Vite projects. + e.Port = 7131; + e.TargetPort = 7131; + e.TargetHost = "web-ex.dev.localhost"; + e.IsProxied = false; + }) + .WithHttpsDeveloperCertificate() + .WithUrlForEndpoint("http", u => + { + u.DisplayText = "Open App"; + u.DisplayOrder = 100; + u.Url = $"{u.Url?.TrimEnd('/')}/next/"; + }) + .WithParentRelationship(api); +} await builder.Build().RunAsync(); diff --git a/src/Exceptionless.AppHost/Properties/launchSettings.json b/src/Exceptionless.AppHost/Properties/launchSettings.json index a657132e91..e72305f8d7 100644 --- a/src/Exceptionless.AppHost/Properties/launchSettings.json +++ b/src/Exceptionless.AppHost/Properties/launchSettings.json @@ -1,11 +1,11 @@ { - "$schema": "https://json.schemastore.org/launchsettings.json", + "$schema": "https://json.schemastore.org/launchsettings.json", "profiles": { "https": { "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, - "applicationUrl": "https://localhost:17056;http://localhost:15161", + "applicationUrl": "https://ex.dev.localhost:7101;http://ex.dev.localhost:7100", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", "DOTNET_ENVIRONMENT": "Development", @@ -17,7 +17,7 @@ "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, - "applicationUrl": "https://localhost:17056;http://localhost:15161", + "applicationUrl": "https://ex.dev.localhost:7101;http://ex.dev.localhost:7100", "environmentVariables": { "EX_ALL": "true", "ASPNETCORE_ENVIRONMENT": "Development", @@ -30,7 +30,7 @@ "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, - "applicationUrl": "http://localhost:15161", + "applicationUrl": "http://ex.dev.localhost:7100", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", "DOTNET_ENVIRONMENT": "Development", diff --git a/src/Exceptionless.Core/Bootstrapper.cs b/src/Exceptionless.Core/Bootstrapper.cs index f264439dca..714a343068 100644 --- a/src/Exceptionless.Core/Bootstrapper.cs +++ b/src/Exceptionless.Core/Bootstrapper.cs @@ -112,6 +112,7 @@ public static void RegisterServices(IServiceCollection services, AppOptions appO handlers.Register(s.GetRequiredService); handlers.Register(s.GetRequiredService); handlers.Register(s.GetRequiredService); + handlers.Register(s.GetRequiredService); return handlers; }); @@ -253,6 +254,7 @@ private static async Task CreateSampleDataAsync(IServiceProvider container) var dataHelper = container.GetRequiredService(); await dataHelper.CreateDataAsync(); + await dataHelper.EnqueueSampleEventsAsync(eventCount: 100, daysBack: 7); } public static void AddHostedJobs(IServiceCollection services, ILoggerFactory loggerFactory) diff --git a/src/Exceptionless.Core/Exceptionless.Core.csproj b/src/Exceptionless.Core/Exceptionless.Core.csproj index ba87b5537c..53b0517ee6 100644 --- a/src/Exceptionless.Core/Exceptionless.Core.csproj +++ b/src/Exceptionless.Core/Exceptionless.Core.csproj @@ -21,6 +21,7 @@ + diff --git a/src/Exceptionless.Core/Jobs/WorkItemHandlers/GenerateSampleEventsWorkItemHandler.cs b/src/Exceptionless.Core/Jobs/WorkItemHandlers/GenerateSampleEventsWorkItemHandler.cs new file mode 100644 index 0000000000..df21d48ffe --- /dev/null +++ b/src/Exceptionless.Core/Jobs/WorkItemHandlers/GenerateSampleEventsWorkItemHandler.cs @@ -0,0 +1,101 @@ +using Exceptionless.Core.Models; +using Exceptionless.Core.Models.WorkItems; +using Exceptionless.Core.Pipeline; +using Exceptionless.Core.Repositories; +using Exceptionless.Core.Utility; +using Foundatio.Jobs; +using Foundatio.Lock; +using Foundatio.Repositories; +using Microsoft.Extensions.Logging; + +namespace Exceptionless.Core.Jobs.WorkItemHandlers; + +public class GenerateSampleEventsWorkItemHandler : WorkItemHandlerBase +{ + private readonly EventPipeline _eventPipeline; + private readonly IOrganizationRepository _organizationRepository; + private readonly IProjectRepository _projectRepository; + private readonly ILockProvider _lockProvider; + private readonly TimeProvider _timeProvider; + + public GenerateSampleEventsWorkItemHandler( + EventPipeline eventPipeline, + IOrganizationRepository organizationRepository, + IProjectRepository projectRepository, + ILockProvider lockProvider, + TimeProvider timeProvider, + ILoggerFactory loggerFactory) : base(loggerFactory) + { + _eventPipeline = eventPipeline; + _organizationRepository = organizationRepository; + _projectRepository = projectRepository; + _lockProvider = lockProvider; + _timeProvider = timeProvider; + } + + public override Task GetWorkItemLockAsync(object workItem, CancellationToken cancellationToken = default) + { + return _lockProvider.AcquireAsync(nameof(GenerateSampleEventsWorkItemHandler), TimeSpan.FromMinutes(30), cancellationToken); + } + + public override async Task HandleItemAsync(WorkItemContext context) + { + var workItem = context.GetData()!; + int eventCount = Math.Clamp(workItem.EventCount, 1, 10000); + int daysBack = Math.Clamp(workItem.DaysBack, 1, 365); + + Log.LogInformation("Generating {EventCount} sample events over {DaysBack} days", eventCount, daysBack); + await context.ReportProgressAsync(0, $"Generating {eventCount} sample events"); + + var generator = new RandomEventGenerator(_timeProvider); + var utcNow = _timeProvider.GetUtcNow().UtcDateTime; + var minDate = utcNow.AddDays(-daysBack); + + var projectResults = await _projectRepository.GetByOrganizationIdAsync(SampleDataService.TEST_ORG_ID); + var projectList = projectResults.Documents.ToList(); + if (projectList.Count == 0) + { + Log.LogWarning("No projects found for sample organization {OrganizationId}", SampleDataService.TEST_ORG_ID); + return; + } + + var organization = await _organizationRepository.GetByIdAsync(SampleDataService.TEST_ORG_ID); + if (organization is null) + { + Log.LogWarning("Sample organization {OrganizationId} not found", SampleDataService.TEST_ORG_ID); + return; + } + + int eventsPerProject = eventCount / projectList.Count; + int remainder = eventCount % projectList.Count; + int totalProcessed = 0; + const int batchSize = 50; + + for (int p = 0; p < projectList.Count; p++) + { + if (context.CancellationToken.IsCancellationRequested) + break; + + var project = projectList[p]; + int projectEventCount = eventsPerProject + (p < remainder ? 1 : 0); + + var events = generator.Generate(organization.Id, project.Id, projectEventCount, minDate, utcNow); + + for (int i = 0; i < events.Count; i += batchSize) + { + if (context.CancellationToken.IsCancellationRequested) + break; + + var batch = events.Skip(i).Take(batchSize).ToList(); + await _eventPipeline.RunAsync(batch, organization, project); + totalProcessed += batch.Count; + + int percentage = (int)Math.Min(99, totalProcessed * 100.0 / eventCount); + await context.ReportProgressAsync(percentage, $"Processed {totalProcessed}/{eventCount} events"); + } + } + + await context.ReportProgressAsync(100, $"Generated {totalProcessed} sample events across {projectList.Count} projects"); + Log.LogInformation("Generated {TotalEvents} sample events across {ProjectCount} projects", totalProcessed, projectList.Count); + } +} diff --git a/src/Exceptionless.Core/Models/WorkItems/GenerateSampleEventsWorkItem.cs b/src/Exceptionless.Core/Models/WorkItems/GenerateSampleEventsWorkItem.cs new file mode 100644 index 0000000000..06ec8c4dde --- /dev/null +++ b/src/Exceptionless.Core/Models/WorkItems/GenerateSampleEventsWorkItem.cs @@ -0,0 +1,7 @@ +namespace Exceptionless.Core.Models.WorkItems; + +public record GenerateSampleEventsWorkItem +{ + public int EventCount { get; init; } = 100; + public int DaysBack { get; init; } = 7; +} diff --git a/src/Exceptionless.Core/Utility/RandomEventGenerator.cs b/src/Exceptionless.Core/Utility/RandomEventGenerator.cs new file mode 100644 index 0000000000..aabd7161c3 --- /dev/null +++ b/src/Exceptionless.Core/Utility/RandomEventGenerator.cs @@ -0,0 +1,297 @@ +using Exceptionless.Core.Models; +using Exceptionless.Core.Models.Data; +using DataDictionary = Exceptionless.Core.Models.DataDictionary; + +namespace Exceptionless.Core.Utility; + +public class RandomEventGenerator +{ + private readonly TimeProvider _timeProvider; + + public RandomEventGenerator(TimeProvider timeProvider) + { + _timeProvider = timeProvider; + } + + public List Generate(string organizationId, string projectId, int count, DateTime? minDate = null, DateTime? maxDate = null) + { + var events = new List(count); + var min = minDate ?? _timeProvider.GetUtcNow().UtcDateTime.AddDays(-7); + var max = maxDate ?? _timeProvider.GetUtcNow().UtcDateTime; + + for (int i = 0; i < count; i++) + { + var ev = new PersistentEvent + { + OrganizationId = organizationId, + ProjectId = projectId, + Date = RandomData.GetDateTime(min, max) + }; + + PopulateEvent(ev); + events.Add(ev); + } + + return events; + } + + private void PopulateEvent(Event ev) + { + ev.Data ??= new DataDictionary(); + ev.Tags ??= []; + + ev.Type = EventTypes.Random()!; + switch (ev.Type) + { + case Event.KnownTypes.FeatureUsage: + ev.Source = FeatureNames.Random(); + break; + case Event.KnownTypes.NotFound: + ev.Source = PageNames.Random(); + break; + case Event.KnownTypes.Log: + ev.Source = LogSources.Random(); + ev.Message = LogMessages.Random(); + string? level = LogLevels.Random(); + if (!String.IsNullOrEmpty(level)) + ev.Data[Event.KnownDataKeys.Level] = level; + break; + } + + if (RandomData.GetBool(70)) + ev.Geo = RandomData.GetCoordinate(); + + if (RandomData.GetBool(30)) + ev.Value = RandomData.GetInt(0, 10000); + + string? identity = Identities.Random(); + if (!String.IsNullOrEmpty(identity)) + ev.SetUserIdentity(identity); + + ev.SetVersion(RandomData.GetVersion("2.0", "4.0")); + + ev.AddRequestInfo(new RequestInfo + { + Path = PageNames.Random() + }); + + ev.Data[Event.KnownDataKeys.EnvironmentInfo] = new EnvironmentInfo + { + IpAddress = MachineIpAddresses.Random() + ", " + MachineIpAddresses.Random(), + MachineName = MachineNames.Random() + }; + + for (int i = 0; i < RandomData.GetInt(1, 3); i++) + { + string key = RandomData.GetWord(); + while (ev.Data.ContainsKey(key) || key == Event.KnownDataKeys.Error) + key = RandomData.GetWord(); + ev.Data.Add(key, RandomData.GetString()); + } + + int tagCount = RandomData.GetInt(1, 3); + for (int i = 0; i < tagCount; i++) + { + string? tag = EventTags.Random(); + if (tag is not null) + ev.Tags.Add(tag); + } + + if (ev.Type == Event.KnownTypes.Error) + { + // Pre-generate a limited set of errors so stacking occurs + _randomErrors ??= [.. Enumerable.Range(1, 15).Select(_ => GenerateError())]; + _randomSimpleErrors ??= [.. Enumerable.Range(1, 10).Select(_ => GenerateSimpleError())]; + + if (RandomData.GetBool()) + ev.Data[Event.KnownDataKeys.Error] = _randomErrors.Random(); + else + ev.Data[Event.KnownDataKeys.SimpleError] = _randomSimpleErrors.Random(); + } + } + + private List? _randomErrors; + private List? _randomSimpleErrors; + + private Error GenerateError(int maxNesting = 3, int currentLevel = 0) + { + var error = new Error + { + Message = ErrorMessages.Random()!, + Type = ExceptionTypes.Random() + }; + + if (RandomData.GetBool()) + error.Code = RandomData.GetInt(-234523453, 98690899).ToString(); + + error.Data = new DataDictionary(); + for (int i = 0; i < RandomData.GetInt(1, 3); i++) + { + string key = RandomData.GetWord(); + while (error.Data.ContainsKey(key) || key == Event.KnownDataKeys.Error) + key = RandomData.GetWord(); + error.Data.Add(key, RandomData.GetString()); + } + + var stack = new StackFrameCollection(); + for (int i = 0; i < RandomData.GetInt(2, 8); i++) + stack.Add(GenerateStackFrame()); + error.StackTrace = stack; + + if (currentLevel < maxNesting && RandomData.GetBool()) + error.Inner = GenerateError(maxNesting, currentLevel + 1); + + return error; + } + + private SimpleError GenerateSimpleError(int maxNesting = 3, int currentLevel = 0) + { + var error = new SimpleError + { + Message = ErrorMessages.Random()!, + Type = ExceptionTypes.Random() + }; + + error.Data = new DataDictionary(); + for (int i = 0; i < RandomData.GetInt(1, 3); i++) + { + string key = RandomData.GetWord(); + while (error.Data.ContainsKey(key) || key == Event.KnownDataKeys.Error) + key = RandomData.GetWord(); + error.Data.Add(key, RandomData.GetString()); + } + + error.StackTrace = RandomData.GetString(); + + if (currentLevel < maxNesting && RandomData.GetBool()) + error.Inner = GenerateSimpleError(maxNesting, currentLevel + 1); + + return error; + } + + private static StackFrame GenerateStackFrame() + { + return new StackFrame + { + DeclaringNamespace = Namespaces.Random(), + DeclaringType = TypeNames.Random(), + Name = MethodNames.Random(), + Parameters = [new Parameter { Type = "String", Name = "path" }] + }; + } + + private static readonly List Identities = + [ + "eric@exceptionless.io", + "blake@exceptionless.io", + "support@exceptionless.io", + "dev@exceptionless.io", + "user42@example.com" + ]; + + private static readonly List MachineIpAddresses = + [ + "127.34.36.89", "45.66.89.98", "10.12.18.193", "16.89.17.197", "43.10.99.234" + ]; + + private static readonly List LogSources = + [ + "Exceptionless.Core.Pipeline.EventPipeline", + "Exceptionless.Core.Services.UsageService", + "Microsoft.AspNetCore.Hosting.WebHost", + "Foundatio.Jobs.WorkItemJob" + ]; + + private static readonly List LogMessages = + [ + "Processing event batch completed successfully", + "Cache miss for organization settings", + "Retrying failed operation after transient error", + "Request completed in 234ms", + "Background job started", + "Connection pool exhausted, waiting for available connection", + "Configuration reloaded from source", + "Health check passed" + ]; + + private static readonly List LogLevels = + [ + "Trace", "Debug", "Info", "Info", "Warn", "Error" + ]; + + private static readonly List FeatureNames = + [ + "Dashboard", "Event Search", "Stack Details", "Project Settings", + "User Management", "Notifications", "API Usage", "Billing" + ]; + + private static readonly List MachineNames = + [ + "web-prod-01", "web-prod-02", "api-prod-01", "worker-01", "worker-02" + ]; + + private static readonly List PageNames = + [ + "/dashboard", "/project/settings", "/account/manage", "/event/search", + "/stack/details", "/api/v2/events", "/api/v2/stacks", "/next/login" + ]; + + private static readonly List EventTypes = + [ + Event.KnownTypes.Error, Event.KnownTypes.Error, Event.KnownTypes.Error, + Event.KnownTypes.Log, Event.KnownTypes.Log, + Event.KnownTypes.FeatureUsage, + Event.KnownTypes.NotFound + ]; + + private static readonly List ExceptionTypes = + [ + "System.NullReferenceException", + "System.ArgumentException", + "System.InvalidOperationException", + "System.AggregateException", + "System.IO.FileNotFoundException", + "System.Net.Http.HttpRequestException", + "System.TimeoutException", + "System.UnauthorizedAccessException" + ]; + + private static readonly List ErrorMessages = + [ + "Object reference not set to an instance of an object.", + "Value cannot be null. (Parameter 'organizationId')", + "Sequence contains no elements", + "The operation was canceled.", + "Could not load file or assembly 'Newtonsoft.Json'", + "A task was canceled.", + "Connection refused (localhost:9200)", + "The request was aborted: Could not create SSL/TLS secure channel.", + "Index not found: stacks-v1-000001", + "Timeout expired. The timeout period elapsed prior to completion of the operation." + ]; + + private static readonly List EventTags = + [ + "Critical", "Production", "UI", "API", "Background", + "Authentication", "Search", "Billing", "Notification", "Import" + ]; + + private static readonly List Namespaces = + [ + "System", "System.IO", "System.Net.Http", + "Exceptionless.Core.Pipeline", "Exceptionless.Core.Services", + "Foundatio.Jobs", "Foundatio.Repositories" + ]; + + private static readonly List TypeNames = + [ + "EventPipeline", "UsageService", "StackRepository", + "EventPostsJob", "OrganizationController", "BillingManager" + ]; + + private static readonly List MethodNames = + [ + "ProcessAsync", "RunAsync", "GetByIdAsync", + "SaveAsync", "HandleItemAsync", "ValidateAsync" + ]; +} diff --git a/src/Exceptionless.Core/Utility/SampleDataService.cs b/src/Exceptionless.Core/Utility/SampleDataService.cs index 98538baf23..d9bf2789db 100644 --- a/src/Exceptionless.Core/Utility/SampleDataService.cs +++ b/src/Exceptionless.Core/Utility/SampleDataService.cs @@ -2,7 +2,10 @@ using Exceptionless.Core.Billing; using Exceptionless.Core.Extensions; using Exceptionless.Core.Models; +using Exceptionless.Core.Models.WorkItems; using Exceptionless.Core.Repositories; +using Foundatio.Jobs; +using Foundatio.Queues; using Foundatio.Repositories; using Microsoft.Extensions.Logging; @@ -13,6 +16,7 @@ public class SampleDataService private readonly IOrganizationRepository _organizationRepository; private readonly IProjectRepository _projectRepository; private readonly ITokenRepository _tokenRepository; + private readonly IQueue _workItemQueue; private readonly BillingManager _billingManager; private readonly BillingPlans _billingPlans; private readonly TimeProvider _timeProvider; @@ -43,6 +47,7 @@ public SampleDataService( IProjectRepository projectRepository, IUserRepository userRepository, ITokenRepository tokenRepository, + IQueue workItemQueue, BillingManager billingManager, BillingPlans billingPlans, TimeProvider timeProvider, @@ -53,6 +58,7 @@ ILoggerFactory loggerFactory _projectRepository = projectRepository; _userRepository = userRepository; _tokenRepository = tokenRepository; + _workItemQueue = workItemQueue; _billingManager = billingManager; _billingPlans = billingPlans; _timeProvider = timeProvider; @@ -270,4 +276,14 @@ await _tokenRepository.AddAsync(new Token await _userRepository.SaveAsync(user, o => o.Cache()); _logger.LogDebug("Created Internal Organization {OrganizationName} and Project {ProjectName}", organization.Name, project.Name); } + + public async Task EnqueueSampleEventsAsync(int eventCount = 100, int daysBack = 7) + { + await _workItemQueue.EnqueueAsync(new GenerateSampleEventsWorkItem + { + EventCount = eventCount, + DaysBack = daysBack + }); + _logger.LogInformation("Enqueued sample event generation: {EventCount} events over {DaysBack} days", eventCount, daysBack); + } } diff --git a/src/Exceptionless.Web/ClientApp.angular/grunt/task-configs/connect.js b/src/Exceptionless.Web/ClientApp.angular/grunt/task-configs/connect.js index 8000e545d6..a4a6621fde 100644 --- a/src/Exceptionless.Web/ClientApp.angular/grunt/task-configs/connect.js +++ b/src/Exceptionless.Web/ClientApp.angular/grunt/task-configs/connect.js @@ -6,13 +6,15 @@ var proxyRequest = require("grunt-connect-proxy2/lib/utils").proxyRequest; module.exports = function () { var target = getTarget(); - var certs = target.ssl ? generateCerts() : { cert: undefined, key: undefined }; + var useHttps = String(process.env.USE_HTTPS || "").toLowerCase() === "true"; + var port = Number(process.env.PORT) || 7121; + var certs = useHttps ? generateCerts() : { cert: undefined, key: undefined }; return { main: { options: { - port: 5100, - protocol: "http", + port: port, + protocol: useHttps ? "https" : "http", key: certs.key, cert: certs.cert, middleware: function (connect, options, middlewares) { @@ -63,48 +65,31 @@ module.exports = function () { }; function getTarget() { - var port = 5200; - var host = "localhost"; - var ssl = false; + var url = process.env.API_HTTPS || process.env.API_HTTP; - if (process.env.ASPNETCORE_HTTPS_PORT) { - port = process.env.ASPNETCORE_HTTPS_PORT; - } else if (process.env.ASPNETCORE_URLS) { - var url = process.env.ASPNETCORE_URLS.split(";")[0]; - var parts = url.split(":"); - if (url.startsWith("http://")) ssl = false; - if (parts.length >= 2) host = parts[1].substring(2); - if (parts.length >= 3) port = parts[2]; - else port = ssl ? 443 : 80; + if (url) { + var parsed = new URL(url); + var ssl = parsed.protocol === "https:"; + return { + host: parsed.hostname, + port: Number(parsed.port) || (ssl ? 443 : 80), + ssl, + }; } - return { - port, - host, - ssl, - }; + return { host: "localhost", port: 7110, ssl: false }; } -/** Function taken from aspnetcore-https.js in ASP.NET React template https://github.com/microsoft/commercial-marketplace-offer-deploy/blob/main/src/ClientApp/ClientApp/aspnetcore-https.ts */ function generateCerts() { var baseFolder = process.env.APPDATA !== undefined && process.env.APPDATA !== "" ? `${process.env.APPDATA}/ASP.NET/https` : `${process.env.HOME}/.aspnet/https`; - var certificateArg = process.argv - .map((arg) => { - var match = arg.match(/--name=(.+)/i); - return match ? { value: match[1] } : null; - }) - .filter(Boolean)[0]; - - var certificateName = certificateArg ? certificateArg.groups.value : process.env.npm_package_name; + var certificateName = process.env.npm_package_name; if (!certificateName) { // eslint-disable-next-line no-console - console.error( - "Invalid certificate name. Run this script in the context of an npm/yarn script or pass --name=<> explicitly." - ); + console.error("Invalid certificate name. Run this script in the context of an npm script."); process.exit(-1); } @@ -112,12 +97,7 @@ function generateCerts() { var keyFilePath = path.join(baseFolder, `${certificateName}.key`); if (!fs.existsSync(certFilePath) || !fs.existsSync(keyFilePath)) { - var outp = s.execSync( - "dotnet " + - ["dev-certs", "https", "--export-path", `"${certFilePath}"`, "--format", "Pem", "--no-password"].join( - " " - ) - ); + var outp = s.execSync(`dotnet dev-certs https --export-path "${certFilePath}" --format Pem --no-password`); // eslint-disable-next-line no-console console.log(outp.toString()); } diff --git a/src/Exceptionless.Web/ClientApp/package-lock.json b/src/Exceptionless.Web/ClientApp/package-lock.json index 009ca86816..97d141f1d4 100644 --- a/src/Exceptionless.Web/ClientApp/package-lock.json +++ b/src/Exceptionless.Web/ClientApp/package-lock.json @@ -7,77 +7,75 @@ "": { "name": "exceptionless", "version": "8.0.0", - "dependencies": { - "@exceptionless/browser": "^3.1.0", - "@exceptionless/fetchclient": "^0.44.0", - "@internationalized/date": "^3.12.1", - "@lucide/svelte": "^1.8.0", - "@tanstack/svelte-form": "^1.29.0", - "@tanstack/svelte-query": "^6.1.16", - "@tanstack/svelte-query-devtools": "^6.1.16", - "@tanstack/svelte-table": "^9.0.0-alpha.10", - "@types/d3-scale": "^4.0.9", - "@types/d3-shape": "^3.1.8", - "bits-ui": "^2.17.3", - "clsx": "^2.1.1", - "d3-scale": "^4.0.2", - "dompurify": "^3.4.0", - "kit-query-params": "^0.0.26", - "layerchart": "^2.0.0-next.46", - "mode-watcher": "^1.1.0", - "oidc-client-ts": "^3.5.0", - "pretty-ms": "^9.3.0", - "runed": "^0.37.1", - "shiki": "^4.0.2", - "svelte-intercom": "^0.0.35", - "svelte-sonner": "^1.1.0", - "svelte-time": "^2.1.0", - "tailwind-merge": "^3.5.0", - "tailwind-variants": "^3.2.2", - "tailwindcss": "^4.2.2", - "throttle-debounce": "^5.0.2", - "tw-animate-css": "^1.4.0" - }, "devDependencies": { "@chromatic-com/storybook": "^5.1.2", "@eslint/compat": "^2.0.5", "@eslint/js": "^10.0.1", - "@iconify-json/lucide": "^1.2.102", + "@exceptionless/browser": "^3.1.0", + "@exceptionless/fetchclient": "^0.44.0", + "@iconify-json/lucide": "^1.2.103", + "@internationalized/date": "^3.12.1", + "@lucide/svelte": "^1.11.0", "@playwright/test": "^1.59.1", "@storybook/addon-a11y": "^10.3.5", "@storybook/addon-docs": "^10.3.5", "@storybook/addon-svelte-csf": "^5.1.2", "@storybook/sveltekit": "^10.3.5", "@sveltejs/adapter-static": "^3.0.10", - "@sveltejs/kit": "^2.57.1", + "@sveltejs/kit": "^2.58.0", "@sveltejs/vite-plugin-svelte": "^6.2.4", - "@tailwindcss/vite": "^4.2.2", - "@tanstack/eslint-plugin-query": "^5.99.0", + "@tailwindcss/vite": "^4.2.4", + "@tanstack/eslint-plugin-query": "^5.100.4", + "@tanstack/svelte-form": "^1.29.1", + "@tanstack/svelte-query": "^6.1.23", + "@tanstack/svelte-query-devtools": "^6.1.23", + "@tanstack/svelte-table": "^9.0.0-alpha.10", "@testing-library/jest-dom": "^6.9.1", "@testing-library/svelte": "^5.3.1", + "@types/d3-scale": "^4.0.9", + "@types/d3-shape": "^3.1.8", "@types/eslint": "^9.6.1", "@types/node": "^25.6.0", "@types/throttle-debounce": "^5.0.2", + "bits-ui": "^2.18.0", + "clsx": "^2.1.1", "cross-env": "^10.1.0", - "eslint": "^10.2.0", + "d3-scale": "^4.0.2", + "dompurify": "^3.4.1", + "eslint": "^10.2.1", "eslint-config-prettier": "^10.1.8", - "eslint-plugin-perfectionist": "^5.8.0", + "eslint-plugin-perfectionist": "^5.9.0", "eslint-plugin-storybook": "^10.3.5", - "eslint-plugin-svelte": "^3.17.0", + "eslint-plugin-svelte": "^3.17.1", "globals": "^17.5.0", "jsdom": "^29.0.2", + "kit-query-params": "^0.0.26", + "layerchart": "^2.0.0-next.46", + "mode-watcher": "^1.1.0", + "oidc-client-ts": "^3.5.0", "prettier": "^3.8.3", "prettier-plugin-svelte": "^3.5.1", "prettier-plugin-tailwindcss": "^0.7.2", + "pretty-ms": "^9.3.0", + "runed": "^0.37.1", + "shiki": "^4.0.2", "storybook": "^10.3.5", - "svelte": "^5.55.4", + "svelte": "^5.55.5", "svelte-check": "^4.4.6", - "swagger-typescript-api": "^13.6.10", + "svelte-intercom": "^0.0.35", + "svelte-sonner": "^1.1.1", + "svelte-time": "^2.1.0", + "swagger-typescript-api": "^13.7.0", + "tailwind-merge": "^3.5.0", + "tailwind-variants": "^3.2.2", + "tailwindcss": "^4.2.4", + "throttle-debounce": "^5.0.2", "tslib": "^2.8.1", - "typescript": "^6.0.2", - "typescript-eslint": "^8.58.2", + "tw-animate-css": "^1.4.0", + "typescript": "^6.0.3", + "typescript-eslint": "^8.59.0", "vite": "^7.3.2", - "vitest": "4.1.4", + "vitest": "4.1.5", "vitest-websocket-mock": "^0.5.0", "zod": "^4.3.6" } @@ -278,9 +276,9 @@ } }, "node_modules/@biomejs/wasm-nodejs": { - "version": "2.4.12", - "resolved": "https://registry.npmjs.org/@biomejs/wasm-nodejs/-/wasm-nodejs-2.4.12.tgz", - "integrity": "sha512-3SGczq2LKHJw9TYhJEmNwgBY767wSu+nRZVkp+oDiczYn2u7Xekb4smn/r3KJpaxGKkxxtTV4h6RTwBUKzf8Fw==", + "version": "2.4.13", + "resolved": "https://registry.npmjs.org/@biomejs/wasm-nodejs/-/wasm-nodejs-2.4.13.tgz", + "integrity": "sha512-BA64wwPmk5NUz+kP+jrHa8dnIjZSqeX6Q+Q9CC5skZ9c1AqkdS+3vR1QQUzqJ4btbxLPoYVSe1OiTYil7nr8YQ==", "dev": true, "license": "MIT OR Apache-2.0" }, @@ -462,6 +460,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/@dagrejs/dagre/-/dagre-2.0.4.tgz", "integrity": "sha512-J6vCWTNpicHF4zFlZG1cS5DkGzMr9941gddYkakjrg3ZNev4bbqEgLHFTWiFrcJm7UCRu7olO3K6IRDd9gSGhA==", + "dev": true, "license": "MIT", "dependencies": { "@dagrejs/graphlib": "3.0.4" @@ -471,6 +470,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@dagrejs/graphlib/-/graphlib-3.0.4.tgz", "integrity": "sha512-HxZ7fCvAwTLCWCO0WjDkzAFQze8LdC6iOpKbetDKHIuDfIgMlIzYzqZ4nxwLlclQX+3ZVeZ1K2OuaOE2WWcyOg==", + "dev": true, "license": "MIT" }, "node_modules/@epic-web/invariant": { @@ -487,6 +487,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -503,6 +504,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -519,6 +521,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -535,6 +538,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -551,6 +555,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -567,6 +572,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -583,6 +589,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -599,6 +606,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -615,6 +623,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -631,6 +640,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -647,6 +657,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -663,6 +674,7 @@ "cpu": [ "loong64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -679,6 +691,7 @@ "cpu": [ "mips64el" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -695,6 +708,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -711,6 +725,7 @@ "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -727,6 +742,7 @@ "cpu": [ "s390x" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -743,6 +759,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -759,6 +776,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -775,6 +793,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -791,6 +810,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -807,6 +827,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -823,6 +844,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -839,6 +861,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -855,6 +878,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -871,6 +895,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -887,6 +912,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1049,6 +1075,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/@exceptionless/browser/-/browser-3.1.0.tgz", "integrity": "sha512-RzQDR2ua+CdaJAepxo8SIy/kEcNlnvrd6AsecYJ3cY0JSNIS0r+oNgfT/QNX+I8Vzp1CjUUHiZs5E3U+haoShg==", + "dev": true, "license": "Apache-2.0", "dependencies": { "@exceptionless/core": "3.1.0", @@ -1059,12 +1086,14 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/@exceptionless/core/-/core-3.1.0.tgz", "integrity": "sha512-BwyKIsaL61lqsOcI8+rSr1M55w+iyGw1G/XXNd7BWU/oOoWZRTBRM/RPDQIzycqphq52LWdDWS1aHDnUcKNnKw==", + "dev": true, "license": "Apache-2.0" }, "node_modules/@exceptionless/fetchclient": { "version": "0.44.0", "resolved": "https://registry.npmjs.org/@exceptionless/fetchclient/-/fetchclient-0.44.0.tgz", "integrity": "sha512-xA3+/ZEJvKTXj0B8tI+6s7enBZp0l7ix/m+oDQOyxWaZY59PSiMehtdjbDbFs2dsX62caPuhMIMa9DK8n9oTnw==", + "dev": true, "license": "Apache-2.0" }, "node_modules/@exodus/bytes": { @@ -1096,6 +1125,7 @@ "version": "1.7.3", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", + "dev": true, "license": "MIT", "dependencies": { "@floating-ui/utils": "^0.2.10" @@ -1105,6 +1135,7 @@ "version": "1.7.4", "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", + "dev": true, "license": "MIT", "dependencies": { "@floating-ui/core": "^1.7.3", @@ -1115,6 +1146,7 @@ "version": "0.2.10", "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "dev": true, "license": "MIT" }, "node_modules/@humanfs/core": { @@ -1170,9 +1202,9 @@ } }, "node_modules/@iconify-json/lucide": { - "version": "1.2.102", - "resolved": "https://registry.npmjs.org/@iconify-json/lucide/-/lucide-1.2.102.tgz", - "integrity": "sha512-Dm3EEqu5NrmzyDMB2U1+8yroEj2/dB9V4KlH0m/szwwF/ofSf0cPaGTZqkd1aExXjCor+vU53ttRMCGuXf+/cg==", + "version": "1.2.103", + "resolved": "https://registry.npmjs.org/@iconify-json/lucide/-/lucide-1.2.103.tgz", + "integrity": "sha512-A5WdDll62RPxYs1yeYa9b2mV1Vs+lK/wX0wYKR+xVnhwsfPGLBwo6qfyoAQLo7AaP7oHlkw3gnHdOCERegbwTA==", "dev": true, "license": "ISC", "dependencies": { @@ -1190,12 +1222,14 @@ "version": "0.0.14", "resolved": "https://registry.npmjs.org/@intercom/messenger-js-sdk/-/messenger-js-sdk-0.0.14.tgz", "integrity": "sha512-2dH4BDAh9EI90K7hUkAdZ76W79LM45Sd1OBX7t6Vzy8twpNiQ5X+7sH9G5hlJlkSGnf+vFWlFcy9TOYAyEs1hA==", + "dev": true, "license": "MIT" }, "node_modules/@internationalized/date": { "version": "3.12.1", "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.12.1.tgz", "integrity": "sha512-6IedsVWXyq4P9Tj+TxuU8WGWM70hYLl12nbYU8jkikVpa6WXapFazPUcHUMDMoWftIDE2ILDkFFte6W2nFCkRQ==", + "dev": true, "license": "Apache-2.0", "dependencies": { "@swc/helpers": "^0.5.0" @@ -1205,6 +1239,7 @@ "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", @@ -1215,6 +1250,7 @@ "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", @@ -1225,6 +1261,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -1234,12 +1271,14 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -1250,6 +1289,7 @@ "version": "1.0.1-next.18", "resolved": "https://registry.npmjs.org/@layerstack/svelte-actions/-/svelte-actions-1.0.1-next.18.tgz", "integrity": "sha512-gxPzCnJ1c9LTfWtRqLUzefCx+k59ZpxDUQ2XB+LokveZQPe7IDSOwHaBOEMlaGoGrtwc3Ft8dSZq+2WT2o9u/g==", + "dev": true, "license": "MIT", "dependencies": { "@floating-ui/dom": "^1.7.0", @@ -1261,6 +1301,7 @@ "version": "0.1.0-next.23", "resolved": "https://registry.npmjs.org/@layerstack/svelte-state/-/svelte-state-0.1.0-next.23.tgz", "integrity": "sha512-7O4umv+gXwFfs3/vjzFWYHNXGwYnnjBapWJ5Y+9u99F4eVk6rh4ocNwqkqQNkpMZ5tUJBlRTWjPE1So6+hEzIg==", + "dev": true, "license": "MIT", "dependencies": { "@layerstack/utils": "2.0.0-next.18" @@ -1270,6 +1311,7 @@ "version": "2.0.0-next.21", "resolved": "https://registry.npmjs.org/@layerstack/tailwind/-/tailwind-2.0.0-next.21.tgz", "integrity": "sha512-Qgp2EpmEHmjtura8MQzWicR6ztBRSsRvddakFtx9ShrLMz6jWzd6bCMVVRu44Q3ZOrtXmSu4QxjCZWu1ytvuPg==", + "dev": true, "license": "MIT", "dependencies": { "@layerstack/utils": "^2.0.0-next.18", @@ -1282,6 +1324,7 @@ "version": "2.0.0-next.18", "resolved": "https://registry.npmjs.org/@layerstack/utils/-/utils-2.0.0-next.18.tgz", "integrity": "sha512-EYILHpfBRYMMEahajInu9C2AXQom5IcAEdtCeucD3QIl/fdDgRbtzn6/8QW9ewumfyNZetdUvitOksmI1+gZYQ==", + "dev": true, "license": "MIT", "dependencies": { "d3-array": "^3.2.4", @@ -1290,9 +1333,10 @@ } }, "node_modules/@lucide/svelte": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@lucide/svelte/-/svelte-1.8.0.tgz", - "integrity": "sha512-+zYQUKqEOVP5lxbGmxL1OVgGMQtRK91eIJ0bR+3Cr1ts4oQEsQfxyzzd5X47psJlblAuGFrl2xm4YuATjR9oaA==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@lucide/svelte/-/svelte-1.11.0.tgz", + "integrity": "sha512-4I9b+RuxIf8OkGG883bIYDIXjqVOH0ClgrB0j+jI6CrzEziroEwxiubQwW2xKT3esdJcRVTtW9RtCQM9D+r+ew==", + "dev": true, "license": "ISC", "peerDependencies": { "svelte": "^5" @@ -1343,7 +1387,7 @@ "version": "1.0.0-next.29", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { @@ -1353,6 +1397,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1366,6 +1411,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1379,6 +1425,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1392,6 +1439,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1405,6 +1453,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1418,6 +1467,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1431,6 +1481,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1444,6 +1495,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1457,6 +1509,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1470,6 +1523,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1483,6 +1537,7 @@ "cpu": [ "loong64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1496,6 +1551,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1509,6 +1565,7 @@ "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1522,6 +1579,7 @@ "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1535,6 +1593,7 @@ "cpu": [ "s390x" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1548,6 +1607,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1561,6 +1621,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1574,6 +1635,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1587,6 +1649,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1600,6 +1663,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1613,6 +1677,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1626,6 +1691,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1636,6 +1702,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.0.2.tgz", "integrity": "sha512-hxT0YF4ExEqB8G/qFdtJvpmHXBYJ2lWW7qTHDarVkIudPFE6iCIrqdgWxGn5s+ppkGXI0aEGlibI0PAyzP3zlw==", + "dev": true, "license": "MIT", "dependencies": { "@shikijs/primitive": "4.0.2", @@ -1652,6 +1719,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.0.2.tgz", "integrity": "sha512-7PW0Nm49DcoUIQEXlJhNNBHyoGMjalRETTCcjMqEaMoJRLljy1Bi/EGV3/qLBgLKQejdspiiYuHGQW6dX94Nag==", + "dev": true, "license": "MIT", "dependencies": { "@shikijs/types": "4.0.2", @@ -1666,6 +1734,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.0.2.tgz", "integrity": "sha512-UpCB9Y2sUKlS9z8juFSKz7ZtysmeXCgnRF0dlhXBkmQnek7lAToPte8DkxmEYGNTMii72zU/lyXiCB6StuZeJg==", + "dev": true, "license": "MIT", "dependencies": { "@shikijs/types": "4.0.2", @@ -1679,6 +1748,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.0.2.tgz", "integrity": "sha512-KaXby5dvoeuZzN0rYQiPMjFoUrz4hgwIE+D6Du9owcHcl6/g16/yT5BQxSW5cGt2MZBz6Hl0YuRqf12omRfUUg==", + "dev": true, "license": "MIT", "dependencies": { "@shikijs/types": "4.0.2" @@ -1691,6 +1761,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.0.2.tgz", "integrity": "sha512-M6UMPrSa3fN5ayeJwFVl9qWofl273wtK1VG8ySDZ1mQBfhCpdd8nEx7nPZ/tk7k+TYcpqBZzj/AnwxT9lO+HJw==", + "dev": true, "license": "MIT", "dependencies": { "@shikijs/types": "4.0.2", @@ -1705,6 +1776,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.0.2.tgz", "integrity": "sha512-mjCafwt8lJJaVSsQvNVrJumbnnj1RI8jbUKrPKgE6E3OvQKxnuRoBaYC51H4IGHePsGN/QtALglWBU7DoKDFnA==", + "dev": true, "license": "MIT", "dependencies": { "@shikijs/types": "4.0.2" @@ -1717,6 +1789,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.0.2.tgz", "integrity": "sha512-qzbeRooUTPnLE+sHD/Z8DStmaDgnbbc/pMrU203950aRqjX/6AFHeDYT+j00y2lPdz0ywJKx7o/7qnqTivtlXg==", + "dev": true, "license": "MIT", "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", @@ -1730,13 +1803,14 @@ "version": "10.0.2", "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", + "dev": true, "license": "MIT" }, "node_modules/@standard-schema/spec": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/@storybook/addon-a11y": { @@ -1998,6 +2072,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.8.tgz", "integrity": "sha512-esgN+54+q0NjB0Y/4BomT9samII7jGwNy/2a3wNZbT2A2RpmXsXwUt24LvLhx6jUq2gVk4cWEvcRO6MFQbOfNA==", + "dev": true, "license": "MIT", "peerDependencies": { "acorn": "^8.9.0" @@ -2014,10 +2089,10 @@ } }, "node_modules/@sveltejs/kit": { - "version": "2.57.1", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.57.1.tgz", - "integrity": "sha512-VRdSbB96cI1EnRh09CqmnQqP/YJvET5buj8S6k7CxaJqBJD4bw4fRKDjcarAj/eX9k2eHifQfDH8NtOh+ZxxPw==", - "devOptional": true, + "version": "2.58.0", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.58.0.tgz", + "integrity": "sha512-kT9GCN8yJTkCK1W+Gi/bvGooWAM7y7WXP+yd+rf6QOIjyoK1ERPrMwSufXJUNu2pMWIqruhFvmz+LbOqsEmKmA==", + "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.0.0", @@ -2059,7 +2134,7 @@ "version": "6.2.4", "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-6.2.4.tgz", "integrity": "sha512-ou/d51QSdTyN26D7h6dSpusAKaZkAiGM55/AKYi+9AGZw7q85hElbjK3kEyzXHhLSnRISHOYzVge6x0jRZ7DXA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", @@ -2080,7 +2155,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-5.0.1.tgz", "integrity": "sha512-ubWshlMk4bc8mkwWbg6vNvCeT7lGQojE3ijDh3QTR6Zr/R+GXxsGbyH4PExEPpiFmqPhYiVSVmHBjUcVc1JIrA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "debug": "^4.4.1" @@ -2098,15 +2173,16 @@ "version": "0.5.17", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", + "dev": true, "license": "Apache-2.0", "dependencies": { "tslib": "^2.8.0" } }, "node_modules/@tailwindcss/node": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.2.tgz", - "integrity": "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.4.tgz", + "integrity": "sha512-Ai7+yQPxz3ddrDQzFfBKdHEVBg0w3Zl83jnjuwxnZOsnH9pGn93QHQtpU0p/8rYWxvbFZHneni6p1BSLK4DkGA==", "dev": true, "license": "MIT", "dependencies": { @@ -2116,37 +2192,37 @@ "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", - "tailwindcss": "4.2.2" + "tailwindcss": "4.2.4" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.2.tgz", - "integrity": "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.4.tgz", + "integrity": "sha512-9El/iI069DKDSXwTvB9J4BwdO5JhRrOweGaK25taBAvBXyXqJAX+Jqdvs8r8gKpsI/1m0LeJLyQYTf/WLrBT1Q==", "dev": true, "license": "MIT", "engines": { "node": ">= 20" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.2.2", - "@tailwindcss/oxide-darwin-arm64": "4.2.2", - "@tailwindcss/oxide-darwin-x64": "4.2.2", - "@tailwindcss/oxide-freebsd-x64": "4.2.2", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", - "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", - "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", - "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", - "@tailwindcss/oxide-linux-x64-musl": "4.2.2", - "@tailwindcss/oxide-wasm32-wasi": "4.2.2", - "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", - "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" + "@tailwindcss/oxide-android-arm64": "4.2.4", + "@tailwindcss/oxide-darwin-arm64": "4.2.4", + "@tailwindcss/oxide-darwin-x64": "4.2.4", + "@tailwindcss/oxide-freebsd-x64": "4.2.4", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.4", + "@tailwindcss/oxide-linux-arm64-gnu": "4.2.4", + "@tailwindcss/oxide-linux-arm64-musl": "4.2.4", + "@tailwindcss/oxide-linux-x64-gnu": "4.2.4", + "@tailwindcss/oxide-linux-x64-musl": "4.2.4", + "@tailwindcss/oxide-wasm32-wasi": "4.2.4", + "@tailwindcss/oxide-win32-arm64-msvc": "4.2.4", + "@tailwindcss/oxide-win32-x64-msvc": "4.2.4" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.2.tgz", - "integrity": "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.4.tgz", + "integrity": "sha512-e7MOr1SAn9U8KlZzPi1ZXGZHeC5anY36qjNwmZv9pOJ8E4Q6jmD1vyEHkQFmNOIN7twGPEMXRHmitN4zCMN03g==", "cpu": [ "arm64" ], @@ -2161,9 +2237,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.2.tgz", - "integrity": "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.4.tgz", + "integrity": "sha512-tSC/Kbqpz/5/o/C2sG7QvOxAKqyd10bq+ypZNf+9Fi2TvbVbv1zNpcEptcsU7DPROaSbVgUXmrzKhurFvo5eDg==", "cpu": [ "arm64" ], @@ -2178,9 +2254,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.2.tgz", - "integrity": "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.4.tgz", + "integrity": "sha512-yPyUXn3yO/ufR6+Kzv0t4fCg2qNr90jxXc5QqBpjlPNd0NqyDXcmQb/6weunH/MEDXW5dhyEi+agTDiqa3WsGg==", "cpu": [ "x64" ], @@ -2195,9 +2271,9 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.2.tgz", - "integrity": "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.4.tgz", + "integrity": "sha512-BoMIB4vMQtZsXdGLVc2z+P9DbETkiopogfWZKbWwM8b/1Vinbs4YcUwo+kM/KeLkX3Ygrf4/PsRndKaYhS8Eiw==", "cpu": [ "x64" ], @@ -2212,9 +2288,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.2.tgz", - "integrity": "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.4.tgz", + "integrity": "sha512-7pIHBLTHYRAlS7V22JNuTh33yLH4VElwKtB3bwchK/UaKUPpQ0lPQiOWcbm4V3WP2I6fNIJ23vABIvoy2izdwA==", "cpu": [ "arm" ], @@ -2229,9 +2305,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.2.tgz", - "integrity": "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.4.tgz", + "integrity": "sha512-+E4wxJ0ZGOzSH325reXTWB48l42i93kQqMvDyz5gqfRzRZ7faNhnmvlV4EPGJU3QJM/3Ab5jhJ5pCRUsKn6OQw==", "cpu": [ "arm64" ], @@ -2249,9 +2325,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.2.tgz", - "integrity": "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.4.tgz", + "integrity": "sha512-bBADEGAbo4ASnppIziaQJelekCxdMaxisrk+fB7Thit72IBnALp9K6ffA2G4ruj90G9XRS2VQ6q2bCKbfFV82g==", "cpu": [ "arm64" ], @@ -2269,9 +2345,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.2.tgz", - "integrity": "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.4.tgz", + "integrity": "sha512-7Mx25E4WTfnht0TVRTyC00j3i0M+EeFe7wguMDTlX4mRxafznw0CA8WJkFjWYH5BlgELd1kSjuU2JiPnNZbJDA==", "cpu": [ "x64" ], @@ -2289,9 +2365,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.2.tgz", - "integrity": "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.4.tgz", + "integrity": "sha512-2wwJRF7nyhOR0hhHoChc04xngV3iS+akccHTGtz965FwF0up4b2lOdo6kI1EbDaEXKgvcrFBYcYQQ/rrnWFVfA==", "cpu": [ "x64" ], @@ -2309,9 +2385,9 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.2.tgz", - "integrity": "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.4.tgz", + "integrity": "sha512-FQsqApeor8Fo6gUEklzmaa9994orJZZDBAlQpK2Mq+DslRKFJeD6AjHpBQ0kZFQohVr8o85PPh8eOy86VlSCmw==", "bundleDependencies": [ "@napi-rs/wasm-runtime", "@emnapi/core", @@ -2338,74 +2414,10 @@ "node": ">=14.0.0" } }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { - "version": "1.8.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.1.0", - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { - "version": "1.8.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { - "version": "1.1.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1", - "@tybys/wasm-util": "^0.10.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { - "version": "2.8.1", - "dev": true, - "inBundle": true, - "license": "0BSD", - "optional": true - }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.2.tgz", - "integrity": "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.4.tgz", + "integrity": "sha512-L9BXqxC4ToVgwMFqj3pmZRqyHEztulpUJzCxUtLjobMCzTPsGt1Fa9enKbOpY2iIyVtaHNeNvAK8ERP/64sqGQ==", "cpu": [ "arm64" ], @@ -2420,9 +2432,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.2.tgz", - "integrity": "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.4.tgz", + "integrity": "sha512-ESlKG0EpVJQwRjXDDa9rLvhEAh0mhP1sF7sap9dNZT0yyl9SAG6T7gdP09EH0vIv0UNTlo6jPWyujD6559fZvw==", "cpu": [ "x64" ], @@ -2437,15 +2449,15 @@ } }, "node_modules/@tailwindcss/vite": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.2.2.tgz", - "integrity": "sha512-mEiF5HO1QqCLXoNEfXVA1Tzo+cYsrqV7w9Juj2wdUFyW07JRenqMG225MvPwr3ZD9N1bFQj46X7r33iHxLUW0w==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.2.4.tgz", + "integrity": "sha512-pCvohwOCspk3ZFn6eJzrrX3g4n2JY73H6MmYC87XfGPyTty4YsCjYTMArRZm/zOI8dIt3+EcrLHAFPe5A4bgtw==", "dev": true, "license": "MIT", "dependencies": { - "@tailwindcss/node": "4.2.2", - "@tailwindcss/oxide": "4.2.2", - "tailwindcss": "4.2.2" + "@tailwindcss/node": "4.2.4", + "@tailwindcss/oxide": "4.2.4", + "tailwindcss": "4.2.4" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7 || ^8" @@ -2455,6 +2467,7 @@ "version": "0.4.3", "resolved": "https://registry.npmjs.org/@tanstack/devtools-event-client/-/devtools-event-client-0.4.3.tgz", "integrity": "sha512-OZI6QyULw0FI0wjgmeYzCIfbgPsOEzwJtCpa69XrfLMtNXLGnz3d/dIabk7frg0TmHo+Ah49w5I4KC7Tufwsvw==", + "dev": true, "license": "MIT", "bin": { "intent": "bin/intent.js" @@ -2468,9 +2481,9 @@ } }, "node_modules/@tanstack/eslint-plugin-query": { - "version": "5.99.0", - "resolved": "https://registry.npmjs.org/@tanstack/eslint-plugin-query/-/eslint-plugin-query-5.99.0.tgz", - "integrity": "sha512-jVp1AEL7S7BeuQvH5SN1F5UdrNW/AbryKDeWUUMeAKNzh9C+Ik/bRSa/HeuJLlmaN+WOUkdDFbtCK0go7BxnUQ==", + "version": "5.100.4", + "resolved": "https://registry.npmjs.org/@tanstack/eslint-plugin-query/-/eslint-plugin-query-5.100.4.tgz", + "integrity": "sha512-RLMR5dOhp3CDKLApjipAK7ki2zvrxlZ32HhALXAovqW+GAnxS3s7Wfx6ym1SAX7A4u8HCJJFnwIyBjywrRizbw==", "dev": true, "license": "MIT", "dependencies": { @@ -2491,9 +2504,10 @@ } }, "node_modules/@tanstack/form-core": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/@tanstack/form-core/-/form-core-1.29.0.tgz", - "integrity": "sha512-uyeKEdJBfbj0bkBSwvSYVRtWLOaXvfNX3CeVw1HqGOXVLxpBBGAqWdYLc+UoX/9xcoFwFXrjR9QqMPzvwm2yyQ==", + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/@tanstack/form-core/-/form-core-1.29.1.tgz", + "integrity": "sha512-NIYPO36eEu7nSWvMpbFDQaBWyVtnH/C8fsZ3/XpJUT4uOWgmxsiUvHGbTbDNIQTXAKIkhwEl0sUrqBNn2SfUnw==", + "dev": true, "license": "MIT", "dependencies": { "@tanstack/devtools-event-client": "^0.4.1", @@ -2509,6 +2523,7 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/@tanstack/pacer-lite/-/pacer-lite-0.1.1.tgz", "integrity": "sha512-y/xtNPNt/YeyoVxE/JCx+T7yjEzpezmbb+toK8DDD1P4m7Kzs5YR956+7OKexG3f8aXgC3rLZl7b1V+yNUSy5w==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -2519,9 +2534,10 @@ } }, "node_modules/@tanstack/query-core": { - "version": "5.99.0", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.99.0.tgz", - "integrity": "sha512-3Jv3WQG0BCcH7G+7lf/bP8QyBfJOXeY+T08Rin3GZ1bshvwlbPt7NrDHMEzGdKIOmOzvIQmxjk28YEQX60k7pQ==", + "version": "5.100.4", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.100.4.tgz", + "integrity": "sha512-LdW/DDImiw9g4ukyndlrifIXPFpoQjNybCAIDBcPvdYu9iUIhAKwhznfAATe2dJBonhm0O3ksoCMmVTUxN89uA==", + "dev": true, "license": "MIT", "funding": { "type": "github", @@ -2529,9 +2545,10 @@ } }, "node_modules/@tanstack/query-devtools": { - "version": "5.99.0", - "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.99.0.tgz", - "integrity": "sha512-m4ufXaJ8FjWXw7xDtyzE/6fkZAyQFg9WrbMrUpt8ZecRJx58jiFOZ2lxZMphZdIpAnIeto/S8stbwLKLusyckQ==", + "version": "5.100.4", + "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.100.4.tgz", + "integrity": "sha512-tZK2DrSS7QXDrRYBel9UOVM0WF9gRkBFThVB/kQrEOTG+mUHat+vwnDXlW6wt7tn7QQEghplmmat5jeoTzJzxQ==", + "dev": true, "license": "MIT", "funding": { "type": "github", @@ -2542,6 +2559,7 @@ "version": "0.9.2", "resolved": "https://registry.npmjs.org/@tanstack/store/-/store-0.9.2.tgz", "integrity": "sha512-K013lUJEFJK2ofFQ/hZKJUmCnpcV00ebLyOyFOWQvyQHUOZp/iYO84BM6aOGiV81JzwbX0APTVmW8YI7yiG5oA==", + "dev": true, "license": "MIT", "funding": { "type": "github", @@ -2549,12 +2567,13 @@ } }, "node_modules/@tanstack/svelte-form": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/@tanstack/svelte-form/-/svelte-form-1.29.0.tgz", - "integrity": "sha512-pGZNAzCJ/dmBtrk+BCqORY1C7rCWOrg/E+ekJF6C3LhC9x9/68WFv32Mi3ggfAGCoH00/dsgbbRc9110qfxsCA==", + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/@tanstack/svelte-form/-/svelte-form-1.29.1.tgz", + "integrity": "sha512-XW9/nsoEWf4i/TIkVL7v24yusNllFTytEPJg5TsOI5BYuJBeOpLBP1IRk1nymm+9SPrcxvOOjtWlIeZNik8BEg==", + "dev": true, "license": "MIT", "dependencies": { - "@tanstack/form-core": "1.29.0", + "@tanstack/form-core": "1.29.1", "@tanstack/svelte-store": "^0.10.1" }, "peerDependencies": { @@ -2562,12 +2581,13 @@ } }, "node_modules/@tanstack/svelte-query": { - "version": "6.1.16", - "resolved": "https://registry.npmjs.org/@tanstack/svelte-query/-/svelte-query-6.1.16.tgz", - "integrity": "sha512-G+YMgYJHI+aKX/QJQGY/D3thKiWHRH68s+p17INKgVxULf7yl4q81RZGKwCnobsIKNOWtCw3v9/8wxyj9lOvjQ==", + "version": "6.1.23", + "resolved": "https://registry.npmjs.org/@tanstack/svelte-query/-/svelte-query-6.1.23.tgz", + "integrity": "sha512-uovf6Rp1VAyJBcKfyv6sa4KNoJf07HlOby6TIVDgl6sCYjEnbNmgfcj59hd4GnMi/7qI6gnYbtWlN0AaEiw30Q==", + "dev": true, "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.99.0" + "@tanstack/query-core": "5.100.4" }, "funding": { "type": "github", @@ -2578,12 +2598,13 @@ } }, "node_modules/@tanstack/svelte-query-devtools": { - "version": "6.1.16", - "resolved": "https://registry.npmjs.org/@tanstack/svelte-query-devtools/-/svelte-query-devtools-6.1.16.tgz", - "integrity": "sha512-NOrzZyHxpPez+t2o2+7+tRy4j8whk6V96DSzjRsZd7TS4gS6eBsRHOi8HVgdjBrdlz5TZzbYdJALSFY1zk7ygQ==", + "version": "6.1.23", + "resolved": "https://registry.npmjs.org/@tanstack/svelte-query-devtools/-/svelte-query-devtools-6.1.23.tgz", + "integrity": "sha512-NtdH0kQPhwp9zkbYqVk0bw/tWpGRORLRcQ5SD3ZMZcm6v7UklouulH086C3GUde1Tny0xPkQNNQhCX8atHV95Q==", + "dev": true, "license": "MIT", "dependencies": { - "@tanstack/query-devtools": "5.99.0", + "@tanstack/query-devtools": "5.100.4", "esm-env": "^1.2.1" }, "funding": { @@ -2591,7 +2612,7 @@ "url": "https://github.com/sponsors/tannerlinsley" }, "peerDependencies": { - "@tanstack/svelte-query": "^6.1.16", + "@tanstack/svelte-query": "^6.1.23", "svelte": "^5.25.0" } }, @@ -2599,6 +2620,7 @@ "version": "0.10.2", "resolved": "https://registry.npmjs.org/@tanstack/svelte-store/-/svelte-store-0.10.2.tgz", "integrity": "sha512-6yrYg6ukZZeqPf3CY5+3H0DI6bUoLNwmImgqLNchavK89LtHL4Sel1fDyLtXymMssK4kYrJOQQ9+BBZWZhhTDw==", + "dev": true, "license": "MIT", "dependencies": { "@tanstack/store": "0.9.2" @@ -2615,6 +2637,7 @@ "version": "9.0.0-alpha.10", "resolved": "https://registry.npmjs.org/@tanstack/svelte-table/-/svelte-table-9.0.0-alpha.10.tgz", "integrity": "sha512-H0eAQlpXgK9JYYrgc0cWXVqEJywxkhuYODnVobUZGskFg6J+5PP+7UCjzrY9ftMge6s1hwb2Ipq3u4wPYJz7HA==", + "dev": true, "license": "MIT", "dependencies": { "@tanstack/table-core": "9.0.0-alpha.10" @@ -2634,6 +2657,7 @@ "version": "9.0.0-alpha.10", "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-9.0.0-alpha.10.tgz", "integrity": "sha512-f2kEGGL+d+I7evkhU926cID2MyH7nPI8acAPcwpaAR1DrgTNStAMp3NS+tgMDyrYtc8zd+RyTxC8m+NBhHhFmA==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -2776,19 +2800,21 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/@types/d3-path": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "dev": true, "license": "MIT" }, "node_modules/@types/d3-scale": { "version": "4.0.9", "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "dev": true, "license": "MIT", "dependencies": { "@types/d3-time": "*" @@ -2798,6 +2824,7 @@ "version": "3.1.8", "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", + "dev": true, "license": "MIT", "dependencies": { "@types/d3-path": "*" @@ -2807,6 +2834,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "dev": true, "license": "MIT" }, "node_modules/@types/deep-eql": { @@ -2838,12 +2866,14 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, "license": "MIT" }, "node_modules/@types/hast": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dev": true, "license": "MIT", "dependencies": { "@types/unist": "*" @@ -2860,6 +2890,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "dev": true, "license": "MIT", "dependencies": { "@types/unist": "*" @@ -2876,7 +2907,7 @@ "version": "25.6.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz", "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "undici-types": "~7.19.0" @@ -2911,26 +2942,28 @@ "version": "2.0.7", "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "dev": true, "license": "MIT" }, "node_modules/@types/unist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "dev": true, "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.58.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.2.tgz", - "integrity": "sha512-aC2qc5thQahutKjP+cl8cgN9DWe3ZUqVko30CMSZHnFEHyhOYoZSzkGtAI2mcwZ38xeImDucI4dnqsHiOYuuCw==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.0.tgz", + "integrity": "sha512-HyAZtpdkgZwpq8Sz3FSUvCR4c+ScbuWa9AksK2Jweub7w4M3yTz4O11AqVJzLYjy/B9ZWPyc81I+mOdJU/bDQw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.58.2", - "@typescript-eslint/type-utils": "8.58.2", - "@typescript-eslint/utils": "8.58.2", - "@typescript-eslint/visitor-keys": "8.58.2", + "@typescript-eslint/scope-manager": "8.59.0", + "@typescript-eslint/type-utils": "8.59.0", + "@typescript-eslint/utils": "8.59.0", + "@typescript-eslint/visitor-keys": "8.59.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -2943,7 +2976,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.58.2", + "@typescript-eslint/parser": "^8.59.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } @@ -2959,16 +2992,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.58.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.2.tgz", - "integrity": "sha512-/Zb/xaIDfxeJnvishjGdcR4jmr7S+bda8PKNhRGdljDM+elXhlvN0FyPSsMnLmJUrVG9aPO6dof80wjMawsASg==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.0.tgz", + "integrity": "sha512-TI1XGwKbDpo9tRW8UDIXCOeLk55qe9ZFGs8MTKU6/M08HWTw52DD/IYhfQtOEhEdPhLMT26Ka/x7p70nd3dzDg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.58.2", - "@typescript-eslint/types": "8.58.2", - "@typescript-eslint/typescript-estree": "8.58.2", - "@typescript-eslint/visitor-keys": "8.58.2", + "@typescript-eslint/scope-manager": "8.59.0", + "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/typescript-estree": "8.59.0", + "@typescript-eslint/visitor-keys": "8.59.0", "debug": "^4.4.3" }, "engines": { @@ -2984,14 +3017,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.58.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.2.tgz", - "integrity": "sha512-Cq6UfpZZk15+r87BkIh5rDpi38W4b+Sjnb8wQCPPDDweS/LRCFjCyViEbzHk5Ck3f2QDfgmlxqSa7S7clDtlfg==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.0.tgz", + "integrity": "sha512-Lw5ITrR5s5TbC19YSvlr63ZfLaJoU6vtKTHyB0GQOpX0W7d5/Ir6vUahWi/8Sps/nOukZQ0IB3SmlxZnjaKVnw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.58.2", - "@typescript-eslint/types": "^8.58.2", + "@typescript-eslint/tsconfig-utils": "^8.59.0", + "@typescript-eslint/types": "^8.59.0", "debug": "^4.4.3" }, "engines": { @@ -3006,14 +3039,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.58.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.2.tgz", - "integrity": "sha512-SgmyvDPexWETQek+qzZnrG6844IaO02UVyOLhI4wpo82dpZJY9+6YZCKAMFzXb7qhx37mFK1QcPQ18tud+vo6Q==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.0.tgz", + "integrity": "sha512-UzR16Ut8IpA3Mc4DbgAShlPPkVm8xXMWafXxB0BocaVRHs8ZGakAxGRskF7FId3sdk9lgGD73GSFaWmWFDE4dg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.58.2", - "@typescript-eslint/visitor-keys": "8.58.2" + "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/visitor-keys": "8.59.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3024,9 +3057,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.58.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.2.tgz", - "integrity": "sha512-3SR+RukipDvkkKp/d0jP0dyzuls3DbGmwDpVEc5wqk5f38KFThakqAAO0XMirWAE+kT00oTauTbzMFGPoAzB0A==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.0.tgz", + "integrity": "sha512-91Sbl3s4Kb3SybliIY6muFBmHVv+pYXfybC4Oolp3dvk8BvIE3wOPc+403CWIT7mJNkfQRGtdqghzs2+Z91Tqg==", "dev": true, "license": "MIT", "engines": { @@ -3041,15 +3074,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.58.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.2.tgz", - "integrity": "sha512-Z7EloNR/B389FvabdGeTo2XMs4W9TjtPiO9DAsmT0yom0bwlPyRjkJ1uCdW1DvrrrYP50AJZ9Xc3sByZA9+dcg==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.0.tgz", + "integrity": "sha512-3TRiZaQSltGqGeNrJzzr1+8YcEobKH9rHnqIp/1psfKFmhRQDNMGP5hBufanYTGznwShzVLs3Mz+gDN7HkWfXg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.58.2", - "@typescript-eslint/typescript-estree": "8.58.2", - "@typescript-eslint/utils": "8.58.2", + "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/typescript-estree": "8.59.0", + "@typescript-eslint/utils": "8.59.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -3066,10 +3099,10 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.58.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.2.tgz", - "integrity": "sha512-9TukXyATBQf/Jq9AMQXfvurk+G5R2MwfqQGDR2GzGz28HvY/lXNKGhkY+6IOubwcquikWk5cjlgPvD2uAA7htQ==", - "devOptional": true, + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.0.tgz", + "integrity": "sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A==", + "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3080,16 +3113,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.58.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.2.tgz", - "integrity": "sha512-ELGuoofuhhoCvNbQjFFiobFcGgcDCEm0ThWdmO4Z0UzLqPXS3KFvnEZ+SHewwOYHjM09tkzOWXNTv9u6Gqtyuw==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.0.tgz", + "integrity": "sha512-O9Re9P1BmBLFJyikRbQpLku/QA3/AueZNO9WePLBwQrvkixTmDe8u76B6CYUAITRl/rHawggEqUGn5QIkVRLMw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.58.2", - "@typescript-eslint/tsconfig-utils": "8.58.2", - "@typescript-eslint/types": "8.58.2", - "@typescript-eslint/visitor-keys": "8.58.2", + "@typescript-eslint/project-service": "8.59.0", + "@typescript-eslint/tsconfig-utils": "8.59.0", + "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/visitor-keys": "8.59.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -3108,16 +3141,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.58.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.2.tgz", - "integrity": "sha512-QZfjHNEzPY8+l0+fIXMvuQ2sJlplB4zgDZvA+NmvZsZv3EQwOcc1DuIU1VJUTWZ/RKouBMhDyNaBMx4sWvrzRA==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.0.tgz", + "integrity": "sha512-I1R/K7V07XsMJ12Oaxg/O9GfrysGTmCRhvZJBv0RE0NcULMzjqVpR5kRRQjHsz3J/bElU7HwCO7zkqL+MSUz+g==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.58.2", - "@typescript-eslint/types": "8.58.2", - "@typescript-eslint/typescript-estree": "8.58.2" + "@typescript-eslint/scope-manager": "8.59.0", + "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/typescript-estree": "8.59.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3132,13 +3165,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.58.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.2.tgz", - "integrity": "sha512-f1WO2Lx8a9t8DARmcWAUPJbu0G20bJlj8L4z72K00TMeJAoyLr/tHhI/pzYBLrR4dXWkcxO1cWYZEOX8DKHTqA==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.0.tgz", + "integrity": "sha512-/uejZt4dSere1bx12WLlPfv8GktzcaDtuJ7s42/HEZ5zGj9oxRaD4bj7qwSunXkf+pbAhFt2zjpHYUiT5lHf0Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/types": "8.59.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -3166,6 +3199,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, "license": "ISC" }, "node_modules/@vitest/expect": { @@ -3186,13 +3220,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.4.tgz", - "integrity": "sha512-R9HTZBhW6yCSGbGQnDnH3QHfJxokKN4KB+Yvk9Q1le7eQNYwiCyKxmLmurSpFy6BzJanSLuEUDrD+j97Q+ZLPg==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.5.tgz", + "integrity": "sha512-/x2EmFC4mT4NNzqvC3fmesuV97w5FC903KPmey4gsnJiMQ3Be1IlDKVaDaG8iqaLFHqJ2FVEkxZk5VmeLjIItw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.4", + "@vitest/spy": "4.1.5", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -3213,9 +3247,9 @@ } }, "node_modules/@vitest/mocker/node_modules/@vitest/spy": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.4.tgz", - "integrity": "sha512-XxNdAsKW7C+FLydqFJLb5KhJtl3PGCMmYwFRfhvIgxJvLSXhhVI1zM8f1qD3Zg7RCjTSzDVyct6sghs9UEgBEQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.5.tgz", + "integrity": "sha512-2lNOsh6+R2Idnf1TCZqSwYlKN2E/iDlD8sgU59kYVl+OMDmvldO1VDk39smRfpUNwYpNRVn3w4YfuC7KfbBnkQ==", "dev": true, "license": "MIT", "funding": { @@ -3236,13 +3270,13 @@ } }, "node_modules/@vitest/runner": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.4.tgz", - "integrity": "sha512-xTp7VZ5aXP5ZJrn15UtJUWlx6qXLnGtF6jNxHepdPHpMfz/aVPx+htHtgcAL2mDXJgKhpoo2e9/hVJsIeFbytQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.5.tgz", + "integrity": "sha512-2D+o7Pr82IEO46YPpoA/YU0neeyr6FTerQb5Ro7BUnBuv6NQtT/kmVnczngiMEBhzgqz2UZYl5gArejsyERDSQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.4", + "@vitest/utils": "4.1.5", "pathe": "^2.0.3" }, "funding": { @@ -3250,9 +3284,9 @@ } }, "node_modules/@vitest/runner/node_modules/@vitest/pretty-format": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.4.tgz", - "integrity": "sha512-ddmDHU0gjEUyEVLxtZa7xamrpIefdEETu3nZjWtHeZX4QxqJ7tRxSteHVXJOcr8jhiLoGAhkK4WJ3WqBpjx42A==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.5.tgz", + "integrity": "sha512-7I3q6l5qr03dVfMX2wCo9FxwSJbPdwKjy2uu/YPpU3wfHvIL4QHwVRp57OfGrDFeUJ8/8QdfBKIV12FTtLn00g==", "dev": true, "license": "MIT", "dependencies": { @@ -3263,13 +3297,13 @@ } }, "node_modules/@vitest/runner/node_modules/@vitest/utils": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.4.tgz", - "integrity": "sha512-13QMT+eysM5uVGa1rG4kegGYNp6cnQcsTc67ELFbhNLQO+vgsygtYJx2khvdt4gVQqSSpC/KT5FZZxUpP3Oatw==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.5.tgz", + "integrity": "sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.4", + "@vitest/pretty-format": "4.1.5", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, @@ -3288,14 +3322,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.4.tgz", - "integrity": "sha512-MCjCFgaS8aZz+m5nTcEcgk/xhWv0rEH4Yl53PPlMXOZ1/Ka2VcZU6CJ+MgYCZbcJvzGhQRjVrGQNZqkGPttIKw==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.5.tgz", + "integrity": "sha512-zypXEt4KH/XgKGPUz4eC2AvErYx0My5hfL8oDb1HzGFpEk1P62bxSohdyOmvz+d9UJwanI68MKwr2EquOaOgMQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.4", - "@vitest/utils": "4.1.4", + "@vitest/pretty-format": "4.1.5", + "@vitest/utils": "4.1.5", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -3304,9 +3338,9 @@ } }, "node_modules/@vitest/snapshot/node_modules/@vitest/pretty-format": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.4.tgz", - "integrity": "sha512-ddmDHU0gjEUyEVLxtZa7xamrpIefdEETu3nZjWtHeZX4QxqJ7tRxSteHVXJOcr8jhiLoGAhkK4WJ3WqBpjx42A==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.5.tgz", + "integrity": "sha512-7I3q6l5qr03dVfMX2wCo9FxwSJbPdwKjy2uu/YPpU3wfHvIL4QHwVRp57OfGrDFeUJ8/8QdfBKIV12FTtLn00g==", "dev": true, "license": "MIT", "dependencies": { @@ -3317,13 +3351,13 @@ } }, "node_modules/@vitest/snapshot/node_modules/@vitest/utils": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.4.tgz", - "integrity": "sha512-13QMT+eysM5uVGa1rG4kegGYNp6cnQcsTc67ELFbhNLQO+vgsygtYJx2khvdt4gVQqSSpC/KT5FZZxUpP3Oatw==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.5.tgz", + "integrity": "sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.4", + "@vitest/pretty-format": "4.1.5", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, @@ -3380,6 +3414,7 @@ "version": "8.16.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -3495,6 +3530,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, "license": "Apache-2.0", "engines": { "node": ">= 0.4" @@ -3521,9 +3557,10 @@ } }, "node_modules/bits-ui": { - "version": "2.17.3", - "resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-2.17.3.tgz", - "integrity": "sha512-Bef41uY9U2jaBJHPhcPvmBNkGec5Wx2z6eioDsTmsaR2vH4QoaOcPi75gzCG3+/2TNr6v/qBwzgWNPYCxNtrEA==", + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-2.18.0.tgz", + "integrity": "sha512-GLOBZRVy3hxNHIQ2MpD/+5aK9KcBFZRhUJtZ1UDABXdlVR4K6zFpgt4T+Rwuhf2sQzlc6yK1q/DprHPjwT4Pjw==", + "dev": true, "license": "MIT", "dependencies": { "@floating-ui/core": "^1.7.1", @@ -3548,6 +3585,7 @@ "version": "0.35.1", "resolved": "https://registry.npmjs.org/runed/-/runed-0.35.1.tgz", "integrity": "sha512-2F4Q/FZzbeJTFdIS/PuOoPRSm92sA2LhzTnv6FXhCoENb3huf5+fDuNOg1LNvGOouy3u/225qxmuJvcV3IZK5Q==", + "dev": true, "funding": [ "https://github.com/sponsors/huntabyte", "https://github.com/sponsors/tglide" @@ -3667,6 +3705,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "dev": true, "license": "MIT", "funding": { "type": "github", @@ -3694,6 +3733,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "dev": true, "license": "MIT", "funding": { "type": "github", @@ -3704,6 +3744,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "dev": true, "license": "MIT", "funding": { "type": "github", @@ -3812,6 +3853,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -3841,6 +3883,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "dev": true, "license": "MIT", "funding": { "type": "github", @@ -3851,6 +3894,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, "license": "MIT", "engines": { "node": ">= 10" @@ -3884,7 +3928,7 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -3969,6 +4013,7 @@ "version": "3.2.4", "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "dev": true, "license": "ISC", "dependencies": { "internmap": "1 - 2" @@ -3981,6 +4026,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -3990,6 +4036,7 @@ "version": "6.0.4", "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "dev": true, "license": "ISC", "dependencies": { "delaunator": "5" @@ -4002,6 +4049,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -4011,6 +4059,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "dev": true, "license": "ISC", "dependencies": { "commander": "7", @@ -4036,6 +4085,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "dev": true, "license": "ISC", "dependencies": { "d3-dispatch": "1 - 3", @@ -4050,6 +4100,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -4059,6 +4110,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "dev": true, "license": "ISC", "dependencies": { "d3-array": "2.5.0 - 3" @@ -4071,6 +4123,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/d3-geo-voronoi/-/d3-geo-voronoi-2.1.0.tgz", "integrity": "sha512-kqE4yYuOjPbKdBXG0xztCacPwkVSK2REF1opSNrnqqtXJmNcM++UbwQ8SxvwP6IQTj9RvIjjK4qeiVsEfj0Z2Q==", + "dev": true, "license": "ISC", "dependencies": { "d3-array": "3", @@ -4086,6 +4139,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -4095,6 +4149,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dev": true, "license": "ISC", "dependencies": { "d3-color": "1 - 3" @@ -4107,12 +4162,14 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/d3-interpolate-path/-/d3-interpolate-path-2.3.0.tgz", "integrity": "sha512-tZYtGXxBmbgHsIc9Wms6LS5u4w6KbP8C09a4/ZYc4KLMYYqub57rRBUgpUr2CIarIrJEpdAWWxWQvofgaMpbKQ==", + "dev": true, "license": "BSD-3-Clause" }, "node_modules/d3-path": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -4122,6 +4179,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -4131,6 +4189,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -4140,6 +4199,7 @@ "version": "0.12.3", "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", + "dev": true, "license": "BSD-3-Clause", "dependencies": { "d3-array": "1 - 2", @@ -4150,6 +4210,7 @@ "version": "2.12.1", "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "dev": true, "license": "BSD-3-Clause", "dependencies": { "internmap": "^1.0.0" @@ -4159,12 +4220,14 @@ "version": "1.0.9", "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==", + "dev": true, "license": "BSD-3-Clause" }, "node_modules/d3-sankey/node_modules/d3-shape": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "dev": true, "license": "BSD-3-Clause", "dependencies": { "d3-path": "1" @@ -4174,12 +4237,14 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", + "dev": true, "license": "ISC" }, "node_modules/d3-scale": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dev": true, "license": "ISC", "dependencies": { "d3-array": "2.10.0 - 3", @@ -4196,6 +4261,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "dev": true, "license": "ISC", "dependencies": { "d3-color": "1 - 3", @@ -4209,6 +4275,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dev": true, "license": "ISC", "dependencies": { "d3-path": "^3.1.0" @@ -4221,12 +4288,14 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/d3-tile/-/d3-tile-1.0.0.tgz", "integrity": "sha512-79fnTKpPMPDS5xQ0xuS9ir0165NEwwkFpe/DSOmc2Gl9ldYzKKRDWogmTTE8wAJ8NA7PMapNfEcyKhI9Lxdu5Q==", + "dev": true, "license": "BSD-3-Clause" }, "node_modules/d3-time": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dev": true, "license": "ISC", "dependencies": { "d3-array": "2 - 3" @@ -4239,6 +4308,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dev": true, "license": "ISC", "dependencies": { "d3-time": "1 - 3" @@ -4251,6 +4321,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -4260,6 +4331,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/d3-tricontour/-/d3-tricontour-1.1.0.tgz", "integrity": "sha512-G7gHKj89n2owmkGb6WX6ixcnQ0Kf/0wpa9VIh9DGdbHu8wdrlaHU4ir3/bFNERl8N8nn4G7e7qbtBG8N9caihQ==", + "dev": true, "license": "ISC", "dependencies": { "d3-delaunay": "6", @@ -4287,13 +4359,14 @@ "version": "1.11.20", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.20.tgz", "integrity": "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==", + "dev": true, "license": "MIT" }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -4357,7 +4430,7 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -4417,6 +4490,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", + "dev": true, "license": "ISC", "dependencies": { "robust-predicates": "^3.0.2" @@ -4426,6 +4500,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -4442,7 +4517,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "engines": { "node": ">=8" @@ -4452,12 +4527,14 @@ "version": "5.6.4", "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.4.tgz", "integrity": "sha512-Gp6rDldRsFh/7XuouDbxMH3Mx8GMCcgzIb1pDTvNyn8pZGQ22u+Wa+lGV9dQCltFQ7uVw0MhRyb8XDskNFOReA==", + "dev": true, "license": "MIT" }, "node_modules/devlop": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dev": true, "license": "MIT", "dependencies": { "dequal": "^2.0.0" @@ -4475,9 +4552,10 @@ "license": "MIT" }, "node_modules/dompurify": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.0.tgz", - "integrity": "sha512-nolgK9JcaUXMSmW+j1yaSvaEaoXYHwWyGJlkoCTghc97KgGDDSnpoU/PlEnw63Ah+TGKFOyY+X5LnxaWbCSfXg==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.1.tgz", + "integrity": "sha512-JahakDAIg1gyOm7dlgWSDjV4n7Ip2PKR55NIT6jrMfIgLFgWo81vdr1/QGqWtFNRqXP9UV71oVePtjqS2ebnPw==", + "dev": true, "license": "(MPL-2.0 OR Apache-2.0)", "optionalDependencies": { "@types/trusted-types": "^2.0.7" @@ -4504,14 +4582,14 @@ "license": "MIT" }, "node_modules/enhanced-resolve": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", - "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.0.tgz", + "integrity": "sha512-otxSQPw4lkOZWkHpB3zaEQs6gWYEsmX4xQF68ElXC/TWvGxGMSGOvoNbaLXm6/cS/fSfHtsEdw90y20PCd+sCA==", "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", - "tapable": "^2.3.0" + "tapable": "^2.3.3" }, "engines": { "node": ">=10.13.0" @@ -4534,6 +4612,7 @@ "version": "2.1.4", "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "dev": true, "license": "MIT", "dependencies": { "stackframe": "^1.3.4" @@ -4568,7 +4647,7 @@ "version": "0.27.1", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.1.tgz", "integrity": "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==", - "devOptional": true, + "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -4630,18 +4709,18 @@ } }, "node_modules/eslint": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.2.0.tgz", - "integrity": "sha512-+L0vBFYGIpSNIt/KWTpFonPrqYvgKw1eUI5Vn7mEogrQcWtWYtNQ7dNqC+px/J0idT3BAkiWrhfS7k+Tum8TUA==", + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.2.1.tgz", + "integrity": "sha512-wiyGaKsDgqXvF40P8mDwiUp/KQjE1FdrIEJsM8PZ3XCiniTMXS3OHWWUe5FI5agoCnr8x4xPrTDZuxsBlNHl+Q==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", - "@eslint/config-array": "^0.23.4", - "@eslint/config-helpers": "^0.5.4", - "@eslint/core": "^1.2.0", - "@eslint/plugin-kit": "^0.7.0", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.5.5", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -4702,13 +4781,13 @@ } }, "node_modules/eslint-plugin-perfectionist": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-perfectionist/-/eslint-plugin-perfectionist-5.8.0.tgz", - "integrity": "sha512-k8uIptWIxkUclonCFGyDzgYs9NI+Qh0a7cUXS3L7IYZDEsjXuimFBVbxXPQQngWqMiaxJRwbtYB4smMGMqF+cw==", + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-perfectionist/-/eslint-plugin-perfectionist-5.9.0.tgz", + "integrity": "sha512-8TWzg02zmnBdZwCkWLi8jhzqXI+fE7Z/RwV8SL6xD45tJ8Bp3wGuYL2XtQgfe/Wd0eBqOUX+s6ey73IyszvKTA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/utils": "^8.58.0", + "@typescript-eslint/utils": "^8.58.2", "natural-orderby": "^5.0.0" }, "engines": { @@ -4733,9 +4812,9 @@ } }, "node_modules/eslint-plugin-svelte": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-3.17.0.tgz", - "integrity": "sha512-sF6wgd5FLS2P8CCaOy2HdYYYEcZ6TwL251dLHUkNmtLnWECk1Dwc+j6VeulmmnFxr7Xs0WNtjweOA+bJ0PnaFw==", + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-3.17.1.tgz", + "integrity": "sha512-NyiXHtS3Ni7e532RBwS9OXlMKDIrENg3gY+/+ODjZzQx2xhU3NlJ+nIl1a93iUUQeiJL3lS8KLmY+W8hklzweQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4863,6 +4942,7 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==", + "dev": true, "license": "MIT" }, "node_modules/espree": { @@ -5042,7 +5122,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" @@ -5121,6 +5201,7 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -5206,6 +5287,7 @@ "version": "9.0.5", "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", + "dev": true, "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -5229,6 +5311,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dev": true, "license": "MIT", "dependencies": { "@types/hast": "^3.0.0" @@ -5255,6 +5338,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "dev": true, "license": "MIT", "funding": { "type": "github", @@ -5272,6 +5356,7 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -5314,12 +5399,14 @@ "version": "0.2.7", "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", + "dev": true, "license": "MIT" }, "node_modules/internmap": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -5404,6 +5491,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", + "dev": true, "license": "MIT", "dependencies": { "@types/estree": "^1.0.6" @@ -5436,7 +5524,7 @@ "version": "2.6.1", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", - "devOptional": true, + "dev": true, "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" @@ -5541,6 +5629,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -5560,6 +5649,7 @@ "version": "0.0.26", "resolved": "https://registry.npmjs.org/kit-query-params/-/kit-query-params-0.0.26.tgz", "integrity": "sha512-Tvb2QUgzSKCkxwn0zPv/nswsbuA8ExI9vqoUzgHbF0nkEUMW8YwQLMufEV5kCLoyeMN4Z3noAVdVhZderpGUQw==", + "dev": true, "peerDependencies": { "svelte": "^5.0.0-next.1" } @@ -5568,7 +5658,7 @@ "version": "4.1.5", "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -5585,6 +5675,7 @@ "version": "2.0.0-next.46", "resolved": "https://registry.npmjs.org/layerchart/-/layerchart-2.0.0-next.46.tgz", "integrity": "sha512-fpdnvexCG/chonAIunDdLbqrUrD0IZ2v6MG/kbI5v7/yi0bIC47er1Kz31sACQNuoK+sxMIvWeebMHqiQP5XSQ==", + "dev": true, "license": "MIT", "dependencies": { "@dagrejs/dagre": "^2.0.4", @@ -5636,7 +5727,7 @@ "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", - "devOptional": true, + "dev": true, "license": "MPL-2.0", "dependencies": { "detect-libc": "^2.0.3" @@ -5669,6 +5760,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -5689,6 +5781,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -5709,6 +5802,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -5729,6 +5823,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -5749,6 +5844,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -5769,6 +5865,7 @@ "cpu": [ "arm64" ], + "dev": true, "libc": [ "glibc" ], @@ -5792,6 +5889,7 @@ "cpu": [ "arm64" ], + "dev": true, "libc": [ "musl" ], @@ -5815,6 +5913,7 @@ "cpu": [ "x64" ], + "dev": true, "libc": [ "glibc" ], @@ -5838,6 +5937,7 @@ "cpu": [ "x64" ], + "dev": true, "libc": [ "musl" ], @@ -5861,6 +5961,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -5881,6 +5982,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -5908,6 +6010,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", + "dev": true, "license": "MIT" }, "node_modules/locate-path": { @@ -5947,6 +6050,7 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, "license": "MIT", "bin": { "lz-string": "bin/bin.js" @@ -5956,6 +6060,7 @@ "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" @@ -5965,6 +6070,7 @@ "version": "13.2.1", "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "dev": true, "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -5993,6 +6099,7 @@ "version": "10.2.0", "resolved": "https://registry.npmjs.org/memoize/-/memoize-10.2.0.tgz", "integrity": "sha512-DeC6b7QBrZsRs3Y02A6A7lQyzFbsQbqgjI6UW0GigGWV+u1s25TycMr0XHZE4cJce7rY/vyw2ctMQqfDkIhUEA==", + "dev": true, "license": "MIT", "dependencies": { "mimic-function": "^5.0.1" @@ -6008,6 +6115,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "dev": true, "funding": [ { "type": "GitHub Sponsors", @@ -6028,6 +6136,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "dev": true, "funding": [ { "type": "GitHub Sponsors", @@ -6044,6 +6153,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "dev": true, "funding": [ { "type": "GitHub Sponsors", @@ -6065,6 +6175,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "dev": true, "funding": [ { "type": "GitHub Sponsors", @@ -6081,6 +6192,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "dev": true, "funding": [ { "type": "GitHub Sponsors", @@ -6097,6 +6209,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -6145,6 +6258,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/mode-watcher/-/mode-watcher-1.1.0.tgz", "integrity": "sha512-mUT9RRGPDYenk59qJauN1rhsIMKBmWA3xMF+uRwE8MW/tjhaDSCCARqkSuDTq8vr4/2KcAxIGVjACxTjdk5C3g==", + "dev": true, "license": "MIT", "dependencies": { "runed": "^0.25.0", @@ -6158,6 +6272,7 @@ "version": "0.25.0", "resolved": "https://registry.npmjs.org/runed/-/runed-0.25.0.tgz", "integrity": "sha512-7+ma4AG9FT2sWQEA0Egf6mb7PBT2vHyuHail1ie8ropfSjvZGtEAx8YTmUjv/APCsdRRxEVvArNjALk9zFSOrg==", + "dev": true, "funding": [ "https://github.com/sponsors/huntabyte", "https://github.com/sponsors/tglide" @@ -6173,6 +6288,7 @@ "version": "0.7.1", "resolved": "https://registry.npmjs.org/svelte-toolbelt/-/svelte-toolbelt-0.7.1.tgz", "integrity": "sha512-HcBOcR17Vx9bjaOceUvxkY3nGmbBmCBBbuWLLEWO6jtmWH8f/QoWmbyUfQZrpDINH39en1b8mptfPQT9VKQ1xQ==", + "dev": true, "funding": [ "https://github.com/sponsors/huntabyte" ], @@ -6193,6 +6309,7 @@ "version": "0.23.4", "resolved": "https://registry.npmjs.org/runed/-/runed-0.23.4.tgz", "integrity": "sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA==", + "dev": true, "funding": [ "https://github.com/sponsors/huntabyte", "https://github.com/sponsors/tglide" @@ -6218,7 +6335,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -6228,14 +6345,14 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -6470,7 +6587,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", - "devOptional": true, + "dev": true, "funding": [ "https://github.com/sponsors/sxzz", "https://opencollective.com/debug" @@ -6488,6 +6605,7 @@ "version": "3.5.0", "resolved": "https://registry.npmjs.org/oidc-client-ts/-/oidc-client-ts-3.5.0.tgz", "integrity": "sha512-l2q8l9CTCTOlbX+AnK4p3M+4CEpKpyQhle6blQkdFhm0IsBqsxm15bYaSa11G7pWdsYr6epdsRZxJpCyCRbT8A==", + "dev": true, "license": "Apache-2.0", "dependencies": { "jwt-decode": "^4.0.0" @@ -6500,12 +6618,14 @@ "version": "0.12.1", "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.1.tgz", "integrity": "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==", + "dev": true, "license": "MIT" }, "node_modules/oniguruma-to-es": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.4.tgz", "integrity": "sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA==", + "dev": true, "license": "MIT", "dependencies": { "oniguruma-parser": "^0.12.1", @@ -6593,6 +6713,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -6662,14 +6783,14 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "devOptional": true, + "dev": true, "license": "ISC" }, "node_modules/picomatch": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -6726,7 +6847,7 @@ "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "devOptional": true, + "dev": true, "funding": [ { "type": "opencollective", @@ -7007,6 +7128,7 @@ "version": "9.3.0", "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz", "integrity": "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==", + "dev": true, "license": "MIT", "dependencies": { "parse-ms": "^4.0.0" @@ -7022,6 +7144,7 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "dev": true, "license": "MIT", "funding": { "type": "github", @@ -7148,6 +7271,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", + "dev": true, "license": "MIT", "dependencies": { "regex-utilities": "^2.3.0" @@ -7157,6 +7281,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", + "dev": true, "license": "MIT", "dependencies": { "regex-utilities": "^2.3.0" @@ -7166,6 +7291,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", + "dev": true, "license": "MIT" }, "node_modules/require-directory": { @@ -7192,13 +7318,14 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", + "dev": true, "license": "Unlicense" }, "node_modules/rollup": { "version": "4.53.3", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@types/estree": "1.0.8" @@ -7253,6 +7380,7 @@ "version": "0.37.1", "resolved": "https://registry.npmjs.org/runed/-/runed-0.37.1.tgz", "integrity": "sha512-MeFY73xBW8IueWBm012nNFIGy19WUGPLtknavyUPMpnyt350M47PhGSGrGoSLbidwn+Zlt/O0cp8/OZE3LASWA==", + "dev": true, "funding": [ "https://github.com/sponsors/huntabyte", "https://github.com/sponsors/tglide" @@ -7281,6 +7409,7 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "dev": true, "license": "BSD-3-Clause" }, "node_modules/sade": { @@ -7300,6 +7429,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, "license": "MIT" }, "node_modules/saxes": { @@ -7346,7 +7476,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.0.1.tgz", "integrity": "sha512-n7Z7dXZhJbwuAHhNzkTti6Aw9QDDjZtm3JTpTGATIdNzdQz5GuFs22w90BcvF4INfnrL5xrX3oGsuqO5Dx3A1Q==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/shebang-command": { @@ -7376,6 +7506,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/shiki/-/shiki-4.0.2.tgz", "integrity": "sha512-eAVKTMedR5ckPo4xne/PjYQYrU3qx78gtJZ+sHlXEg5IHhhoQhMfZVzetTYuaJS0L2Ef3AcCRzCHV8T0WI6nIQ==", + "dev": true, "license": "MIT", "dependencies": { "@shikijs/core": "4.0.2", @@ -7462,7 +7593,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@polka/url": "^1.0.0-next.24", @@ -7477,6 +7608,7 @@ "version": "0.5.6", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", "integrity": "sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==", + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -7486,7 +7618,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "devOptional": true, + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -7496,6 +7628,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "dev": true, "license": "MIT", "funding": { "type": "github", @@ -7506,6 +7639,7 @@ "version": "2.0.10", "resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.10.tgz", "integrity": "sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==", + "dev": true, "license": "MIT", "dependencies": { "stackframe": "^1.3.4" @@ -7522,12 +7656,14 @@ "version": "1.3.4", "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", + "dev": true, "license": "MIT" }, "node_modules/stacktrace-gps": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/stacktrace-gps/-/stacktrace-gps-3.1.2.tgz", "integrity": "sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==", + "dev": true, "license": "MIT", "dependencies": { "source-map": "0.5.6", @@ -7538,6 +7674,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/stacktrace-js/-/stacktrace-js-2.0.2.tgz", "integrity": "sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==", + "dev": true, "license": "MIT", "dependencies": { "error-stack-parser": "^2.0.6", @@ -7621,6 +7758,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "dev": true, "license": "MIT", "dependencies": { "character-entities-html4": "^2.0.0", @@ -7677,15 +7815,17 @@ "version": "1.0.14", "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", + "dev": true, "license": "MIT", "dependencies": { "inline-style-parser": "0.2.7" } }, "node_modules/svelte": { - "version": "5.55.4", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.55.4.tgz", - "integrity": "sha512-q8DFohk6vUswSng95IZb9nzWJnbINZsK7OiM1snAa3qCjJBL0ZQpvMyAaVXjUukdM75J/m8UE8xwqat8Ors/zQ==", + "version": "5.55.5", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.55.5.tgz", + "integrity": "sha512-2uCs/LZ9us+AktdzYJM8OcxQ8qnPS1kpaO7syGT/MgO+6Qr1Ybl+TqPq+97u7PHqmmMlye5ZkoyXONy5mjjAbw==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/remapping": "^2.3.4", @@ -7812,6 +7952,7 @@ "version": "0.0.35", "resolved": "https://registry.npmjs.org/svelte-intercom/-/svelte-intercom-0.0.35.tgz", "integrity": "sha512-iJ1CYQW110titaAD6EjZqOl5LWRJQR9T2Np4b4NkT1y560w9d814mlBEH0djGo5IfDaWH3vT12eGIPK0nxxP3Q==", + "dev": true, "dependencies": { "@intercom/messenger-js-sdk": "^0.0.14" }, @@ -7820,9 +7961,10 @@ } }, "node_modules/svelte-sonner": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/svelte-sonner/-/svelte-sonner-1.1.0.tgz", - "integrity": "sha512-3lYM6ZIqWe+p9vwwWHGWP/ZdvHiUtzURsud2quIxivrX4rvpXh6i+geBGn0m3JS6KwW6W8VgbOl3xQMcDuh6gg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/svelte-sonner/-/svelte-sonner-1.1.1.tgz", + "integrity": "sha512-5cd3p7wa4cq0NsqslMwdlPb7x1JglEZ/GKrLePWNr5bCxR1nagAVrY01FRFrXfUGs41miLt3C327+8XJo5BzZw==", + "dev": true, "license": "MIT", "dependencies": { "runed": "^0.28.0" @@ -7835,6 +7977,7 @@ "version": "0.28.0", "resolved": "https://registry.npmjs.org/runed/-/runed-0.28.0.tgz", "integrity": "sha512-k2xx7RuO9hWcdd9f+8JoBeqWtYrm5CALfgpkg2YDB80ds/QE4w0qqu34A7fqiAwiBBSBQOid7TLxwxVC27ymWQ==", + "dev": true, "funding": [ "https://github.com/sponsors/huntabyte", "https://github.com/sponsors/tglide" @@ -7851,6 +7994,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/svelte-time/-/svelte-time-2.1.0.tgz", "integrity": "sha512-uSCWayqLfbu+L8dQ92SX8fn0+JgllgBvZEcgips8flkmQ0iTzTCIBrW4XokkEqq92CRq4Vbu28pTneFxOBu2FA==", + "dev": true, "license": "MIT", "dependencies": { "dayjs": "^1.11.13" @@ -7860,6 +8004,7 @@ "version": "0.10.6", "resolved": "https://registry.npmjs.org/svelte-toolbelt/-/svelte-toolbelt-0.10.6.tgz", "integrity": "sha512-YWuX+RE+CnWYx09yseAe4ZVMM7e7GRFZM6OYWpBKOb++s+SQ8RBIMMe+Bs/CznBMc0QPLjr+vDBxTAkozXsFXQ==", + "dev": true, "funding": [ "https://github.com/sponsors/huntabyte" ], @@ -7880,6 +8025,7 @@ "version": "0.35.1", "resolved": "https://registry.npmjs.org/runed/-/runed-0.35.1.tgz", "integrity": "sha512-2F4Q/FZzbeJTFdIS/PuOoPRSm92sA2LhzTnv6FXhCoENb3huf5+fDuNOg1LNvGOouy3u/225qxmuJvcV3IZK5Q==", + "dev": true, "funding": [ "https://github.com/sponsors/huntabyte", "https://github.com/sponsors/tglide" @@ -7904,6 +8050,7 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.1.tgz", "integrity": "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==", + "dev": true, "license": "Apache-2.0", "engines": { "node": ">= 0.4" @@ -7913,6 +8060,7 @@ "version": "2.2.5", "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.2.5.tgz", "integrity": "sha512-/yLB1538mag+dn0wsePTe8C0rDIjUOaJpMs2McodSzmM2msWcZsBSdRtg6HOBt0A/r82BN+Md3pgwSc/uWt2Ig==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" @@ -7934,15 +8082,15 @@ "license": "ISC" }, "node_modules/swagger-typescript-api": { - "version": "13.6.10", - "resolved": "https://registry.npmjs.org/swagger-typescript-api/-/swagger-typescript-api-13.6.10.tgz", - "integrity": "sha512-X+DenKK2txtI0LdNcpmWddcZWiWaf6erIZ+m9Wz7vk/yMFmPJ9EljSuX+dnJQ9YuzjV1imaRCDKsHA4CG2wUZA==", + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/swagger-typescript-api/-/swagger-typescript-api-13.7.0.tgz", + "integrity": "sha512-2j8L3pGBqaOCzzkgVZoX6/D/KFCo2lUiN+0LiDNxI9HkFSXAg/GpMMe2G8eF7+bFSs3mM107gD0uckT7dYjShw==", "dev": true, "license": "MIT", "dependencies": { "@apidevtools/swagger-parser": "12.1.0", "@biomejs/js-api": "4.0.0", - "@biomejs/wasm-nodejs": "2.4.12", + "@biomejs/wasm-nodejs": "2.4.13", "@types/swagger-schema-official": "^2.0.25", "c12": "^3.3.3", "citty": "^0.2.1", @@ -7956,7 +8104,7 @@ "type-fest": "^5.4.4", "typescript": "~6.0.2", "yaml": "^2.8.2", - "yummies": "7.18.0" + "yummies": "7.19.2" }, "bin": { "sta": "dist/cli.mjs", @@ -8050,6 +8198,7 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.3.0.tgz", "integrity": "sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==", + "dev": true, "license": "MIT" }, "node_modules/tagged-tag": { @@ -8069,6 +8218,7 @@ "version": "3.5.0", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.5.0.tgz", "integrity": "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==", + "dev": true, "license": "MIT", "funding": { "type": "github", @@ -8079,6 +8229,7 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/tailwind-variants/-/tailwind-variants-3.2.2.tgz", "integrity": "sha512-Mi4kHeMTLvKlM98XPnK+7HoBPmf4gygdFmqQPaDivc3DpYS6aIY6KiG/PgThrGvii5YZJqRsPz0aPyhoFzmZgg==", + "dev": true, "license": "MIT", "engines": { "node": ">=16.x", @@ -8095,15 +8246,16 @@ } }, "node_modules/tailwindcss": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.2.tgz", - "integrity": "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.4.tgz", + "integrity": "sha512-HhKppgO81FQof5m6TEnuBWCZGgfRAWbaeOaGT00KOy/Pf/j6oUihdvBpA7ltCeAvZpFhW3j0PTclkxsd4IXYDA==", + "dev": true, "license": "MIT" }, "node_modules/tapable": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", - "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", "dev": true, "license": "MIT", "engines": { @@ -8118,6 +8270,7 @@ "version": "5.0.2", "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.2.tgz", "integrity": "sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==", + "dev": true, "license": "MIT", "engines": { "node": ">=12.22" @@ -8151,7 +8304,7 @@ "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", @@ -8208,7 +8361,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -8244,6 +8397,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "dev": true, "license": "MIT", "funding": { "type": "github", @@ -8277,12 +8431,14 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, "license": "0BSD" }, "node_modules/tw-animate-css": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.4.0.tgz", "integrity": "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==", + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/Wombosvideo" @@ -8315,10 +8471,10 @@ } }, "node_modules/typescript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz", - "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==", - "devOptional": true, + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -8329,16 +8485,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.58.2", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.2.tgz", - "integrity": "sha512-V8iSng9mRbdZjl54VJ9NKr6ZB+dW0J3TzRXRGcSbLIej9jV86ZRtlYeTKDR/QLxXykocJ5icNzbsl2+5TzIvcQ==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.0.tgz", + "integrity": "sha512-BU3ONW9X+v90EcCH9ZS6LMackcVtxRLlI3XrYyqZIwVSHIk7Qf7bFw1z0M9Q0IUxhTMZCf8piY9hTYaNEIASrw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.58.2", - "@typescript-eslint/parser": "8.58.2", - "@typescript-eslint/typescript-estree": "8.58.2", - "@typescript-eslint/utils": "8.58.2" + "@typescript-eslint/eslint-plugin": "8.59.0", + "@typescript-eslint/parser": "8.59.0", + "@typescript-eslint/typescript-estree": "8.59.0", + "@typescript-eslint/utils": "8.59.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -8366,13 +8522,14 @@ "version": "7.19.2", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz", "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/unist-util-is": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "dev": true, "license": "MIT", "dependencies": { "@types/unist": "^3.0.0" @@ -8386,6 +8543,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dev": true, "license": "MIT", "dependencies": { "@types/unist": "^3.0.0" @@ -8399,6 +8557,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dev": true, "license": "MIT", "dependencies": { "@types/unist": "^3.0.0" @@ -8412,6 +8571,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "dev": true, "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -8427,6 +8587,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "dev": true, "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -8494,6 +8655,7 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dev": true, "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -8508,6 +8670,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "dev": true, "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", @@ -8522,7 +8685,7 @@ "version": "7.3.2", "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.2.tgz", "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.27.0", @@ -8597,6 +8760,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -8611,7 +8775,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.1.tgz", "integrity": "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==", - "devOptional": true, + "dev": true, "license": "MIT", "workspaces": [ "tests/deps/*", @@ -8628,19 +8792,19 @@ } }, "node_modules/vitest": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.4.tgz", - "integrity": "sha512-tFuJqTxKb8AvfyqMfnavXdzfy3h3sWZRWwfluGbkeR7n0HUev+FmNgZ8SDrRBTVrVCjgH5cA21qGbCffMNtWvg==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.5.tgz", + "integrity": "sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.4", - "@vitest/mocker": "4.1.4", - "@vitest/pretty-format": "4.1.4", - "@vitest/runner": "4.1.4", - "@vitest/snapshot": "4.1.4", - "@vitest/spy": "4.1.4", - "@vitest/utils": "4.1.4", + "@vitest/expect": "4.1.5", + "@vitest/mocker": "4.1.5", + "@vitest/pretty-format": "4.1.5", + "@vitest/runner": "4.1.5", + "@vitest/snapshot": "4.1.5", + "@vitest/spy": "4.1.5", + "@vitest/utils": "4.1.5", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -8668,12 +8832,12 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.4", - "@vitest/browser-preview": "4.1.4", - "@vitest/browser-webdriverio": "4.1.4", - "@vitest/coverage-istanbul": "4.1.4", - "@vitest/coverage-v8": "4.1.4", - "@vitest/ui": "4.1.4", + "@vitest/browser-playwright": "4.1.5", + "@vitest/browser-preview": "4.1.5", + "@vitest/browser-webdriverio": "4.1.5", + "@vitest/coverage-istanbul": "4.1.5", + "@vitest/coverage-v8": "4.1.5", + "@vitest/ui": "4.1.5", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -8732,16 +8896,16 @@ } }, "node_modules/vitest/node_modules/@vitest/expect": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.4.tgz", - "integrity": "sha512-iPBpra+VDuXmBFI3FMKHSFXp3Gx5HfmSCE8X67Dn+bwephCnQCaB7qWK2ldHa+8ncN8hJU8VTMcxjPpyMkUjww==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.5.tgz", + "integrity": "sha512-PWBaRY5JoKuRnHlUHfpV/KohFylaDZTupcXN1H9vYryNLOnitSw60Mw9IAE2r67NbwwzBw/Cc/8q9BK3kIX8Kw==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.4", - "@vitest/utils": "4.1.4", + "@vitest/spy": "4.1.5", + "@vitest/utils": "4.1.5", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" }, @@ -8750,9 +8914,9 @@ } }, "node_modules/vitest/node_modules/@vitest/pretty-format": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.4.tgz", - "integrity": "sha512-ddmDHU0gjEUyEVLxtZa7xamrpIefdEETu3nZjWtHeZX4QxqJ7tRxSteHVXJOcr8jhiLoGAhkK4WJ3WqBpjx42A==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.5.tgz", + "integrity": "sha512-7I3q6l5qr03dVfMX2wCo9FxwSJbPdwKjy2uu/YPpU3wfHvIL4QHwVRp57OfGrDFeUJ8/8QdfBKIV12FTtLn00g==", "dev": true, "license": "MIT", "dependencies": { @@ -8763,9 +8927,9 @@ } }, "node_modules/vitest/node_modules/@vitest/spy": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.4.tgz", - "integrity": "sha512-XxNdAsKW7C+FLydqFJLb5KhJtl3PGCMmYwFRfhvIgxJvLSXhhVI1zM8f1qD3Zg7RCjTSzDVyct6sghs9UEgBEQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.5.tgz", + "integrity": "sha512-2lNOsh6+R2Idnf1TCZqSwYlKN2E/iDlD8sgU59kYVl+OMDmvldO1VDk39smRfpUNwYpNRVn3w4YfuC7KfbBnkQ==", "dev": true, "license": "MIT", "funding": { @@ -8773,13 +8937,13 @@ } }, "node_modules/vitest/node_modules/@vitest/utils": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.4.tgz", - "integrity": "sha512-13QMT+eysM5uVGa1rG4kegGYNp6cnQcsTc67ELFbhNLQO+vgsygtYJx2khvdt4gVQqSSpC/KT5FZZxUpP3Oatw==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.5.tgz", + "integrity": "sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.4", + "@vitest/pretty-format": "4.1.5", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, @@ -9005,7 +9169,7 @@ "version": "2.8.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", - "devOptional": true, + "dev": true, "license": "ISC", "bin": { "yaml": "bin.mjs" @@ -9060,9 +9224,9 @@ } }, "node_modules/yummies": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/yummies/-/yummies-7.18.0.tgz", - "integrity": "sha512-fgldINrxPi20XJjIa1LOniyqHROm5HwDmmq0VZtQml4u3cMerm8H7H2BM8kMhHilgd8l7rg3mN4xt2apc2l/xA==", + "version": "7.19.2", + "resolved": "https://registry.npmjs.org/yummies/-/yummies-7.19.2.tgz", + "integrity": "sha512-3a8C0lsYP3c1sMsnQiPffS/X6hj1T0oIWOV/P6515ytGY5scAtcgaIg4mv8pe3856vyoTjagllvixdMxnitNUg==", "dev": true, "license": "MIT", "dependencies": { @@ -9109,13 +9273,14 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz", "integrity": "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==", + "dev": true, "license": "MIT" }, "node_modules/zod": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", - "devOptional": true, + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -9125,6 +9290,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "dev": true, "license": "MIT", "funding": { "type": "github", diff --git a/src/Exceptionless.Web/ClientApp/package.json b/src/Exceptionless.Web/ClientApp/package.json index 74ea8a3618..ae02d6eceb 100644 --- a/src/Exceptionless.Web/ClientApp/package.json +++ b/src/Exceptionless.Web/ClientApp/package.json @@ -3,10 +3,11 @@ "version": "8.0.0", "private": true, "scripts": { - "dev": "cross-env NODE_OPTIONS=--trace-warnings vite dev", - "dev:api": "cross-env ASPNETCORE_URLS=https://be.exceptionless.io/ vite dev", + "dev": "vite", + "dev:api": "cross-env API_HTTPS=https://be.exceptionless.io/ vite", "build": "vite build", "preview": "vite preview", + "clean": "node -e \"fs.rmSync('.svelte-kit',{recursive:true,force:true});fs.rmSync('build',{recursive:true,force:true});fs.rmSync('node_modules/.vite',{recursive:true,force:true})\"", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", "lint": "npm run lint:prettier && npm run lint:eslint", @@ -28,75 +29,73 @@ "@chromatic-com/storybook": "^5.1.2", "@eslint/compat": "^2.0.5", "@eslint/js": "^10.0.1", - "@iconify-json/lucide": "^1.2.102", + "@exceptionless/browser": "^3.1.0", + "@exceptionless/fetchclient": "^0.44.0", + "@iconify-json/lucide": "^1.2.103", + "@internationalized/date": "^3.12.1", + "@lucide/svelte": "^1.11.0", "@playwright/test": "^1.59.1", "@storybook/addon-a11y": "^10.3.5", "@storybook/addon-docs": "^10.3.5", "@storybook/addon-svelte-csf": "^5.1.2", "@storybook/sveltekit": "^10.3.5", "@sveltejs/adapter-static": "^3.0.10", - "@sveltejs/kit": "^2.57.1", + "@sveltejs/kit": "^2.58.0", "@sveltejs/vite-plugin-svelte": "^6.2.4", - "@tailwindcss/vite": "^4.2.2", - "@tanstack/eslint-plugin-query": "^5.99.0", + "@tailwindcss/vite": "^4.2.4", + "@tanstack/eslint-plugin-query": "^5.100.4", + "@tanstack/svelte-form": "^1.29.1", + "@tanstack/svelte-query": "^6.1.23", + "@tanstack/svelte-query-devtools": "^6.1.23", + "@tanstack/svelte-table": "^9.0.0-alpha.10", "@testing-library/jest-dom": "^6.9.1", "@testing-library/svelte": "^5.3.1", + "@types/d3-scale": "^4.0.9", + "@types/d3-shape": "^3.1.8", "@types/eslint": "^9.6.1", "@types/node": "^25.6.0", "@types/throttle-debounce": "^5.0.2", + "bits-ui": "^2.18.0", + "clsx": "^2.1.1", "cross-env": "^10.1.0", - "eslint": "^10.2.0", + "d3-scale": "^4.0.2", + "dompurify": "^3.4.1", + "eslint": "^10.2.1", "eslint-config-prettier": "^10.1.8", - "eslint-plugin-perfectionist": "^5.8.0", + "eslint-plugin-perfectionist": "^5.9.0", "eslint-plugin-storybook": "^10.3.5", - "eslint-plugin-svelte": "^3.17.0", + "eslint-plugin-svelte": "^3.17.1", "globals": "^17.5.0", "jsdom": "^29.0.2", - "prettier": "^3.8.3", - "prettier-plugin-svelte": "^3.5.1", - "prettier-plugin-tailwindcss": "^0.7.2", - "storybook": "^10.3.5", - "svelte": "^5.55.4", - "svelte-check": "^4.4.6", - "swagger-typescript-api": "^13.6.10", - "tslib": "^2.8.1", - "typescript": "^6.0.2", - "typescript-eslint": "^8.58.2", - "vite": "^7.3.2", - "vitest": "4.1.4", - "vitest-websocket-mock": "^0.5.0", - "zod": "^4.3.6" - }, - "dependencies": { - "@exceptionless/browser": "^3.1.0", - "@exceptionless/fetchclient": "^0.44.0", - "@internationalized/date": "^3.12.1", - "@lucide/svelte": "^1.8.0", - "@tanstack/svelte-form": "^1.29.0", - "@tanstack/svelte-query": "^6.1.16", - "@tanstack/svelte-query-devtools": "^6.1.16", - "@tanstack/svelte-table": "^9.0.0-alpha.10", - "@types/d3-scale": "^4.0.9", - "@types/d3-shape": "^3.1.8", - "bits-ui": "^2.17.3", - "clsx": "^2.1.1", - "d3-scale": "^4.0.2", - "dompurify": "^3.4.0", "kit-query-params": "^0.0.26", "layerchart": "^2.0.0-next.46", "mode-watcher": "^1.1.0", "oidc-client-ts": "^3.5.0", + "prettier": "^3.8.3", + "prettier-plugin-svelte": "^3.5.1", + "prettier-plugin-tailwindcss": "^0.7.2", "pretty-ms": "^9.3.0", "runed": "^0.37.1", "shiki": "^4.0.2", + "storybook": "^10.3.5", + "svelte": "^5.55.5", + "svelte-check": "^4.4.6", "svelte-intercom": "^0.0.35", - "svelte-sonner": "^1.1.0", + "svelte-sonner": "^1.1.1", "svelte-time": "^2.1.0", + "swagger-typescript-api": "^13.7.0", "tailwind-merge": "^3.5.0", "tailwind-variants": "^3.2.2", - "tailwindcss": "^4.2.2", + "tailwindcss": "^4.2.4", "throttle-debounce": "^5.0.2", - "tw-animate-css": "^1.4.0" + "tslib": "^2.8.1", + "tw-animate-css": "^1.4.0", + "typescript": "^6.0.3", + "typescript-eslint": "^8.59.0", + "vite": "^7.3.2", + "vitest": "4.1.5", + "vitest-websocket-mock": "^0.5.0", + "zod": "^4.3.6" }, "type": "module", "overrides": { diff --git a/src/Exceptionless.Web/ClientApp/scripts/generate-api.mjs b/src/Exceptionless.Web/ClientApp/scripts/generate-api.mjs index 5f02414cd8..edcb315653 100644 --- a/src/Exceptionless.Web/ClientApp/scripts/generate-api.mjs +++ b/src/Exceptionless.Web/ClientApp/scripts/generate-api.mjs @@ -10,7 +10,7 @@ const rootDir = path.resolve(__dirname, '..'); const outputDir = path.resolve(rootDir, 'src/lib/generated'); // SWAGGER_URL can be an HTTP URL or a file path (for regenerating from baseline during development) -const swaggerSource = process.env.SWAGGER_URL || 'http://localhost:5200/docs/v2/openapi.json'; +const swaggerSource = process.env.SWAGGER_URL || 'http://localhost:7110/docs/v2/openapi.json'; const isLocalFile = swaggerSource.startsWith('/') || swaggerSource.startsWith('.'); if (isLocalFile && !existsSync(swaggerSource)) { diff --git a/src/Exceptionless.Web/ClientApp/src/routes/(app)/(components)/layouts/footer.svelte b/src/Exceptionless.Web/ClientApp/src/routes/(app)/(components)/layouts/footer.svelte index fd145a74ad..c35211b2da 100644 --- a/src/Exceptionless.Web/ClientApp/src/routes/(app)/(components)/layouts/footer.svelte +++ b/src/Exceptionless.Web/ClientApp/src/routes/(app)/(components)/layouts/footer.svelte @@ -1,5 +1,4 @@ -
- -
- - - - - - - - - - - - + diff --git a/src/Exceptionless.Web/ClientApp/src/routes/(app)/+layout.svelte b/src/Exceptionless.Web/ClientApp/src/routes/(app)/+layout.svelte index 66c40f2fdb..13e38b1d28 100644 --- a/src/Exceptionless.Web/ClientApp/src/routes/(app)/+layout.svelte +++ b/src/Exceptionless.Web/ClientApp/src/routes/(app)/+layout.svelte @@ -272,9 +272,9 @@ /> {/snippet} -
-
-
+
+
+
{#if showOrganizationNotifications.current} diff --git a/src/Exceptionless.Web/ClientApp/src/routes/(auth)/login/+page.svelte b/src/Exceptionless.Web/ClientApp/src/routes/(auth)/login/+page.svelte index 5a191060bb..976764165b 100644 --- a/src/Exceptionless.Web/ClientApp/src/routes/(auth)/login/+page.svelte +++ b/src/Exceptionless.Web/ClientApp/src/routes/(auth)/login/+page.svelte @@ -74,6 +74,9 @@ {/snippet} + {#if dev} + Default credentials: test@localhost / tester + {/if} {#snippet children(field)} diff --git a/src/Exceptionless.Web/ClientApp/vite.config.ts b/src/Exceptionless.Web/ClientApp/vite.config.ts index 521b51c653..0f06b8e728 100644 --- a/src/Exceptionless.Web/ClientApp/vite.config.ts +++ b/src/Exceptionless.Web/ClientApp/vite.config.ts @@ -3,7 +3,19 @@ import tailwindcss from '@tailwindcss/vite'; import { svelteTesting } from '@testing-library/svelte/vite'; import { defineConfig } from 'vitest/config'; -const aspNetConfig = getAspNetConfig(); +const apiTarget = process.env.API_HTTPS || process.env.API_HTTP; +const apiProxy = { changeOrigin: true, target: apiTarget }; + +const oldAppTarget = process.env.OLDAPP_HTTPS || process.env.OLDAPP_HTTP; +const oldAppProxy = { changeOrigin: true, secure: false, target: oldAppTarget }; + +const codespaceName = process.env.CODESPACE_NAME; +const codespaceDomain = process.env.GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN; +const hmr = codespaceName && codespaceDomain ? { clientPort: 443, host: `${codespaceName}-7131.${codespaceDomain}`, protocol: 'wss' as const } : undefined; +const allowedHosts = ['web-ex.dev.localhost', 'localhost', '127.0.0.1']; +if (codespaceName && codespaceDomain) { + allowedHosts.push(`${codespaceName}-7131.${codespaceDomain}`); +} export default defineConfig({ base: '/next/', @@ -11,56 +23,27 @@ export default defineConfig({ sourcemap: true, target: 'esnext' }, + clearScreen: false, + logLevel: 'info', + optimizeDeps: { + entries: ['src/**/*.{svelte,ts,js}'] + }, plugins: [tailwindcss(), sveltekit()], server: { - hmr: aspNetConfig.hmr, - host: true, - port: parseInt(process.env.PORT ?? '5173'), + allowedHosts, + hmr, + port: 7131, proxy: { - '/_framework': { - changeOrigin: true, - secure: false, - target: aspNetConfig.url - }, - '/_vs': { - changeOrigin: true, - secure: false, - target: aspNetConfig.url - }, - // proxy API requests to the ASP.NET backend - '/api': { - changeOrigin: true, - secure: false, - target: aspNetConfig.url - }, - '/api/v2/push': { - changeOrigin: true, - secure: false, - target: aspNetConfig.wsUrl, - ws: true - }, - '/docs': { - changeOrigin: true, - secure: false, - target: aspNetConfig.url - }, - '/health': { - changeOrigin: true, - secure: false, - target: aspNetConfig.url - }, - '/ready': { - changeOrigin: true, - secure: false, - target: aspNetConfig.url - }, - '^/(?!(next|api|docs|health|ready|_)).*': { - changeOrigin: true, - secure: false, - target: 'http://localhost:5100' - } + '/api': { ...apiProxy, ws: true }, + '/docs': apiProxy, + '/health': apiProxy, + '/ready': apiProxy, + '^/(?!(next|api|docs|health|ready|_)).*': oldAppProxy }, - strictPort: true + strictPort: true, + warmup: { + clientFiles: ['src/routes/**/*.svelte'] + } }, test: { projects: [ @@ -91,43 +74,3 @@ export default defineConfig({ ] } }); - -// adapted from src/setupProxy.js in ASP.NET React template -function getAspNetConfig() { - // check to see if we are running inside of codespaces - const codespaceName = process.env.CODESPACE_NAME; - const codespaceDomain = process.env.GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN; - - // get current aspnetcore port / url - const aspnetHttpsPort = process.env.ASPNETCORE_HTTPS_PORT; - const aspnetUrls = process.env.ASPNETCORE_URLS ?? process.env.services__Api__0; - const serverPort = 5173; - - const hmrRemoteHost = codespaceName ? `${codespaceName}-${serverPort}.${codespaceDomain}` : 'localhost'; - const hmrRemotePort = codespaceName ? 443 : serverPort; - - let url = 'http://localhost:5200'; - if (aspnetHttpsPort) { - url = `https://localhost:${aspnetHttpsPort}`; - } else if (aspnetUrls) { - url = aspnetUrls.split(';')[0] as string; - } - - const wsUrl = url.replace('https://', 'wss://').replace('http://', 'ws://'); - - let hmrRemoteProtocol = 'ws'; - if (codespaceName || (wsUrl.startsWith('wss') && hmrRemoteHost !== 'localhost')) { - hmrRemoteProtocol = 'wss'; - } - - return { - hmr: { - clientPort: hmrRemotePort, - host: hmrRemoteHost, - port: hmrRemotePort, - protocol: hmrRemoteProtocol - }, - url, - wsUrl - }; -} diff --git a/src/Exceptionless.Web/Controllers/AdminController.cs b/src/Exceptionless.Web/Controllers/AdminController.cs index 0549a57def..3c374602fe 100644 --- a/src/Exceptionless.Web/Controllers/AdminController.cs +++ b/src/Exceptionless.Web/Controllers/AdminController.cs @@ -43,6 +43,7 @@ public class AdminController : ExceptionlessApiController private readonly BillingManager _billingManager; private readonly BillingPlans _plans; private readonly IMigrationStateRepository _migrationStateRepository; + private readonly SampleDataService _sampleDataService; public AdminController( ExceptionlessElasticConfiguration configuration, @@ -59,6 +60,7 @@ public AdminController( BillingManager billingManager, BillingPlans plans, IMigrationStateRepository migrationStateRepository, + SampleDataService sampleDataService, TimeProvider timeProvider, ILoggerFactory loggerFactory) : base(timeProvider) { @@ -77,6 +79,7 @@ public AdminController( _billingManager = billingManager; _plans = plans; _migrationStateRepository = migrationStateRepository; + _sampleDataService = sampleDataService; } [HttpGet("settings")] @@ -434,5 +437,24 @@ public async Task> GetElasticsearch return Problem(title: "Unable to retrieve snapshot information."); } } + + [HttpPost("generate-sample-events")] + public async Task GenerateSampleEventsAsync(int eventCount = 100, int daysBack = 7) + { + if (eventCount < 1 || eventCount > 10000) + { + ModelState.AddModelError(nameof(eventCount), "Event count must be between 1 and 10,000."); + return ValidationProblem(ModelState); + } + + if (daysBack < 1 || daysBack > 365) + { + ModelState.AddModelError(nameof(daysBack), "Days back must be between 1 and 365."); + return ValidationProblem(ModelState); + } + + await _sampleDataService.EnqueueSampleEventsAsync(eventCount, daysBack); + return Ok(new { Success = true, Message = $"Enqueued generation of {eventCount} sample events over {daysBack} days. Events will appear shortly." }); + } } diff --git a/src/Exceptionless.Web/Properties/launchSettings.json b/src/Exceptionless.Web/Properties/launchSettings.json index a62987a549..0644afc437 100644 --- a/src/Exceptionless.Web/Properties/launchSettings.json +++ b/src/Exceptionless.Web/Properties/launchSettings.json @@ -1,13 +1,22 @@ { "profiles": { - "Exceptionless": { + "http": { "commandName": "Project", "launchBrowser": false, "environmentVariables": { "EX_AppMode": "Development" }, "dotnetRunMessages": true, - "applicationUrl": "http://localhost:5200" + "applicationUrl": "http://localhost:7110" + }, + "https": { + "commandName": "Project", + "launchBrowser": false, + "environmentVariables": { + "EX_AppMode": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "https://localhost:7111;http://localhost:7110" } } } diff --git a/tests/Exceptionless.Tests/AppWebHostFactory.cs b/tests/Exceptionless.Tests/AppWebHostFactory.cs index 5508341b6c..19aa9d17cf 100644 --- a/tests/Exceptionless.Tests/AppWebHostFactory.cs +++ b/tests/Exceptionless.Tests/AppWebHostFactory.cs @@ -1,3 +1,5 @@ +using System.Collections.Concurrent; +using System.Net; using Aspire.Hosting; using Aspire.Hosting.ApplicationModel; using Exceptionless.Insulation.Configuration; @@ -10,11 +12,30 @@ namespace Exceptionless.Tests; public class AppWebHostFactory : WebApplicationFactory, IAsyncLifetime { - private DistributedApplication? _app; + private static int s_counter = -1; + private static readonly ConcurrentQueue s_pool = new(); + private static readonly Lazy> s_sharedApplication = new(StartSharedApplicationAsync, LazyThreadSafetyMode.ExecutionAndPublication); + private bool _sliceReleased; - public DistributedApplication App => _app ?? throw new InvalidOperationException("The application is not initialized"); + public AppWebHostFactory() + { + if (!s_pool.TryDequeue(out var instanceId)) + instanceId = Interlocked.Increment(ref s_counter); + + InstanceId = instanceId; + AppScope = instanceId == 0 ? "test" : $"test-{instanceId}"; + } + + public string AppScope { get; } + public int InstanceId { get; } + public bool IndexesHaveBeenConfigured { get; set; } public async ValueTask InitializeAsync() + { + _ = await s_sharedApplication.Value; + } + + private static async Task StartSharedApplicationAsync() { var options = new DistributedApplicationOptions { AssemblyName = typeof(ElasticsearchResource).Assembly.FullName, DisableDashboard = true }; var builder = DistributedApplication.CreateBuilder(options); @@ -22,13 +43,45 @@ public async ValueTask InitializeAsync() // don't use random ports for tests builder.Configuration["DcpPublisher:RandomizePorts"] = "false"; - builder.AddElasticsearch("Elasticsearch", port: 9200) + var elasticsearch = builder.AddElasticsearch("Elasticsearch", port: 9200) .WithContainerName("Exceptionless-Elasticsearch-Test") .WithLifetime(ContainerLifetime.Persistent); - _app = builder.Build(); + var app = builder.Build(); + + await app.StartAsync(); + + var connectionString = await elasticsearch.Resource.GetConnectionStringAsync() + ?? throw new InvalidOperationException("Could not resolve Elasticsearch connection string."); + await WaitForElasticsearchAsync(new Uri(connectionString)); - await _app.StartAsync(); + return app; + } + + private static async Task WaitForElasticsearchAsync(Uri elasticsearchUri) + { + using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(1) }; + var deadline = TimeProvider.System.GetUtcNow() + TimeSpan.FromSeconds(60); + + while (TimeProvider.System.GetUtcNow() < deadline) + { + try + { + using var response = await client.GetAsync(elasticsearchUri); + if (response.StatusCode == HttpStatusCode.OK) + return; + } + catch (HttpRequestException) + { + } + catch (TaskCanceledException) + { + } + + await Task.Delay(TimeSpan.FromMilliseconds(250)); + } + + throw new TimeoutException("Timed out waiting for Elasticsearch test container to be ready."); } protected override void ConfigureWebHost(IWebHostBuilder builder) @@ -41,17 +94,23 @@ protected override IHostBuilder CreateHostBuilder() var config = new ConfigurationBuilder() .SetBasePath(AppContext.BaseDirectory) .AddYamlFile("appsettings.yml", optional: false, reloadOnChange: false) + .AddInMemoryCollection(new Dictionary + { + ["AppScope"] = AppScope + }) .Build(); return Web.Program.CreateHostBuilder(config, Environments.Development); } - public override async ValueTask DisposeAsync() + public override ValueTask DisposeAsync() { - if (_app is not null) + if (!_sliceReleased) { - await _app.DisposeAsync(); + s_pool.Enqueue(InstanceId); + _sliceReleased = true; } - await base.DisposeAsync(); + + return base.DisposeAsync(); } } diff --git a/tests/Exceptionless.Tests/Controllers/EventControllerTests.cs b/tests/Exceptionless.Tests/Controllers/EventControllerTests.cs index 1ef4cccfaf..0dc05cce55 100644 --- a/tests/Exceptionless.Tests/Controllers/EventControllerTests.cs +++ b/tests/Exceptionless.Tests/Controllers/EventControllerTests.cs @@ -37,7 +37,7 @@ public class EventControllerTests : IntegrationTestsBase private readonly JsonSerializerOptions _jsonSerializerOptions; private readonly IOrganizationRepository _organizationRepository; private readonly StackData _stackData; - private readonly RandomEventGenerator _randomEventGenerator; + private readonly Exceptionless.Helpers.RandomEventGenerator _randomEventGenerator; private readonly EventData _eventData; private readonly IEventRepository _eventRepository; private readonly IQueue _eventQueue; @@ -49,7 +49,7 @@ public EventControllerTests(ITestOutputHelper output, AppWebHostFactory factory) _jsonSerializerOptions = GetService(); _organizationRepository = GetService(); _stackData = GetService(); - _randomEventGenerator = GetService(); + _randomEventGenerator = GetService(); _eventData = GetService(); _eventRepository = GetService(); _eventQueue = GetService>(); diff --git a/tests/Exceptionless.Tests/Controllers/OpenApiControllerTests.cs b/tests/Exceptionless.Tests/Controllers/OpenApiControllerTests.cs index 7d2c199a3f..23e1a98cfc 100644 --- a/tests/Exceptionless.Tests/Controllers/OpenApiControllerTests.cs +++ b/tests/Exceptionless.Tests/Controllers/OpenApiControllerTests.cs @@ -25,7 +25,9 @@ public async Task GetOpenApiJson_Default_ReturnsExpectedBaseline() string actualJson = await response.Content.ReadAsStringAsync(TestCancellationToken); // Assert - string expectedJson = await File.ReadAllTextAsync(baselinePath, TestCancellationToken); + string expectedJson = (await File.ReadAllTextAsync(baselinePath, TestCancellationToken)).Replace("\\r\\n", "\\n"); + actualJson = actualJson.Replace("\\r\\n", "\\n"); + Assert.Equal(expectedJson, actualJson); } } diff --git a/tests/Exceptionless.Tests/Exceptionless.Tests.csproj b/tests/Exceptionless.Tests/Exceptionless.Tests.csproj index 0ecf004c75..76d9d10308 100644 --- a/tests/Exceptionless.Tests/Exceptionless.Tests.csproj +++ b/tests/Exceptionless.Tests/Exceptionless.Tests.csproj @@ -11,7 +11,7 @@ - + diff --git a/tests/Exceptionless.Tests/Extensions/TestServerExtensions.cs b/tests/Exceptionless.Tests/Extensions/TestServerExtensions.cs index 485220c913..7918a676b6 100644 --- a/tests/Exceptionless.Tests/Extensions/TestServerExtensions.cs +++ b/tests/Exceptionless.Tests/Extensions/TestServerExtensions.cs @@ -6,17 +6,13 @@ namespace Exceptionless.Tests; public static class TestServerExtensions { - private static bool _alreadyWaited; - public static async Task WaitForReadyAsync(this TestServer server) { var startupContext = server.Services.GetService(); - var maxWaitTime = !_alreadyWaited ? TimeSpan.FromSeconds(30) : TimeSpan.FromSeconds(2); + var maxWaitTime = TimeSpan.FromSeconds(30); if (Debugger.IsAttached) maxWaitTime = maxWaitTime.Add(TimeSpan.FromMinutes(1)); - _alreadyWaited = true; - var client = server.CreateClient(); var startTime = DateTime.UtcNow; do diff --git a/tests/Exceptionless.Tests/IntegrationTestsBase.cs b/tests/Exceptionless.Tests/IntegrationTestsBase.cs index 45ae11da82..bcd8b4faa0 100644 --- a/tests/Exceptionless.Tests/IntegrationTestsBase.cs +++ b/tests/Exceptionless.Tests/IntegrationTestsBase.cs @@ -3,6 +3,7 @@ using Exceptionless.Core.Extensions; using Exceptionless.Core.Mail; using Exceptionless.Core.Models; +using Exceptionless.Core.Queues.Models; using Exceptionless.Core.Repositories; using Exceptionless.Core.Repositories.Configuration; using Exceptionless.Helpers; @@ -31,15 +32,15 @@ namespace Exceptionless.Tests; public abstract class IntegrationTestsBase : TestWithLoggingBase, Xunit.IAsyncLifetime, IClassFixture { - private static bool _indexesHaveBeenConfigured = false; - private static readonly SemaphoreSlim _semaphoreSlim = new(1, 1); private readonly ExceptionlessElasticConfiguration _configuration; + private readonly AppWebHostFactory _factory; protected readonly TestServer _server; private readonly ProxyTimeProvider _timeProvider; protected readonly IList _disposables = new List(); public IntegrationTestsBase(ITestOutputHelper output, AppWebHostFactory factory) : base(output) { + _factory = factory; Log.DefaultLogLevel = LogLevel.Information; Log.SetLogLevel(LogLevel.Warning); Log.SetLogLevel(LogLevel.Warning); @@ -109,7 +110,7 @@ protected virtual void RegisterServices(IServiceCollection services) services.AddTransient(); services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); @@ -145,18 +146,17 @@ protected virtual void RegisterServices(IServiceCollection services) protected virtual async Task ResetDataAsync() { - await _semaphoreSlim.WaitAsync(); + var oldLoggingLevel = Log.DefaultLogLevel; + Log.DefaultLogLevel = LogLevel.Warning; + try { - var oldLoggingLevel = Log.DefaultLogLevel; - Log.DefaultLogLevel = LogLevel.Warning; - await RefreshDataAsync(); - if (!_indexesHaveBeenConfigured) + if (!_factory.IndexesHaveBeenConfigured) { await _configuration.DeleteIndexesAsync(); await _configuration.ConfigureIndexesAsync(); - _indexesHaveBeenConfigured = true; + _factory.IndexesHaveBeenConfigured = true; } else { @@ -169,7 +169,7 @@ await _configuration.Client.DeleteByQueryAsync(new DeleteByQueryRequest(indexes) }); } - _logger.LogTrace("Configured Indexes"); + _logger.LogTrace("Configured indexes for {AppScope}", _factory.AppScope); foreach (var index in _configuration.Indexes) index.QueryParser.Configuration?.MappingResolver?.RefreshMapping(); @@ -180,14 +180,17 @@ await _configuration.Client.DeleteByQueryAsync(new DeleteByQueryRequest(indexes) var fileStorage = GetService(); await fileStorage.DeleteFilesAsync(await fileStorage.GetFileListAsync()); + await GetService>().DeleteQueueAsync(); + await GetService>().DeleteQueueAsync(); + await GetService>().DeleteQueueAsync(); + await GetService>().DeleteQueueAsync(); + await GetService>().DeleteQueueAsync(); await GetService>().DeleteQueueAsync(); - - Log.DefaultLogLevel = oldLoggingLevel; } finally { - _semaphoreSlim.Release(); - _logger.LogDebug("Reset Data"); + Log.DefaultLogLevel = oldLoggingLevel; + _logger.LogDebug("Reset data for {AppScope}", _factory.AppScope); } } diff --git a/tests/Exceptionless.Tests/Plugins/WebHookData/v1.event.expected.json b/tests/Exceptionless.Tests/Plugins/WebHookData/v1.event.expected.json index a10ccba924..0883a60a0f 100644 --- a/tests/Exceptionless.Tests/Plugins/WebHookData/v1.event.expected.json +++ b/tests/Exceptionless.Tests/Plugins/WebHookData/v1.event.expected.json @@ -1,6 +1,6 @@ -{ +{ "Id": "22cd0826e447a44e78877a22", - "Url": "http://localhost:5200/event/22cd0826e447a44e78877a22", + "Url": "http://localhost:7110/event/22cd0826e447a44e78877a22", "OccurrenceDate": "2014-01-17T14:37:02.739-06:00", "Tags": [], "MachineName": "TestMachine", @@ -16,7 +16,7 @@ "OrganizationName": "Acme", "ErrorStackId": "1ecd0826e447a44e78877ab1", "ErrorStackStatus": "open", - "ErrorStackUrl": "http://localhost:5200/stack/1ecd0826e447a44e78877ab1", + "ErrorStackUrl": "http://localhost:7110/stack/1ecd0826e447a44e78877ab1", "ErrorStackTitle": "A potentially dangerous Request.Path value was detected from the client (&).", "ErrorStackDescription": null, "ErrorStackTags": [ diff --git a/tests/Exceptionless.Tests/Plugins/WebHookData/v1.stack.expected.json b/tests/Exceptionless.Tests/Plugins/WebHookData/v1.stack.expected.json index 6dda425259..4cb51a53a2 100644 --- a/tests/Exceptionless.Tests/Plugins/WebHookData/v1.stack.expected.json +++ b/tests/Exceptionless.Tests/Plugins/WebHookData/v1.stack.expected.json @@ -1,7 +1,7 @@ -{ +{ "Id": "1ecd0826e447a44e78877ab1", "Status": "open", - "Url": "http://localhost:5200/stack/1ecd0826e447a44e78877ab1", + "Url": "http://localhost:7110/stack/1ecd0826e447a44e78877ab1", "Title": "A potentially dangerous Request.Path value was detected from the client (&).", "Description": null, "Tags": [ diff --git a/tests/Exceptionless.Tests/Plugins/WebHookData/v2.event.expected.json b/tests/Exceptionless.Tests/Plugins/WebHookData/v2.event.expected.json index 1c7136f797..445dd8f778 100644 --- a/tests/Exceptionless.Tests/Plugins/WebHookData/v2.event.expected.json +++ b/tests/Exceptionless.Tests/Plugins/WebHookData/v2.event.expected.json @@ -1,6 +1,6 @@ -{ +{ "id": "22cd0826e447a44e78877a22", - "url": "http://localhost:5200/event/22cd0826e447a44e78877a22", + "url": "http://localhost:7110/event/22cd0826e447a44e78877a22", "occurrence_date": "2014-01-17T14:37:02.739-06:00", "type": "error", "message": "A potentially dangerous Request.Path value was detected from the client (&).", @@ -9,7 +9,7 @@ "organization_id": "537650f3b77efe23a47914f3", "organization_name": "Acme", "stack_id": "1ecd0826e447a44e78877ab1", - "stack_url": "http://localhost:5200/stack/1ecd0826e447a44e78877ab1", + "stack_url": "http://localhost:7110/stack/1ecd0826e447a44e78877ab1", "stack_title": "A potentially dangerous Request.Path value was detected from the client (&).", "stack_tags": [ "Test" diff --git a/tests/Exceptionless.Tests/Plugins/WebHookData/v2.stack.expected.json b/tests/Exceptionless.Tests/Plugins/WebHookData/v2.stack.expected.json index a8a19d3614..9c251b81df 100644 --- a/tests/Exceptionless.Tests/Plugins/WebHookData/v2.stack.expected.json +++ b/tests/Exceptionless.Tests/Plugins/WebHookData/v2.stack.expected.json @@ -1,6 +1,6 @@ -{ +{ "id": "1ecd0826e447a44e78877ab1", - "url": "http://localhost:5200/stack/1ecd0826e447a44e78877ab1", + "url": "http://localhost:7110/stack/1ecd0826e447a44e78877ab1", "title": "A potentially dangerous Request.Path value was detected from the client (&).", "tags": [ "Test" diff --git a/tests/Exceptionless.Tests/Properties/PropertyInfo.cs b/tests/Exceptionless.Tests/Properties/PropertyInfo.cs index a4bcec543f..dc5b351f3f 100644 --- a/tests/Exceptionless.Tests/Properties/PropertyInfo.cs +++ b/tests/Exceptionless.Tests/Properties/PropertyInfo.cs @@ -1,3 +1,3 @@ using Xunit; -[assembly: CollectionBehavior(DisableTestParallelization = true)] +[assembly: CollectionBehavior(MaxParallelThreads = 6)] diff --git a/tests/Exceptionless.Tests/Repositories/EventRepositoryTests.cs b/tests/Exceptionless.Tests/Repositories/EventRepositoryTests.cs index d13cdc6bc5..cd790c66b3 100644 --- a/tests/Exceptionless.Tests/Repositories/EventRepositoryTests.cs +++ b/tests/Exceptionless.Tests/Repositories/EventRepositoryTests.cs @@ -16,7 +16,7 @@ namespace Exceptionless.Tests.Repositories; public sealed class EventRepositoryTests : IntegrationTestsBase { private readonly List> _ids = new(); - private readonly RandomEventGenerator _randomEventGenerator; + private readonly Exceptionless.Helpers.RandomEventGenerator _randomEventGenerator; private readonly EventData _eventData; private readonly IEventRepository _repository; private readonly StackData _stackData; @@ -25,7 +25,7 @@ public sealed class EventRepositoryTests : IntegrationTestsBase public EventRepositoryTests(ITestOutputHelper output, AppWebHostFactory factory) : base(output, factory) { - _randomEventGenerator = GetService(); + _randomEventGenerator = GetService(); _eventData = GetService(); _repository = GetService(); _stackData = GetService(); diff --git a/tests/Exceptionless.Tests/Services/SlackServiceTests.cs b/tests/Exceptionless.Tests/Services/SlackServiceTests.cs index 5e340234fb..6ac9440248 100644 --- a/tests/Exceptionless.Tests/Services/SlackServiceTests.cs +++ b/tests/Exceptionless.Tests/Services/SlackServiceTests.cs @@ -8,14 +8,14 @@ namespace Exceptionless.Tests.Services; -public sealed class SlackServiceTests : TestWithServices +public sealed class SlackServiceTests : IntegrationTestsBase { private readonly Project _project; private readonly SlackService _slackService; private readonly ProjectData _projectData; private readonly EventData _eventData; - public SlackServiceTests(ITestOutputHelper output) : base(output) + public SlackServiceTests(ITestOutputHelper output, AppWebHostFactory factory) : base(output, factory) { _projectData = GetService(); _eventData = GetService(); diff --git a/tests/Exceptionless.Tests/Utility/StackData.cs b/tests/Exceptionless.Tests/Utility/StackData.cs index c07fdf9f47..7411880481 100644 --- a/tests/Exceptionless.Tests/Utility/StackData.cs +++ b/tests/Exceptionless.Tests/Utility/StackData.cs @@ -88,7 +88,7 @@ public async Task CreateSearchDataAsync(bool updateDates = false) if (file.EndsWith("summary.json")) continue; - await using var stream = new FileStream(file, FileMode.Open); + await using var stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); var stack = _serializer.Deserialize(stream); Assert.NotNull(stack); diff --git a/tests/Exceptionless.Tests/appsettings.yml b/tests/Exceptionless.Tests/appsettings.yml index 401ea93e0e..5bdb00cb6a 100644 --- a/tests/Exceptionless.Tests/appsettings.yml +++ b/tests/Exceptionless.Tests/appsettings.yml @@ -1,4 +1,4 @@ ---- +--- ConnectionStrings: # Redis: localhost,abortConnect=false # Elasticsearch: server=https://elastic:elastic@localhost:9200 @@ -8,7 +8,7 @@ ConnectionStrings: Storage: provider=folder;path=..\..\..\..\..\src\Exceptionless.Web\storage # Base url for the ui used to build links in emails and other places. -BaseURL: "http://localhost:5200" +BaseURL: "http://localhost:7110" AppMode: Development AppScope: test diff --git a/tests/http/admin.http b/tests/http/admin.http index bfc3249eca..9bd618c1c9 100644 --- a/tests/http/admin.http +++ b/tests/http/admin.http @@ -1,4 +1,4 @@ -@url = http://localhost:5200 +@url = http://localhost:7110 @apiUrl = {url}/api/v2 @email = test@localhost @password = tester diff --git a/tests/http/auth.http b/tests/http/auth.http index 372d0117ea..3659e16b7a 100644 --- a/tests/http/auth.http +++ b/tests/http/auth.http @@ -1,4 +1,4 @@ -@apiUrl = http://localhost:5200/api/v2 +@apiUrl = http://localhost:7110/api/v2 @email = test@localhost @password = tester diff --git a/tests/http/events.http b/tests/http/events.http index 80bfa8dee5..f5ad89141a 100644 --- a/tests/http/events.http +++ b/tests/http/events.http @@ -1,4 +1,4 @@ -@apiUrl = http://localhost:5200/api/v2 +@apiUrl = http://localhost:7110/api/v2 @email = test@localhost @password = tester @clientToken = LhhP1C9gijpSKCslHHCvwdSIz298twx271nTest diff --git a/tests/http/organizations.http b/tests/http/organizations.http index ff060f944f..88b53a4a67 100644 --- a/tests/http/organizations.http +++ b/tests/http/organizations.http @@ -1,4 +1,4 @@ -@apiUrl = http://localhost:5200/api/v2 +@apiUrl = http://localhost:7110/api/v2 @email = test@localhost @password = tester @organizationId = 537650f3b77efe23a47914f3 diff --git a/tests/http/projects.http b/tests/http/projects.http index e66dd11905..9cef0249d3 100644 --- a/tests/http/projects.http +++ b/tests/http/projects.http @@ -1,4 +1,4 @@ -@apiUrl = http://localhost:5200/api/v2 +@apiUrl = http://localhost:7110/api/v2 @email = test@localhost @password = tester @organizationId = 537650f3b77efe23a47914f3 diff --git a/tests/http/stacks.http b/tests/http/stacks.http index 3fe6c9c475..5140be812a 100644 --- a/tests/http/stacks.http +++ b/tests/http/stacks.http @@ -1,4 +1,4 @@ -@apiUrl = http://localhost:5200/api/v2 +@apiUrl = http://localhost:7110/api/v2 @email = test@localhost @password = tester @organizationId = 537650f3b77efe23a47914f3 diff --git a/tests/http/status.http b/tests/http/status.http index c8d476b749..a5ce50bc02 100644 --- a/tests/http/status.http +++ b/tests/http/status.http @@ -1,4 +1,4 @@ -@url = http://localhost:5200 +@url = http://localhost:7110 @apiUrl = {{url}}/api/v2 @email = test@localhost @password = tester diff --git a/tests/http/tokens.http b/tests/http/tokens.http index 47fed5942b..18ce95c8d8 100644 --- a/tests/http/tokens.http +++ b/tests/http/tokens.http @@ -1,4 +1,4 @@ -@apiUrl = http://localhost:5200/api/v2 +@apiUrl = http://localhost:7110/api/v2 @email = test@localhost @password = tester @organizationId = 537650f3b77efe23a47914f3 diff --git a/tests/http/users.http b/tests/http/users.http index a380e058db..c6ee0f6f5d 100644 --- a/tests/http/users.http +++ b/tests/http/users.http @@ -1,4 +1,4 @@ -@apiUrl = http://localhost:5200/api/v2 +@apiUrl = http://localhost:7110/api/v2 @email = test@localhost @password = tester diff --git a/tests/http/webhooks.http b/tests/http/webhooks.http index ad4426ac4a..212217c87d 100644 --- a/tests/http/webhooks.http +++ b/tests/http/webhooks.http @@ -1,4 +1,4 @@ -@apiUrl = http://localhost:5200/api/v2 +@apiUrl = http://localhost:7110/api/v2 @email = test@localhost @password = tester @projectId = 537650f3b77efe23a47914f4