diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..15b19377 --- /dev/null +++ b/.env.example @@ -0,0 +1,4 @@ +# Create a file called .env and add the web ui api key +OPEN_WEB_UI_API_KEY= + + diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..6a4c7469 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +client/src/api/main/generated/** linguist-generated=true +client/src/api/genai/generated/** linguist-generated=true +client/src/api/realtime/generated/** linguist-generated=true \ No newline at end of file diff --git a/.github/workflows/build_docker_image.yml b/.github/workflows/build_docker_image.yml deleted file mode 100644 index dfb41c4d..00000000 --- a/.github/workflows/build_docker_image.yml +++ /dev/null @@ -1,72 +0,0 @@ -name: Build Docker Images - -on: - push: - -jobs: - test: - name: Run Java Tests - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - - uses: actions/setup-java@v4 - with: - java-version: '21' - distribution: 'temurin' - - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 - - - name: Build with Gradle - run: cd server && gradle build - - build: - name: Build Docker Images - needs: test - runs-on: ubuntu-latest - strategy: - matrix: - service: [client, server] - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Log in to the Container registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - with: - platforms: all - - - name: Install Docker Buildx - id: buildx - uses: docker/setup-buildx-action@v3 - - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@v5 - with: - images: ghcr.io/${{ github.repository }}/${{ matrix.service }} - tags: | - type=raw,value=latest,enable={{is_default_branch}} - type=ref,event=branch - type=ref,event=pr - - - name: Build and push Docker Image - uses: docker/build-push-action@v5 - with: - platforms: linux/amd64,linux/arm64 - context: ./${{ matrix.service }} - file: ./${{ matrix.service }}/Dockerfile - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file diff --git a/.github/workflows/client-linters.yml b/.github/workflows/client-linters.yml index 09351e23..203a7934 100644 --- a/.github/workflows/client-linters.yml +++ b/.github/workflows/client-linters.yml @@ -31,9 +31,6 @@ jobs: run: npx prettier --check "src/**/*.{ts,tsx}" working-directory: client - - name: Fix Prettier formatting - run: npm run format + - name: Run Type Check + run: npx --package=typescript@latest -- tsc --build . working-directory: client - if: failure() - - diff --git a/.github/workflows/deploy_docker.yml b/.github/workflows/deploy-to-ec2.yml similarity index 93% rename from .github/workflows/deploy_docker.yml rename to .github/workflows/deploy-to-ec2.yml index 45d0c877..5d39140a 100644 --- a/.github/workflows/deploy_docker.yml +++ b/.github/workflows/deploy-to-ec2.yml @@ -1,15 +1,15 @@ -name: Deploy Docker Images +name: Deploy to AWS EC2 on: - push: + workflow_dispatch: jobs: deploy: runs-on: ubuntu-latest environment: name: AWS - url: 'https://client.${{ vars.EC2_PUBLIC_IP }}.nip.io' - steps: + url: 'https://client.${{ vars.EC2_PUBLIC_IP }}.nip.io' + steps: - name: Checkout Code uses: actions/checkout@v4 diff --git a/.github/workflows/deploy-to-k8s.yml b/.github/workflows/deploy-to-k8s.yml new file mode 100644 index 00000000..367faf82 --- /dev/null +++ b/.github/workflows/deploy-to-k8s.yml @@ -0,0 +1,425 @@ +name: Deploy to Kubernetes + +on: + push: + branches: + - main + - develop + pull_request: + types: [ opened, synchronize, reopened ] + branches: + - develop + +jobs: + setup: + runs-on: ubuntu-latest + if: github.event_name != 'pull_request' || github.event.pull_request.draft == false + outputs: + repo: ${{ steps.set-vars.outputs.repo }} + tag: ${{ steps.set-vars.outputs.tag }} + api_url: ${{ steps.set-vars.outputs.api_url }} + base_url: ${{ steps.set-vars.outputs.base_url }} + genai_url: ${{ steps.set-vars.outputs.genai_url }} + realtime_url: ${{ steps.set-vars.outputs.realtime_url }} + merge_commit: ${{ steps.merge-base-branch.outputs.merge_commit }} + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{ github.head_ref }} + + - name: Fetch base branch + if: github.event_name == 'pull_request' + run: | + git fetch origin ${{ github.base_ref }} + + - name: Merge base branch into PR branch + id: merge-base-branch + if: github.event_name == 'pull_request' + run: | + git merge origin/${{ github.base_ref }} --no-ff --no-edit + echo "merge_commit=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + + - name: Set variables + id: set-vars + run: | + BRANCH="${GITHUB_HEAD_REF:-${GITHUB_REF##*/}}" + echo "repo=${GITHUB_REPOSITORY,,}" >> $GITHUB_OUTPUT + if [[ "$BRANCH" == "main" ]]; then + echo "tag=latest" >> $GITHUB_OUTPUT + echo "api_url=https://api.whiteboard.student.k8s.aet.cit.tum.de" >> $GITHUB_OUTPUT + echo "base_url=https://whiteboard.student.k8s.aet.cit.tum.de" >> $GITHUB_OUTPUT + echo "genai_url=https://genai.whiteboard.student.k8s.aet.cit.tum.de" >> $GITHUB_OUTPUT + echo "realtime_url=https://realtime.whiteboard.student.k8s.aet.cit.tum.de" >> $GITHUB_OUTPUT + elif [[ "$BRANCH" == "develop" ]]; then + echo "tag=develop" >> $GITHUB_OUTPUT + echo "api_url=https://staging.api.whiteboard.student.k8s.aet.cit.tum.de" >> $GITHUB_OUTPUT + echo "base_url=https://staging.whiteboard.student.k8s.aet.cit.tum.de" >> $GITHUB_OUTPUT + echo "genai_url=https://staging.genai.whiteboard.student.k8s.aet.cit.tum.de" >> $GITHUB_OUTPUT + echo "realtime_url=https://staging.realtime.whiteboard.student.k8s.aet.cit.tum.de" >> $GITHUB_OUTPUT + else + BRANCH_SAFE=${BRANCH//\//-} + echo "tag=$BRANCH_SAFE" >> $GITHUB_OUTPUT + echo "api_url=https://$BRANCH_SAFE.api.whiteboard.student.k8s.aet.cit.tum.de" >> $GITHUB_OUTPUT + echo "base_url=https://$BRANCH_SAFE.whiteboard.student.k8s.aet.cit.tum.de" >> $GITHUB_OUTPUT + echo "genai_url=https://$BRANCH_SAFE.genai.whiteboard.student.k8s.aet.cit.tum.de" >> $GITHUB_OUTPUT + echo "realtime_url=wss://$BRANCH_SAFE.realtime.whiteboard.student.k8s.aet.cit.tum.de" >> $GITHUB_OUTPUT + fi + + build-client: + needs: setup + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{ needs.setup.outputs.merge_commit || github.sha }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Log in to GHCR + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Cache Docker layers + uses: actions/cache@v3 + with: + path: /tmp/.buildx-cache-client + key: ${{ runner.os }}-client-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-client- + + - name: Build and push client image + uses: docker/build-push-action@v3 + with: + context: ./client + file: ./client/Dockerfile + push: true + tags: ghcr.io/${{ needs.setup.outputs.repo }}/client:${{ needs.setup.outputs.tag }} + build-args: | + API_URL=${{ needs.setup.outputs.api_url }} + GENAI_API_URL=${{ needs.setup.outputs.genai_url }} + REALTIME_API_URL=${{ needs.setup.outputs.realtime_url }} + BASE_URL=${{ needs.setup.output.base_url }} + platforms: linux/amd64 + + build-realtime: + needs: setup + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{ needs.setup.outputs.merge_commit || github.sha }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Log in to GHCR + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Cache Docker layers + uses: actions/cache@v3 + with: + path: /tmp/.buildx-cache-realtime + key: ${{ runner.os }}-realtime-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-realtime- + + - name: Build and push realtime image + uses: docker/build-push-action@v3 + with: + context: ./realtime + file: ./realtime/Dockerfile + push: true + tags: ghcr.io/${{ needs.setup.outputs.repo }}/realtime:${{ needs.setup.outputs.tag }} + platforms: linux/amd64 + + build-server: + needs: setup + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{ needs.setup.outputs.merge_commit || github.sha }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Log in to GHCR + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Cache Docker layers + uses: actions/cache@v3 + with: + path: /tmp/.buildx-cache-server + key: ${{ runner.os }}-server-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-server- + + - name: Build and push server image + uses: docker/build-push-action@v3 + with: + context: ./server + file: ./server/Dockerfile + push: true + tags: ghcr.io/${{ needs.setup.outputs.repo }}/server:${{ needs.setup.outputs.tag }} + platforms: linux/amd64 + + build-genai: + needs: setup + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{ needs.setup.outputs.merge_commit || github.sha }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Log in to GHCR + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Cache Docker layers + uses: actions/cache@v3 + with: + path: /tmp/.buildx-cache-genai + key: ${{ runner.os }}-genai-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-genai- + + - name: Build and push genai image + uses: docker/build-push-action@v3 + with: + context: ./genai + file: ./genai/Dockerfile + push: true + tags: ghcr.io/${{ needs.setup.outputs.repo }}/genai:${{ needs.setup.outputs.tag }} + build-args: API_URL=${{ needs.setup.outputs.api_url }} + platforms: linux/amd64 + + deploy: + needs: + - build-client + - build-server + - build-genai + - build-realtime + runs-on: ubuntu-latest + outputs: + client_url: ${{ steps.set-vars.outputs.CLIENT_URL }} + server_url: ${{ steps.set-vars.outputs.SERVER_URL }} + auth_url: ${{ steps.set-vars.outputs.AUTH_URL }} + genai_url: ${{ steps.set-vars.outputs.GENAI_URL }} + realtime_url: ${{ steps.set-vars.outputs.REALTIME_URL }} + steps: + - name: Checkout repository + uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{ needs.setup.outputs.merge_commit || github.sha }} + + - name: Set variables + id: set-vars + run: | + BRANCH="${GITHUB_HEAD_REF:-${GITHUB_REF##*/}}" + if [[ "$BRANCH" == "main" ]]; then + CLIENT_URL=whiteboard.student.k8s.aet.cit.tum.de + SERVER_URL=api.whiteboard.student.k8s.aet.cit.tum.de + AUTH_URL=auth.whiteboard.student.k8s.aet.cit.tum.de + GENAI_URL=genai.whiteboard.student.k8s.aet.cit.tum.de + REALTIME_URL=realtime.whiteboard.student.k8s.aet.cit.tum.de + METRICS_URL=metrics.whiteboard.student.k8s.aet.cit.tum.de + echo "NAMESPACE=production" >> $GITHUB_ENV + echo "IMAGE_TAG=latest" >> $GITHUB_ENV + echo "VALUES_FILE=./infrastructure/whiteboard-app/production.values.yaml" >> $GITHUB_ENV + KEYCLOAK_CLIENT_SECRET="${{ secrets.PRODUCTION_KEYCLOAK_CLIENT_SECRET }}" + NEXTAUTH_SECRET="${{ secrets.PRODUCTION_NEXTAUTH_SECRET }}" + POSTGRESQL_SECRET="${{ secrets.PRODUCTION_DB_PASSWORD }}" + KEYCLOAK_SECRET="${{ secrets.PRODUCTION_KEYCLOAK_SECRET }}" + elif [[ "$BRANCH" == "develop" ]]; then + CLIENT_URL=staging.whiteboard.student.k8s.aet.cit.tum.de + SERVER_URL=staging.api.whiteboard.student.k8s.aet.cit.tum.de + AUTH_URL=staging.auth.whiteboard.student.k8s.aet.cit.tum.de + GENAI_URL=staging.genai.whiteboard.student.k8s.aet.cit.tum.de + REALTIME_URL=staging.realtime.whiteboard.student.k8s.aet.cit.tum.de + METRICS_URL=staging.metrics.whiteboard.student.k8s.aet.cit.tum.de + echo "NAMESPACE=staging" >> $GITHUB_ENV + echo "IMAGE_TAG=develop" >> $GITHUB_ENV + echo "VALUES_FILE=./infrastructure/whiteboard-app/staging.values.yaml" >> $GITHUB_ENV + KEYCLOAK_CLIENT_SECRET="${{ secrets.STAGING_KEYCLOAK_CLIENT_SECRET }}" + NEXTAUTH_SECRET="${{ secrets.STAGING_NEXTAUTH_SECRET }}" + POSTGRESQL_SECRET="${{ secrets.STAGING_DB_PASSWORD }}" + KEYCLOAK_SECRET="${{ secrets.STAGING_KEYCLOAK_SECRET }}" + else + BRANCH_SAFE=${BRANCH//\//-} + CLIENT_URL=$BRANCH_SAFE.whiteboard.student.k8s.aet.cit.tum.de + SERVER_URL=$BRANCH_SAFE.api.whiteboard.student.k8s.aet.cit.tum.de + AUTH_URL=$BRANCH_SAFE.auth.whiteboard.student.k8s.aet.cit.tum.de + GENAI_URL=$BRANCH_SAFE.genai.whiteboard.student.k8s.aet.cit.tum.de + REALTIME_URL=$BRANCH_SAFE.realtime.whiteboard.student.k8s.aet.cit.tum.de + METRICS_URL=$BRANCH_SAFE.metrics.whiteboard.student.k8s.aet.cit.tum.de + echo "NAMESPACE=$BRANCH_SAFE" >> $GITHUB_ENV + echo "IMAGE_TAG=$BRANCH_SAFE" >> $GITHUB_ENV + echo "VALUES_FILE=./infrastructure/whiteboard-app/pullrequest.values.yaml" >> $GITHUB_ENV + KEYCLOAK_CLIENT_SECRET="${{ secrets.PR_KEYCLOAK_CLIENT_SECRET }}" + NEXTAUTH_SECRET="${{ secrets.PR_NEXTAUTH_SECRET }}" + POSTGRESQL_SECRET="${{ secrets.PR_DB_PASSWORD }}" + KEYCLOAK_SECRET="${{ secrets.PR_KEYCLOAK_SECRET }}" + fi + + echo "CLIENT_URL=$CLIENT_URL" >> $GITHUB_ENV + echo "SERVER_URL=$SERVER_URL" >> $GITHUB_ENV + echo "AUTH_URL=$AUTH_URL" >> $GITHUB_ENV + echo "GENAI_URL=$GENAI_URL" >> $GITHUB_ENV + echo "OPEN_WEB_UI_API_KEY=${{ secrets.OPEN_WEB_UI_API_KEY }}" >> $GITHUB_ENV + echo "REALTIME_URL=$REALTIME_URL" >> $GITHUB_ENV + echo "METRICS_URL=$METRICS_URL" >> $GITHUB_ENV + echo "KEYCLOAK_CLIENT_SECRET=$KEYCLOAK_CLIENT_SECRET" >> $GITHUB_ENV + echo "NEXTAUTH_SECRET=$NEXTAUTH_SECRET" >> $GITHUB_ENV + echo "POSTGRESQL_SECRET=$POSTGRESQL_SECRET" >> $GITHUB_ENV + echo "KEYCLOAK_SECRET=$KEYCLOAK_SECRET" >> $GITHUB_ENV + + echo "CLIENT_URL=$CLIENT_URL" >> $GITHUB_OUTPUT + echo "SERVER_URL=$SERVER_URL" >> $GITHUB_OUTPUT + echo "AUTH_URL=$AUTH_URL" >> $GITHUB_OUTPUT + echo "GENAI_URL=$GENAI_URL" >> $GITHUB_OUTPUT + echo "REALTIME_URL=$REALTIME_URL" >> $GITHUB_OUTPUT + + - name: Set up Kubeconfig + run: | + echo "${{ secrets.KUBECONFIG }}" > kubeconfig + echo "KUBECONFIG=$(pwd)/kubeconfig" >> $GITHUB_ENV + + - name: Set release name + id: release-name + run: | + BRANCH="${GITHUB_HEAD_REF:-${GITHUB_REF##*/}}" + + if [[ "$BRANCH" == "main" ]]; then + RELEASE_NAME="whiteboard-production" + OBSERVABILITY_RELEASE_NAME="whiteboard-observability-production" + elif [[ "$BRANCH" == "develop" ]]; then + RELEASE_NAME="whiteboard-staging" + OBSERVABILITY_RELEASE_NAME="whiteboard-observability-staging" + else + PR_NUMBER=${{ github.event.pull_request.number }} + RELEASE_NAME="whiteboard-pr-${PR_NUMBER}" + fi + + echo "RELEASE_NAME=${RELEASE_NAME}" >> $GITHUB_ENV + echo "OBSERVABILITY_RELEASE_NAME=${OBSERVABILITY_RELEASE_NAME}" >> $GITHUB_ENV + + - name: Install Helm + uses: azure/setup-helm@v3 + + - name: Deploy App with Helm + run: | + helm upgrade ${{ env.RELEASE_NAME }} ./infrastructure/whiteboard-app/ \ + -f ${{ env.VALUES_FILE }} \ + -n tsd-${{ env.NAMESPACE }} \ + --create-namespace \ + --install \ + --atomic \ + --kubeconfig ${{ env.KUBECONFIG }} \ + --set namespace="${{ env.NAMESPACE }}" \ + --set server.image.tag="${{ env.IMAGE_TAG }}" \ + --set client.image.tag="${{ env.IMAGE_TAG }}" \ + --set genai.image.tag="${{ env.IMAGE_TAG }}" \ + --set realtime.image.tag="${{ env.IMAGE_TAG }}" \ + --set client.url="${{ env.CLIENT_URL }}" \ + --set server.url="${{ env.SERVER_URL }}" \ + --set auth.url="${{ env.AUTH_URL }}" \ + --set genai.url="${{ env.GENAI_URL }}" \ + --set genai.apiKey="${{ env.OPEN_WEB_UI_API_KEY }}" \ + --set realtime.url="${{ env.REALTIME_URL }}" \ + --set keycloak.clientSecret="${{ env.KEYCLOAK_CLIENT_SECRET }}" \ + --set nextauth.secret="${{ env.NEXTAUTH_SECRET }}" \ + --set postgresql.auth.postgresPassword="${{ env.POSTGRESQL_SECRET }}" \ + --set keycloak.externalDatabase.password="${{ env.POSTGRESQL_SECRET }}" \ + --set keycloak.auth.adminPassword="${{ env.KEYCLOAK_SECRET }}" \ + + - name: Deploy Observability Stack with Helm + if: github.event_name != 'pull_request' && (github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/main') + run: | + helm upgrade ${{ env.OBSERVABILITY_RELEASE_NAME }} ./infrastructure/whiteboard-observability/ \ + -f ${{ env.VALUES_FILE }} \ + -n tsd-${{ env.NAMESPACE }} \ + --create-namespace \ + --install \ + --atomic \ + --kubeconfig ${{ env.KUBECONFIG }} \ + --set namespace="${{ env.NAMESPACE }}" \ + --set client.url="${{ env.CLIENT_URL }}" \ + --set server.url="${{ env.SERVER_URL }}" \ + --set genai.url="${{ env.GENAI_URL }}" \ + --set realtime.url="${{ env.REALTIME_URL }}" \ + --set metrics.url="${{ env.METRICS_URL }}" + + comment-pr: + needs: deploy + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + env: + CLIENT_URL: ${{ needs.deploy.outputs.client_url }} + SERVER_URL: ${{ needs.deploy.outputs.server_url }} + AUTH_URL: ${{ needs.deploy.outputs.auth_url }} + GENAI_URL: ${{ needs.deploy.outputs.genai_url }} + REALTIME_URL: ${{ needs.deploy.outputs.realtime_url }} + steps: + - name: Comment on Pull Request with URLs + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const prNumber = context.payload.pull_request.number; + const clientUrl = `https://${process.env.CLIENT_URL}`; + const serverUrl = `https://${process.env.SERVER_URL}`; + const authUrl = `https://${process.env.AUTH_URL}`; + const genaiUrl = `https://${process.env.GENAI_URL}`; + const realtimeUrl = `https://${process.env.REALTIME_URL}` + + // Check existing comments to avoid duplicates + const { data: comments } = await github.rest.issues.listComments({ + ...context.repo, + issue_number: prNumber, + }); + + const commentExists = comments.some(comment => + comment.body.includes('### Deployment URLs') + ); + + if (!commentExists) { + const body = ` + ### Deployment URLs + - **Client:** [${clientUrl}](${clientUrl}) + - **Server:** [${serverUrl}/swagger-ui/index.html](${serverUrl}/swagger-ui/index.html) + - **Realtime:** [${realtimeUrl}/swagger/index.html](${realtimeUrl}/swagger/index.html) + - **GenAI:** [${genaiUrl}/docs](${genaiUrl}/docs) + - **Keycloak:** [${authUrl}](${authUrl}) + `; + + await github.rest.issues.createComment({ + ...context.repo, + issue_number: prNumber, + body, + }); + } \ No newline at end of file diff --git a/.github/workflows/genai-linters.yml b/.github/workflows/genai-linters.yml index 2f31044e..0f871a48 100644 --- a/.github/workflows/genai-linters.yml +++ b/.github/workflows/genai-linters.yml @@ -30,6 +30,11 @@ jobs: cd genai ruff check . + - name: GenAI format (auto-fix) + run: | + cd genai + ruff format . + - name: GenAI formatting run: | cd genai diff --git a/.github/workflows/genai-tests.yml b/.github/workflows/genai-tests.yml index cbbb64c2..2ea3caa7 100644 --- a/.github/workflows/genai-tests.yml +++ b/.github/workflows/genai-tests.yml @@ -26,6 +26,9 @@ jobs: uv pip install -r ./genai/requirements.txt --system - name: GenAI tests + env: + OPEN_WEB_UI_API_KEY: ${{ secrets.OPEN_WEB_UI_API_KEY }} + API_URL: ${{ vars.API_URL }} run: | cd genai pytest \ No newline at end of file diff --git a/.github/workflows/realtime-tests.yml b/.github/workflows/realtime-tests.yml new file mode 100644 index 00000000..61d64c51 --- /dev/null +++ b/.github/workflows/realtime-tests.yml @@ -0,0 +1,18 @@ +name: Realtime tests + +on: + pull_request: + paths: + - 'realtime/**' + workflow_dispatch: + +jobs: + server-test: + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v4 + + - name: Start containers + run: | + docker compose run --rm --entrypoint="go test ./..." realtime \ No newline at end of file diff --git a/.github/workflows/server-linters.yml b/.github/workflows/server-linters.yml index ce4e4b33..5b77e896 100644 --- a/.github/workflows/server-linters.yml +++ b/.github/workflows/server-linters.yml @@ -22,11 +22,6 @@ jobs: - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 - - name: Server lint - run: | - cd server - gradle sonarLintMain - - name: Server formatting run: | cd server diff --git a/.github/workflows/server-tests.yml b/.github/workflows/server-tests.yml index bcfab921..6220d479 100644 --- a/.github/workflows/server-tests.yml +++ b/.github/workflows/server-tests.yml @@ -13,16 +13,8 @@ jobs: - name: Checkout sources uses: actions/checkout@v4 - - name: Setup Java - uses: actions/setup-java@v4 - with: - distribution: 'temurin' - java-version: 17 - - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 - - - name: Server tests + - name: Start containers run: | - cd server - gradle test \ No newline at end of file + docker compose up -d --build --wait --wait-timeout 30 db + docker compose run --rm --entrypoint="gradle test" server + docker compose down \ No newline at end of file diff --git a/client/.prettierignore b/client/.prettierignore new file mode 100644 index 00000000..2603f0ae --- /dev/null +++ b/client/.prettierignore @@ -0,0 +1 @@ +src/api/generated \ No newline at end of file diff --git a/client/.prettierrc b/client/.prettierrc new file mode 100644 index 00000000..8b0bc4ef --- /dev/null +++ b/client/.prettierrc @@ -0,0 +1,3 @@ +{ + "plugins": ["prettier-plugin-tailwindcss"] +} \ No newline at end of file diff --git a/client/Dockerfile b/client/Dockerfile index b0b53c4d..9197c92e 100644 --- a/client/Dockerfile +++ b/client/Dockerfile @@ -1,36 +1,36 @@ -# Install dependencies only when needed -FROM node:18-alpine AS deps - -# Set working directory +FROM node:24.2-bullseye-slim AS build WORKDIR /app -# Install dependencies -COPY package.json package-lock.json ./ +COPY package*.json ./ RUN npm ci -# Rebuild the source code only when needed -FROM node:20-slim AS builder -WORKDIR /app - COPY . . -COPY --from=deps /app/node_modules ./node_modules +ARG API_URL +ENV NEXT_PUBLIC_API_URL=${API_URL} -# Build the Next.js app -RUN npm run build +ARG GENAI_API_URL +ENV NEXT_PUBLIC_GENAI_URL=${GENAI_API_URL} -# Production image -FROM node:20-alpine AS runner -WORKDIR /app +ARG REALTIME_API_URL +ENV NEXT_PUBLIC_REALTIME_URL=${REALTIME_API_URL} +ARG BASE_URL +ENV NEXT_PUBLIC_BASE_URL=${BASE_URL} + +RUN npm run build + +ENV NEXT_TELEMETRY_DISABLED=1 ENV NODE_ENV=production -# Copy only essential files -COPY --from=builder /app/.next ./.next -COPY --from=builder /app/node_modules ./node_modules -COPY --from=builder /app/package.json ./package.json +# Remove the existing node_modules directory and only install production dependencies +RUN npm ci --only=production && npm cache clean --force + +FROM node:24.2-alpine3.21 AS production -EXPOSE 3000 +COPY --from=build /app/.next ./.next +COPY --from=build /app/node_modules ./node_modules +COPY --from=build /app/package.json ./package.json +COPY --from=build /app/public ./public -# Start the Next.js application -CMD ["npm", "start"] +CMD ["npm", "start"] \ No newline at end of file diff --git a/client/Dockerfile.local b/client/Dockerfile.local new file mode 100644 index 00000000..bc60a516 --- /dev/null +++ b/client/Dockerfile.local @@ -0,0 +1,10 @@ +FROM node:24.2-bullseye-slim AS development + +RUN apt update && apt install --yes --no-install-recommends curl default-jre + +WORKDIR /app + +COPY package*.json ./ +RUN npm ci + +EXPOSE 3000 diff --git a/client/eslint.config.mjs b/client/eslint.config.mjs index c85fb67c..d07ef350 100644 --- a/client/eslint.config.mjs +++ b/client/eslint.config.mjs @@ -10,7 +10,20 @@ const compat = new FlatCompat({ }); const eslintConfig = [ + { + ignores: ["src/api/generated"], + }, ...compat.extends("next/core-web-vitals", "next/typescript"), + { + rules: { + "@typescript-eslint/ban-ts-comment": [ + "error", + { + "ts-ignore": false, + }, + ], + } + } ]; export default eslintConfig; diff --git a/client/next.config.ts b/client/next.config.ts index e9ffa308..199557d2 100644 --- a/client/next.config.ts +++ b/client/next.config.ts @@ -2,6 +2,16 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { /* config options here */ + webpack(config) { + // Configure SVG files to be imported as React components + config.module.rules.push({ + test: /\.svg$/, + use: ['@svgr/webpack'], + }); + + return config; + }, }; + export default nextConfig; diff --git a/genai/app/core/__init__.py b/client/openapi-generator-cli similarity index 100% rename from genai/app/core/__init__.py rename to client/openapi-generator-cli diff --git a/client/openapitools.json b/client/openapitools.json new file mode 100644 index 00000000..151c200f --- /dev/null +++ b/client/openapitools.json @@ -0,0 +1,7 @@ +{ + "$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json", + "spaces": 2, + "generator-cli": { + "version": "7.13.0" + } +} diff --git a/client/package-lock.json b/client/package-lock.json index 8ea91fdb..d4ceb49a 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -8,32 +8,53 @@ "name": "teamserverdown", "version": "0.1.0", "dependencies": { - "@radix-ui/react-slot": "^1.2.2", + "@openapitools/openapi-generator-cli": "^2.20.2", + "@radix-ui/react-alert-dialog": "^1.1.14", + "@radix-ui/react-avatar": "^1.1.10", + "@radix-ui/react-dialog": "^1.1.14", + "@radix-ui/react-dropdown-menu": "^2.1.15", + "@radix-ui/react-popover": "^1.1.14", + "@radix-ui/react-select": "^2.2.5", + "@radix-ui/react-separator": "^1.1.7", + "@radix-ui/react-slider": "^1.3.5", + "@radix-ui/react-slot": "^1.2.3", + "@radix-ui/react-tabs": "^1.1.12", + "@radix-ui/react-tooltip": "^1.2.7", "@radix-ui/themes": "^3.2.1", - "@xyflow/react": "^12.6.1", + "@xyflow/react": "^12.7.0", + "axios": "^1.10.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", - "lucide-react": "^0.509.0", - "next": "15.3.2", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "tailwind-merge": "^3.2.0" + "framer-motion": "^12.23.6", + "jsonwebtoken": "^9.0.2", + "lucide-react": "^0.515.0", + "next": "15.3.3", + "next-auth": "^4.24.11", + "react": "^19.1.0", + "react-colorful": "^5.6.1", + "react-dom": "^19.1.0", + "tailwind-merge": "^3.3.1", + "uuid": "^11.1.0", + "zod": "^4.0.5" }, "devDependencies": { "@eslint/eslintrc": "^3", + "@svgr/webpack": "^8.1.0", "@tailwindcss/postcss": "^4", - "@tanstack/eslint-plugin-query": "^5.74.7", - "@tanstack/react-query-devtools": "^5.75.7", - "@types/node": "^20", + "@tanstack/eslint-plugin-query": "^5.78.0", + "@tanstack/react-query-devtools": "^5.80.7", + "@types/jsonwebtoken": "^9.0.9", + "@types/node": "^24", "@types/react": "^19", "@types/react-dom": "^19", - "eslint": "^9.26.0", - "eslint-config-next": "15.3.2", + "eslint": "^9.29.0", + "eslint-config-next": "15.3.3", "prettier": "^3.5.3", "prettier-eslint": "^16.4.2", "prettier-eslint-cli": "^8.0.1", + "prettier-plugin-tailwindcss": "^0.6.13", "tailwindcss": "^4", - "tw-animate-css": "^1.2.9", + "tw-animate-css": "^1.3.4", "typescript": "^5" } }, @@ -42,26 +63,1615 @@ "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", "dev": true, - "license": "MIT", "engines": { - "node": ">=10" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.5.tgz", + "integrity": "sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.4.tgz", + "integrity": "sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.27.3", + "@babel/helpers": "^7.27.4", + "@babel/parser": "^7.27.4", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.27.4", + "@babel/types": "^7.27.3", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.5.tgz", + "integrity": "sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.27.5", + "@babel/types": "^7.27.3", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz", + "integrity": "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.27.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz", + "integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "regexpu-core": "^6.2.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.4.tgz", + "integrity": "sha512-jljfR1rGnXXNWnmQg2K3+bvhkxB51Rl32QRaOTuwwjviGrHzIbSc8+x9CpraDtbT7mfyjXObULP4w/adunNwAw==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", + "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", + "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", + "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-wrap-function": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", + "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", + "dev": true, + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.27.1.tgz", + "integrity": "sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.27.1", + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", + "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", + "dev": true, + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz", + "integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.27.3" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.27.1.tgz", + "integrity": "sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz", + "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", + "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", + "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.27.1.tgz", + "integrity": "sha512-6BpaYGDavZqkI6yT+KSPdpZFfpnd68UKXbcjI9pJ13pvHhPrCKWOOLp+ysvMeA+DxnhuPpgIaRpxRxo5A9t5jw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz", + "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", + "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.27.1.tgz", + "integrity": "sha512-eST9RrwlpaoJBDHShc+DS2SG4ATTi2MYNb4OxYkf3n+7eb49LWpnS+HSpVfW4x927qQwgk8A2hGNVaajAEw0EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", + "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", + "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.27.5.tgz", + "integrity": "sha512-JF6uE2s67f0y2RZcm2kpAUEbD50vH62TyWVebxwHAlbSdM49VqPz8t4a1uIjp4NIOIZ4xzLfjY5emt/RCyC7TQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz", + "integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.27.1.tgz", + "integrity": "sha512-s734HmYU78MVzZ++joYM+NkJusItbdRcbm+AGRgJCt3iA+yux0QpD9cBVdz3tKyrjVYWRl7j0mHSmv4lhV0aoA==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.27.1.tgz", + "integrity": "sha512-7iLhfFAubmpeJe/Wo2TVuDrykh/zlWXLzPNdL0Jqn/Xu8R3QQ8h9ff8FQoISZOsw74/HFqFI7NX63HN7QFIHKA==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/traverse": "^7.27.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-classes/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", + "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/template": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.27.3.tgz", + "integrity": "sha512-s4Jrok82JpiaIprtY2nHsYmrThKvvwgHwjgd7UMiYhZaN0asdXNLr0y+NjTfkA7SyQE5i2Fb7eawUOZmLvyqOA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz", + "integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", + "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.27.1.tgz", + "integrity": "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", + "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.27.1.tgz", + "integrity": "sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", + "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", + "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", + "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz", + "integrity": "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", + "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.27.1.tgz", + "integrity": "sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", + "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", + "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", + "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.27.1.tgz", + "integrity": "sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", + "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz", + "integrity": "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", + "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz", + "integrity": "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz", + "integrity": "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.27.3.tgz", + "integrity": "sha512-7ZZtznF9g4l2JCImCo5LNKFHB5eXnN39lLtLY5Tg+VkR0jwOt7TBciMckuiQIOIW7L5tkQOCh3bVGYeXgMx52Q==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.27.3", + "@babel/plugin-transform-parameters": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", + "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz", + "integrity": "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz", + "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.1.tgz", + "integrity": "sha512-018KRk76HWKeZ5l4oTj2zPpSh+NbGdt0st5S6x0pga6HgrjBOJb24mMDHorFopOOd6YHkLgOZ+zaCjZGPO4aKg==", "dev": true, - "license": "Apache-2.0", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { - "node": ">=6.0.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz", + "integrity": "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz", + "integrity": "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", + "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-constant-elements": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.27.1.tgz", + "integrity": "sha512-edoidOjl/ZxvYo4lSBOQGDSyToYVkTAwyVoa2tkuYTSmjrB1+uAedoL5iROVLXkxH+vRgA7uP4tMg2pUJpZ3Ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-display-name": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.27.1.tgz", + "integrity": "sha512-p9+Vl3yuHPmkirRrg021XiP+EETmPMQTLr6Ayjj85RLNEbb3Eya/4VI0vAdzQG9SEAl2Lnt7fy5lZyMzjYoZQQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.27.1.tgz", + "integrity": "sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.27.1.tgz", + "integrity": "sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q==", + "dev": true, + "dependencies": { + "@babel/plugin-transform-react-jsx": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-pure-annotations": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.27.1.tgz", + "integrity": "sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.27.5.tgz", + "integrity": "sha512-uhB8yHerfe3MWnuLAhEbeQ4afVoqv8BQsPqrTv7e/jZ9y00kJL6l9a/f4OWaKxotmjzewfEyXE1vgDJenkQ2/Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.27.1.tgz", + "integrity": "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", + "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", + "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", + "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", + "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", + "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", + "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.27.1.tgz", + "integrity": "sha512-Q5sT5+O4QUebHdbwKedFBEwRLb02zJ7r4A5Gg2hUoLuU3FjdMcyqcywqUrLCaDsFCxzokf7u9kuy7qz51YUuAg==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", + "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz", + "integrity": "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", + "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz", + "integrity": "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.27.2.tgz", + "integrity": "sha512-Ma4zSuYSlGNRlCLO+EAzLnCmJK2vdstgv+n7aUP+/IKZrOfWHOJVdSJtuub8RzHTj3ahD37k5OKJWvzf16TQyQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.27.1", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.27.1", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-import-assertions": "^7.27.1", + "@babel/plugin-syntax-import-attributes": "^7.27.1", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.27.1", + "@babel/plugin-transform-async-generator-functions": "^7.27.1", + "@babel/plugin-transform-async-to-generator": "^7.27.1", + "@babel/plugin-transform-block-scoped-functions": "^7.27.1", + "@babel/plugin-transform-block-scoping": "^7.27.1", + "@babel/plugin-transform-class-properties": "^7.27.1", + "@babel/plugin-transform-class-static-block": "^7.27.1", + "@babel/plugin-transform-classes": "^7.27.1", + "@babel/plugin-transform-computed-properties": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.27.1", + "@babel/plugin-transform-dotall-regex": "^7.27.1", + "@babel/plugin-transform-duplicate-keys": "^7.27.1", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-dynamic-import": "^7.27.1", + "@babel/plugin-transform-exponentiation-operator": "^7.27.1", + "@babel/plugin-transform-export-namespace-from": "^7.27.1", + "@babel/plugin-transform-for-of": "^7.27.1", + "@babel/plugin-transform-function-name": "^7.27.1", + "@babel/plugin-transform-json-strings": "^7.27.1", + "@babel/plugin-transform-literals": "^7.27.1", + "@babel/plugin-transform-logical-assignment-operators": "^7.27.1", + "@babel/plugin-transform-member-expression-literals": "^7.27.1", + "@babel/plugin-transform-modules-amd": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-modules-systemjs": "^7.27.1", + "@babel/plugin-transform-modules-umd": "^7.27.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-new-target": "^7.27.1", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", + "@babel/plugin-transform-numeric-separator": "^7.27.1", + "@babel/plugin-transform-object-rest-spread": "^7.27.2", + "@babel/plugin-transform-object-super": "^7.27.1", + "@babel/plugin-transform-optional-catch-binding": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1", + "@babel/plugin-transform-parameters": "^7.27.1", + "@babel/plugin-transform-private-methods": "^7.27.1", + "@babel/plugin-transform-private-property-in-object": "^7.27.1", + "@babel/plugin-transform-property-literals": "^7.27.1", + "@babel/plugin-transform-regenerator": "^7.27.1", + "@babel/plugin-transform-regexp-modifiers": "^7.27.1", + "@babel/plugin-transform-reserved-words": "^7.27.1", + "@babel/plugin-transform-shorthand-properties": "^7.27.1", + "@babel/plugin-transform-spread": "^7.27.1", + "@babel/plugin-transform-sticky-regex": "^7.27.1", + "@babel/plugin-transform-template-literals": "^7.27.1", + "@babel/plugin-transform-typeof-symbol": "^7.27.1", + "@babel/plugin-transform-unicode-escapes": "^7.27.1", + "@babel/plugin-transform-unicode-property-regex": "^7.27.1", + "@babel/plugin-transform-unicode-regex": "^7.27.1", + "@babel/plugin-transform-unicode-sets-regex": "^7.27.1", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.11.0", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "core-js-compat": "^3.40.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/preset-react": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.27.1.tgz", + "integrity": "sha512-oJHWh2gLhU9dW9HHr42q0cI0/iHHXTLGe39qvpAZZzagHy0MzYLCnCVV0symeRvzmjHyVU7mw2K06E6u/JwbhA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-transform-react-display-name": "^7.27.1", + "@babel/plugin-transform-react-jsx": "^7.27.1", + "@babel/plugin-transform-react-jsx-development": "^7.27.1", + "@babel/plugin-transform-react-pure-annotations": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.1.tgz", + "integrity": "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-typescript": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz", + "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.4.tgz", + "integrity": "sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.3", + "@babel/parser": "^7.27.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.3", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz", + "integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@emnapi/core": { @@ -69,7 +1679,6 @@ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.3.tgz", "integrity": "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "@emnapi/wasi-threads": "1.0.2", @@ -80,7 +1689,6 @@ "version": "1.4.3", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", - "license": "MIT", "optional": true, "dependencies": { "tslib": "^2.4.0" @@ -91,7 +1699,6 @@ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.2.tgz", "integrity": "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "tslib": "^2.4.0" @@ -102,7 +1709,6 @@ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", "dev": true, - "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.4.3" }, @@ -116,35 +1722,20 @@ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/@eslint-community/regexpp": { "version": "4.12.1", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, - "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/config-array": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", - "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.1.tgz", + "integrity": "sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==", "dev": true, - "license": "Apache-2.0", "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", @@ -155,21 +1746,19 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.2.tgz", - "integrity": "sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.3.tgz", + "integrity": "sha512-u180qk2Um1le4yf0ruXH3PYFeEZeYC3p/4wCTKrr2U1CmGdzGi3KtY0nuPDH48UJxlKCC5RDzbcbh4X0XlqgHg==", "dev": true, - "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/core": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", - "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", + "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", "dev": true, - "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" }, @@ -182,7 +1771,6 @@ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", "dev": true, - "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -202,13 +1790,15 @@ } }, "node_modules/@eslint/js": { - "version": "9.26.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.26.0.tgz", - "integrity": "sha512-I9XlJawFdSMvWjDt6wksMCrgns5ggLNfFwFvnShsleWruvXM514Qxk8V246efTw+eo9JABvVz+u3q2RiAowKxQ==", + "version": "9.29.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.29.0.tgz", + "integrity": "sha512-3PIF4cBw/y+1u2EazflInpV+lYsSG0aByVIQzAgb1m1MhHFSbqTyNqtBKHgWf/9Ykud+DhILS9EGkmekVhbKoQ==", "dev": true, - "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, "node_modules/@eslint/object-schema": { @@ -216,49 +1806,56 @@ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", "dev": true, - "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", - "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.2.tgz", + "integrity": "sha512-4SaFZCNfJqvk/kenHpI8xvN42DMaoycy4PzKc5otHxRswww1kAt82OlBuwRVLofCACCTZEcla2Ydxv8scMXaTg==", "dev": true, - "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.13.0", + "@eslint/core": "^0.15.0", "levn": "^0.4.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.0.tgz", + "integrity": "sha512-b7ePw78tEWWkpgZCDYkbqDOP8dmM6qe+AOC6iuJqlq1R/0ahMAeH3qynpnqKFGkMltrp44ohV4ubGyvLX28tzw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@floating-ui/core": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.0.tgz", - "integrity": "sha512-FRdBLykrPPA6P76GGGqlex/e7fbe0F1ykgxHYNXQsH/iTEtjMj/f9bpY5oQqbjt5VgZvgz/uKXbGuROijh3VLA==", - "license": "MIT", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.1.tgz", + "integrity": "sha512-azI0DrjMMfIug/ExbBaeDVJXcY0a7EPvPjb2xAJPa4HeimBX+Z18HK8QQR3jb6356SnDDdxx+hinMLcJEDdOjw==", "dependencies": { "@floating-ui/utils": "^0.2.9" } }, "node_modules/@floating-ui/dom": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.0.tgz", - "integrity": "sha512-lGTor4VlXcesUMh1cupTUTDoCxMb0V6bm3CnxHzQcw8Eaf1jQbgQX4i02fYgT0vJ82tb5MZ4CZk1LRGkktJCzg==", - "license": "MIT", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.1.tgz", + "integrity": "sha512-cwsmW/zyw5ltYTUeeYJ60CnQuPqmGwuGVhG9w0PRaRKkAyi38BT5CKrpIbb+jtahSwUl04cWzSx9ZOIxeS6RsQ==", "dependencies": { - "@floating-ui/core": "^1.7.0", + "@floating-ui/core": "^1.7.1", "@floating-ui/utils": "^0.2.9" } }, "node_modules/@floating-ui/react-dom": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", - "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", - "license": "MIT", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.3.tgz", + "integrity": "sha512-huMBfiU9UnQ2oBwIhgzyIiSpVgvlDstU8CX0AF+wS+KzmYMs0J2a3GwuFHV1Lz+jlrQGeC1fF+Nv0QoumyV0bA==", "dependencies": { "@floating-ui/dom": "^1.0.0" }, @@ -270,15 +1867,13 @@ "node_modules/@floating-ui/utils": { "version": "0.2.9", "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz", - "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", - "license": "MIT" + "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==" }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "dev": true, - "license": "Apache-2.0", "engines": { "node": ">=18.18.0" } @@ -288,7 +1883,6 @@ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", "dev": true, - "license": "Apache-2.0", "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" @@ -302,7 +1896,6 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", "dev": true, - "license": "Apache-2.0", "engines": { "node": ">=18.18" }, @@ -317,7 +1910,6 @@ "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", "deprecated": "Use @eslint/config-array instead", "dev": true, - "license": "Apache-2.0", "dependencies": { "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", @@ -332,7 +1924,6 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, - "license": "Apache-2.0", "engines": { "node": ">=12.22" }, @@ -346,15 +1937,13 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "deprecated": "Use @eslint/object-schema instead", - "dev": true, - "license": "BSD-3-Clause" + "dev": true }, "node_modules/@humanwhocodes/retry": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, - "license": "Apache-2.0", "engines": { "node": ">=18.18" }, @@ -364,13 +1953,12 @@ } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.1.tgz", - "integrity": "sha512-pn44xgBtgpEbZsu+lWf2KNb6OAf70X68k+yk69Ic2Xz11zHR/w24/U49XT7AeRwJ0Px+mhALhU5LPci1Aymk7A==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.2.tgz", + "integrity": "sha512-OfXHZPppddivUJnqyKoi5YVeHRkkNE2zUFT2gbpKxp/JZCFYEYubnMg+gOp6lWfasPrTS+KPosKqdI+ELYVDtg==", "cpu": [ "arm64" ], - "license": "Apache-2.0", "optional": true, "os": [ "darwin" @@ -386,13 +1974,12 @@ } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.1.tgz", - "integrity": "sha512-VfuYgG2r8BpYiOUN+BfYeFo69nP/MIwAtSJ7/Zpxc5QF3KS22z8Pvg3FkrSFJBPNQ7mmcUcYQFBmEQp7eu1F8Q==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.2.tgz", + "integrity": "sha512-dYvWqmjU9VxqXmjEtjmvHnGqF8GrVjM2Epj9rJ6BUIXvk8slvNDJbhGFvIoXzkDhrJC2jUxNLz/GUjjvSzfw+g==", "cpu": [ "x64" ], - "license": "Apache-2.0", "optional": true, "os": [ "darwin" @@ -414,7 +2001,6 @@ "cpu": [ "arm64" ], - "license": "LGPL-3.0-or-later", "optional": true, "os": [ "darwin" @@ -430,7 +2016,6 @@ "cpu": [ "x64" ], - "license": "LGPL-3.0-or-later", "optional": true, "os": [ "darwin" @@ -446,7 +2031,6 @@ "cpu": [ "arm" ], - "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" @@ -462,7 +2046,6 @@ "cpu": [ "arm64" ], - "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" @@ -478,7 +2061,6 @@ "cpu": [ "ppc64" ], - "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" @@ -494,7 +2076,6 @@ "cpu": [ "s390x" ], - "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" @@ -510,7 +2091,6 @@ "cpu": [ "x64" ], - "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" @@ -526,7 +2106,6 @@ "cpu": [ "arm64" ], - "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" @@ -542,7 +2121,6 @@ "cpu": [ "x64" ], - "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" @@ -552,13 +2130,12 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.1.tgz", - "integrity": "sha512-anKiszvACti2sGy9CirTlNyk7BjjZPiML1jt2ZkTdcvpLU1YH6CXwRAZCA2UmRXnhiIftXQ7+Oh62Ji25W72jA==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.2.tgz", + "integrity": "sha512-0DZzkvuEOqQUP9mo2kjjKNok5AmnOr1jB2XYjkaoNRwpAYMDzRmAqUIa1nRi58S2WswqSfPOWLNOr0FDT3H5RQ==", "cpu": [ "arm" ], - "license": "Apache-2.0", "optional": true, "os": [ "linux" @@ -574,13 +2151,12 @@ } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.1.tgz", - "integrity": "sha512-kX2c+vbvaXC6vly1RDf/IWNXxrlxLNpBVWkdpRq5Ka7OOKj6nr66etKy2IENf6FtOgklkg9ZdGpEu9kwdlcwOQ==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.2.tgz", + "integrity": "sha512-D8n8wgWmPDakc83LORcfJepdOSN6MvWNzzz2ux0MnIbOqdieRZwVYY32zxVx+IFUT8er5KPcyU3XXsn+GzG/0Q==", "cpu": [ "arm64" ], - "license": "Apache-2.0", "optional": true, "os": [ "linux" @@ -596,13 +2172,12 @@ } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.1.tgz", - "integrity": "sha512-7s0KX2tI9mZI2buRipKIw2X1ufdTeaRgwmRabt5bi9chYfhur+/C1OXg3TKg/eag1W+6CCWLVmSauV1owmRPxA==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.2.tgz", + "integrity": "sha512-EGZ1xwhBI7dNISwxjChqBGELCWMGDvmxZXKjQRuqMrakhO8QoMgqCrdjnAqJq/CScxfRn+Bb7suXBElKQpPDiw==", "cpu": [ "s390x" ], - "license": "Apache-2.0", "optional": true, "os": [ "linux" @@ -618,13 +2193,12 @@ } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.1.tgz", - "integrity": "sha512-wExv7SH9nmoBW3Wr2gvQopX1k8q2g5V5Iag8Zk6AVENsjwd+3adjwxtp3Dcu2QhOXr8W9NusBU6XcQUohBZ5MA==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.2.tgz", + "integrity": "sha512-sD7J+h5nFLMMmOXYH4DD9UtSNBD05tWSSdWAcEyzqW8Cn5UxXvsHAxmxSesYUsTOBmUnjtxghKDl15EvfqLFbQ==", "cpu": [ "x64" ], - "license": "Apache-2.0", "optional": true, "os": [ "linux" @@ -640,13 +2214,12 @@ } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.1.tgz", - "integrity": "sha512-DfvyxzHxw4WGdPiTF0SOHnm11Xv4aQexvqhRDAoD00MzHekAj9a/jADXeXYCDFH/DzYruwHbXU7uz+H+nWmSOQ==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.2.tgz", + "integrity": "sha512-NEE2vQ6wcxYav1/A22OOxoSOGiKnNmDzCYFOZ949xFmrWZOVII1Bp3NqVVpvj+3UeHMFyN5eP/V5hzViQ5CZNA==", "cpu": [ "arm64" ], - "license": "Apache-2.0", "optional": true, "os": [ "linux" @@ -662,13 +2235,12 @@ } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.1.tgz", - "integrity": "sha512-pax/kTR407vNb9qaSIiWVnQplPcGU8LRIJpDT5o8PdAx5aAA7AS3X9PS8Isw1/WfqgQorPotjrZL3Pqh6C5EBg==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.2.tgz", + "integrity": "sha512-DOYMrDm5E6/8bm/yQLCWyuDJwUnlevR8xtF8bs+gjZ7cyUNYXiSf/E8Kp0Ss5xasIaXSHzb888V1BE4i1hFhAA==", "cpu": [ "x64" ], - "license": "Apache-2.0", "optional": true, "os": [ "linux" @@ -684,17 +2256,34 @@ } }, "node_modules/@img/sharp-wasm32": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.1.tgz", - "integrity": "sha512-YDybQnYrLQfEpzGOQe7OKcyLUCML4YOXl428gOOzBgN6Gw0rv8dpsJ7PqTHxBnXnwXr8S1mYFSLSa727tpz0xg==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.2.tgz", + "integrity": "sha512-/VI4mdlJ9zkaq53MbIG6rZY+QRN3MLbR6usYlgITEzi4Rpx5S6LFKsycOQjkOGmqTNmkIdLjEvooFKwww6OpdQ==", "cpu": [ "wasm32" ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.4.0" + "@emnapi/runtime": "^1.4.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.2.tgz", + "integrity": "sha512-cfP/r9FdS63VA5k0xiqaNaEoGxBg9k7uE+RQGzuK9fHt7jib4zAVVseR9LsE4gJcNWgT6APKMNnCcnyOtmSEUQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -703,13 +2292,12 @@ } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.1.tgz", - "integrity": "sha512-WKf/NAZITnonBf3U1LfdjoMgNO5JYRSlhovhRhMxXVdvWYveM4kM3L8m35onYIdh75cOMCo1BexgVQcCDzyoWw==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.2.tgz", + "integrity": "sha512-QLjGGvAbj0X/FXl8n1WbtQ6iVBpWU7JO94u/P2M4a8CFYsvQi4GW2mRy/JqkRx0qpBzaOdKJKw8uc930EX2AHw==", "cpu": [ "ia32" ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", "optional": true, "os": [ "win32" @@ -722,13 +2310,12 @@ } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.1.tgz", - "integrity": "sha512-hw1iIAHpNE8q3uMIRCgGOeDoz9KtFNarFLQclLxr/LK1VBkj8nby18RjFvr6aP7USRYAjTZW6yisnBWMX571Tw==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.2.tgz", + "integrity": "sha512-aUdT6zEYtDKCaxkofmmJDJYGCf0+pJg3eU9/oBuqvEeoB9dKI6ZLc/1iLJCTuJQDO4ptntAlkUmHgGjyuobZbw==", "cpu": [ "x64" ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", "optional": true, "os": [ "win32" @@ -745,7 +2332,6 @@ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, - "license": "ISC", "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -763,7 +2349,6 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, - "license": "MIT", "engines": { "node": ">=12" }, @@ -776,7 +2361,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, - "license": "MIT", "engines": { "node": ">=12" }, @@ -789,7 +2373,6 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, - "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -807,7 +2390,6 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, - "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -823,7 +2405,6 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -841,7 +2422,6 @@ "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", "dev": true, - "license": "ISC", "dependencies": { "minipass": "^7.0.4" }, @@ -849,12 +2429,20 @@ "node": ">=18.0.0" } }, + "node_modules/@isaacs/fs-minipass/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/@jest/schemas": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, - "license": "MIT", "dependencies": { "@sinclair/typebox": "^0.27.8" }, @@ -867,7 +2455,6 @@ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", "dev": true, - "license": "MIT", "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -882,7 +2469,6 @@ "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" } @@ -892,7 +2478,6 @@ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.0.0" } @@ -901,26 +2486,31 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, - "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@lukeed/csprng": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", + "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==", + "engines": { + "node": ">=8" + } + }, "node_modules/@messageformat/core": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/@messageformat/core/-/core-3.4.0.tgz", "integrity": "sha512-NgCFubFFIdMWJGN5WuQhHCNmzk7QgiVfrViFxcS99j7F5dDS5EP6raR54I+2ydhe4+5/XTn/YIEppFaqqVWHsw==", "dev": true, - "license": "MIT", "dependencies": { "@messageformat/date-skeleton": "^1.0.0", "@messageformat/number-skeleton": "^1.0.0", @@ -934,22 +2524,19 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@messageformat/date-skeleton/-/date-skeleton-1.1.0.tgz", "integrity": "sha512-rmGAfB1tIPER+gh3p/RgA+PVeRE/gxuQ2w4snFWPF5xtb5mbWR7Cbw7wCOftcUypbD6HVoxrVdyyghPm3WzP5A==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@messageformat/number-skeleton": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@messageformat/number-skeleton/-/number-skeleton-1.2.0.tgz", "integrity": "sha512-xsgwcL7J7WhlHJ3RNbaVgssaIwcEyFkBqxHdcdaiJzwTZAWEOD8BuUFxnxV9k5S0qHN3v/KzUpq0IUpjH1seRg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@messageformat/parser": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/@messageformat/parser/-/parser-5.1.1.tgz", "integrity": "sha512-3p0YRGCcTUCYvBKLIxtDDyrJ0YijGIwrTRu1DT8gIviIDZru8H23+FkY6MJBzM1n9n20CiM4VeDYuBsrrwnLjg==", "dev": true, - "license": "MIT", "dependencies": { "moo": "^0.5.1" } @@ -959,70 +2546,151 @@ "resolved": "https://registry.npmjs.org/@messageformat/runtime/-/runtime-3.0.1.tgz", "integrity": "sha512-6RU5ol2lDtO8bD9Yxe6CZkl0DArdv0qkuoZC+ZwowU+cdRlVE1157wjCmlA5Rsf1Xc/brACnsZa5PZpEDfTFFg==", "dev": true, - "license": "MIT", "dependencies": { "make-plural": "^7.0.0" } }, - "node_modules/@modelcontextprotocol/sdk": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.11.1.tgz", - "integrity": "sha512-9LfmxKTb1v+vUS1/emSk1f5ePmTLkb9Le9AxOB5T0XM59EUumwcS45z05h7aiZx3GI0Bl7mjb3FMEglYj+acuQ==", + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.11.tgz", + "integrity": "sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA==", "dev": true, - "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.9.0" + } + }, + "node_modules/@nestjs/axios": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-4.0.0.tgz", + "integrity": "sha512-1cB+Jyltu/uUPNQrpUimRHEQHrnQrpLzVj6dU3dgn6iDDDdahr10TgHFGTmw5VuJ9GzKZsCLDL78VSwJAs/9JQ==", + "peerDependencies": { + "@nestjs/common": "^10.0.0 || ^11.0.0", + "axios": "^1.3.1", + "rxjs": "^7.0.0" + } + }, + "node_modules/@nestjs/common": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.1.tgz", + "integrity": "sha512-crzp+1qeZ5EGL0nFTPy9NrVMAaUWewV5AwtQyv6SQ9yQPXwRl9W9hm1pt0nAtUu5QbYMbSuo7lYcF81EjM+nCA==", "dependencies": { - "content-type": "^1.0.5", - "cors": "^2.8.5", - "cross-spawn": "^7.0.3", - "eventsource": "^3.0.2", - "express": "^5.0.1", - "express-rate-limit": "^7.5.0", - "pkce-challenge": "^5.0.0", - "raw-body": "^3.0.0", - "zod": "^3.23.8", - "zod-to-json-schema": "^3.24.1" + "file-type": "20.5.0", + "iterare": "1.2.1", + "load-esm": "1.0.2", + "tslib": "2.8.1", + "uid": "2.0.2" }, - "engines": { - "node": ">=18" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "class-transformer": ">=0.4.1", + "class-validator": ">=0.13.2", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.9.tgz", - "integrity": "sha512-OKRBiajrrxB9ATokgEQoG87Z25c67pCpYcCwmXYX8PBftC9pBfN18gnm/fh1wurSLEKIAt+QRFLFCQISrb66Jg==", - "dev": true, - "license": "MIT", - "optional": true, + "node_modules/@nestjs/core": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.1.tgz", + "integrity": "sha512-UFoUAgLKFT+RwHTANJdr0dF7p0qS9QjkaUPjg8aafnjM/qxxxrUVDB49nVvyMlk+Hr1+vvcNaOHbWWQBxoZcHA==", + "hasInstallScript": true, "dependencies": { - "@emnapi/core": "^1.4.0", - "@emnapi/runtime": "^1.4.0", - "@tybys/wasm-util": "^0.9.0" + "@nuxt/opencollective": "0.4.1", + "fast-safe-stringify": "2.1.1", + "iterare": "1.2.1", + "path-to-regexp": "8.2.0", + "tslib": "2.8.1", + "uid": "2.0.2" + }, + "engines": { + "node": ">= 20" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/microservices": "^11.0.0", + "@nestjs/platform-express": "^11.0.0", + "@nestjs/websockets": "^11.0.0", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "@nestjs/microservices": { + "optional": true + }, + "@nestjs/platform-express": { + "optional": true + }, + "@nestjs/websockets": { + "optional": true + } } }, "node_modules/@next/env": { - "version": "15.3.2", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.3.2.tgz", - "integrity": "sha512-xURk++7P7qR9JG1jJtLzPzf0qEvqCN0A/T3DXf8IPMKo9/6FfjxtEffRJIIew/bIL4T3C2jLLqBor8B/zVlx6g==", - "license": "MIT" + "version": "15.3.3", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.3.3.tgz", + "integrity": "sha512-OdiMrzCl2Xi0VTjiQQUK0Xh7bJHnOuET2s+3V+Y40WJBAXrJeGA3f+I8MZJ/YQ3mVGi5XGR1L66oFlgqXhQ4Vw==" }, "node_modules/@next/eslint-plugin-next": { - "version": "15.3.2", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.3.2.tgz", - "integrity": "sha512-ijVRTXBgnHT33aWnDtmlG+LJD+5vhc9AKTJPquGG5NKXjpKNjc62woIhFtrAcWdBobt8kqjCoaJ0q6sDQoX7aQ==", + "version": "15.3.3", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.3.3.tgz", + "integrity": "sha512-VKZJEiEdpKkfBmcokGjHu0vGDG+8CehGs90tBEy/IDoDDKGngeyIStt2MmE5FYNyU9BhgR7tybNWTAJY/30u+Q==", "dev": true, - "license": "MIT", "dependencies": { "fast-glob": "3.3.1" } }, + "node_modules/@next/eslint-plugin-next/node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@next/eslint-plugin-next/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/@next/swc-darwin-arm64": { - "version": "15.3.2", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.3.2.tgz", - "integrity": "sha512-2DR6kY/OGcokbnCsjHpNeQblqCZ85/1j6njYSkzRdpLn5At7OkSdmk7WyAmB9G0k25+VgqVZ/u356OSoQZ3z0g==", + "version": "15.3.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.3.3.tgz", + "integrity": "sha512-WRJERLuH+O3oYB4yZNVahSVFmtxRNjNF1I1c34tYMoJb0Pve+7/RaLAJJizyYiFhjYNGHRAE1Ri2Fd23zgDqhg==", "cpu": [ "arm64" ], - "license": "MIT", "optional": true, "os": [ "darwin" @@ -1032,13 +2700,12 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "15.3.2", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.3.2.tgz", - "integrity": "sha512-ro/fdqaZWL6k1S/5CLv1I0DaZfDVJkWNaUU3un8Lg6m0YENWlDulmIWzV96Iou2wEYyEsZq51mwV8+XQXqMp3w==", + "version": "15.3.3", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.3.3.tgz", + "integrity": "sha512-XHdzH/yBc55lu78k/XwtuFR/ZXUTcflpRXcsu0nKmF45U96jt1tsOZhVrn5YH+paw66zOANpOnFQ9i6/j+UYvw==", "cpu": [ "x64" ], - "license": "MIT", "optional": true, "os": [ "darwin" @@ -1048,13 +2715,12 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.3.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.3.2.tgz", - "integrity": "sha512-covwwtZYhlbRWK2HlYX9835qXum4xYZ3E2Mra1mdQ+0ICGoMiw1+nVAn4d9Bo7R3JqSmK1grMq/va+0cdh7bJA==", + "version": "15.3.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.3.3.tgz", + "integrity": "sha512-VZ3sYL2LXB8znNGcjhocikEkag/8xiLgnvQts41tq6i+wql63SMS1Q6N8RVXHw5pEUjiof+II3HkDd7GFcgkzw==", "cpu": [ "arm64" ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -1064,13 +2730,12 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "15.3.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.3.2.tgz", - "integrity": "sha512-KQkMEillvlW5Qk5mtGA/3Yz0/tzpNlSw6/3/ttsV1lNtMuOHcGii3zVeXZyi4EJmmLDKYcTcByV2wVsOhDt/zg==", + "version": "15.3.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.3.3.tgz", + "integrity": "sha512-h6Y1fLU4RWAp1HPNJWDYBQ+e3G7sLckyBXhmH9ajn8l/RSMnhbuPBV/fXmy3muMcVwoJdHL+UtzRzs0nXOf9SA==", "cpu": [ "arm64" ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -1080,13 +2745,12 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "15.3.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.3.2.tgz", - "integrity": "sha512-uRBo6THWei0chz+Y5j37qzx+BtoDRFIkDzZjlpCItBRXyMPIg079eIkOCl3aqr2tkxL4HFyJ4GHDes7W8HuAUg==", + "version": "15.3.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.3.3.tgz", + "integrity": "sha512-jJ8HRiF3N8Zw6hGlytCj5BiHyG/K+fnTKVDEKvUCyiQ/0r5tgwO7OgaRiOjjRoIx2vwLR+Rz8hQoPrnmFbJdfw==", "cpu": [ "x64" ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -1096,13 +2760,12 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "15.3.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.3.2.tgz", - "integrity": "sha512-+uxFlPuCNx/T9PdMClOqeE8USKzj8tVz37KflT3Kdbx/LOlZBRI2yxuIcmx1mPNK8DwSOMNCr4ureSet7eyC0w==", + "version": "15.3.3", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.3.3.tgz", + "integrity": "sha512-HrUcTr4N+RgiiGn3jjeT6Oo208UT/7BuTr7K0mdKRBtTbT4v9zJqCDKO97DUqqoBK1qyzP1RwvrWTvU6EPh/Cw==", "cpu": [ "x64" ], - "license": "MIT", "optional": true, "os": [ "linux" @@ -1112,13 +2775,12 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.3.2", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.3.2.tgz", - "integrity": "sha512-LLTKmaI5cfD8dVzh5Vt7+OMo+AIOClEdIU/TSKbXXT2iScUTSxOGoBhfuv+FU8R9MLmrkIL1e2fBMkEEjYAtPQ==", + "version": "15.3.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.3.3.tgz", + "integrity": "sha512-SxorONgi6K7ZUysMtRF3mIeHC5aA3IQLmKFQzU0OuhuUYwpOBc1ypaLJLP5Bf3M9k53KUUUj4vTPwzGvl/NwlQ==", "cpu": [ "arm64" ], - "license": "MIT", "optional": true, "os": [ "win32" @@ -1128,13 +2790,12 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "15.3.2", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.3.2.tgz", - "integrity": "sha512-aW5B8wOPioJ4mBdMDXkt5f3j8pUr9W8AnlX0Df35uRWNT1Y6RIybxjnSUe+PhM+M1bwgyY8PHLmXZC6zT1o5tA==", + "version": "15.3.3", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.3.3.tgz", + "integrity": "sha512-4QZG6F8enl9/S2+yIiOiju0iCTFd93d8VC1q9LZS4p/Xuk81W2QDjCFeoogmrWWkAD59z8ZxepBQap2dKS5ruw==", "cpu": [ "x64" ], - "license": "MIT", "optional": true, "os": [ "win32" @@ -1148,7 +2809,6 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, - "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -1162,7 +2822,6 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, - "license": "MIT", "engines": { "node": ">= 8" } @@ -1172,7 +2831,6 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, - "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -1186,17 +2844,108 @@ "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", "dev": true, - "license": "MIT", "engines": { "node": ">=12.4.0" } }, + "node_modules/@nuxt/opencollective": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@nuxt/opencollective/-/opencollective-0.4.1.tgz", + "integrity": "sha512-GXD3wy50qYbxCJ652bDrDzgMr3NFEkIS374+IgFQKkCvk9yiYcLvX2XDYr7UyQxf4wK0e+yqDYRubZ0DtOxnmQ==", + "dependencies": { + "consola": "^3.2.3" + }, + "bin": { + "opencollective": "bin/opencollective.js" + }, + "engines": { + "node": "^14.18.0 || >=16.10.0", + "npm": ">=5.10.0" + } + }, + "node_modules/@nuxtjs/opencollective": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz", + "integrity": "sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==", + "dependencies": { + "chalk": "^4.1.0", + "consola": "^2.15.0", + "node-fetch": "^2.6.1" + }, + "bin": { + "opencollective": "bin/opencollective.js" + }, + "engines": { + "node": ">=8.0.0", + "npm": ">=5.0.0" + } + }, + "node_modules/@nuxtjs/opencollective/node_modules/consola": { + "version": "2.15.3", + "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", + "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" + }, + "node_modules/@openapitools/openapi-generator-cli": { + "version": "2.20.2", + "resolved": "https://registry.npmjs.org/@openapitools/openapi-generator-cli/-/openapi-generator-cli-2.20.2.tgz", + "integrity": "sha512-dNFwQcQu6+rmEWSJj4KUx468+p6Co7nfpVgi5QEfVhzKj7wBytz9GEhCN2qmVgtg3ZX8H6nxbXI8cjh7hAxAqg==", + "hasInstallScript": true, + "dependencies": { + "@nestjs/axios": "4.0.0", + "@nestjs/common": "11.1.1", + "@nestjs/core": "11.1.1", + "@nuxtjs/opencollective": "0.3.2", + "axios": "1.9.0", + "chalk": "4.1.2", + "commander": "8.3.0", + "compare-versions": "4.1.4", + "concurrently": "6.5.1", + "console.table": "0.10.0", + "fs-extra": "11.3.0", + "glob": "9.3.5", + "inquirer": "8.2.6", + "lodash": "4.17.21", + "proxy-agent": "6.5.0", + "reflect-metadata": "0.2.2", + "rxjs": "7.8.2", + "tslib": "2.8.1" + }, + "bin": { + "openapi-generator-cli": "main.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/openapi_generator" + } + }, + "node_modules/@openapitools/openapi-generator-cli/node_modules/axios": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", + "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/@panva/hkdf": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.2.1.tgz", + "integrity": "sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, - "license": "MIT", "optional": true, "engines": { "node": ">=14" @@ -1208,7 +2957,6 @@ "resolved": "https://registry.npmjs.org/prettier-eslint/-/prettier-eslint-16.4.2.tgz", "integrity": "sha512-vtJAQEkaN8fW5QKl08t7A5KCjlZuDUNeIlr9hgolMS5s3+uzbfRHDwaRnzrdqnY2YpHDmeDS/8zY0MKQHXJtaA==", "dev": true, - "license": "MIT", "dependencies": { "@typescript-eslint/parser": "^6.21.0", "common-tags": "^1.8.2", @@ -1247,7 +2995,6 @@ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, - "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -1271,7 +3018,6 @@ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, - "license": "MIT", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } @@ -1281,7 +3027,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", @@ -1310,7 +3055,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", "dev": true, - "license": "MIT", "dependencies": { "@typescript-eslint/types": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0" @@ -1328,7 +3072,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", "dev": true, - "license": "MIT", "engines": { "node": "^16.0.0 || >=18.0.0" }, @@ -1342,7 +3085,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "@typescript-eslint/types": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0", @@ -1371,7 +3113,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -1387,7 +3128,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", "dev": true, - "license": "MIT", "dependencies": { "@typescript-eslint/types": "6.21.0", "eslint-visitor-keys": "^3.4.1" @@ -1400,22 +3140,11 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@prettier/eslint/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/@prettier/eslint/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -1425,7 +3154,6 @@ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, - "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" }, @@ -1439,7 +3167,6 @@ "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -1495,7 +3222,6 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -1507,25 +3233,11 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@prettier/eslint/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/@prettier/eslint/node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", @@ -1543,7 +3255,6 @@ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, - "license": "MIT", "dependencies": { "flat-cache": "^3.0.4" }, @@ -1556,7 +3267,6 @@ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, - "license": "MIT", "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", @@ -1571,7 +3281,6 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, - "license": "MIT", "dependencies": { "type-fest": "^0.20.2" }, @@ -1582,17 +3291,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@prettier/eslint/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/@prettier/eslint/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">=8" + "node": ">=10" } }, "node_modules/@prettier/eslint/node_modules/ts-api-utils": { @@ -1600,7 +3308,6 @@ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", "dev": true, - "license": "MIT", "engines": { "node": ">=16" }, @@ -1608,31 +3315,39 @@ "typescript": ">=4.2.0" } }, + "node_modules/@prettier/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@radix-ui/colors": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@radix-ui/colors/-/colors-3.0.0.tgz", - "integrity": "sha512-FUOsGBkHrYJwCSEtWRCIfQbZG7q1e6DgxCIOe1SUQzDe/7rXXeA47s8yCn6fuTNQAj1Zq4oTFi9Yjp3wzElcxg==", - "license": "MIT" + "integrity": "sha512-FUOsGBkHrYJwCSEtWRCIfQbZG7q1e6DgxCIOe1SUQzDe/7rXXeA47s8yCn6fuTNQAj1Zq4oTFi9Yjp3wzElcxg==" }, "node_modules/@radix-ui/number": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", - "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==", - "license": "MIT" + "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==" }, "node_modules/@radix-ui/primitive": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz", - "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==", - "license": "MIT" + "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==" }, "node_modules/@radix-ui/react-accessible-icon": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-accessible-icon/-/react-accessible-icon-1.1.6.tgz", - "integrity": "sha512-Eh+3JK1ApmX7DYGMquj6gctxmbLX4JD+5kn1Pi/VlFGdHvod+dtoFoAGEkz3Muy/E+MVC7P77MPC5zqAaxrHxg==", - "license": "MIT", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-accessible-icon/-/react-accessible-icon-1.1.7.tgz", + "integrity": "sha512-XM+E4WXl0OqUJFovy6GjmxxFyx9opfCAIUku4dlKRd5YEPqt4kALOkQOp0Of6reHuUkJuiPBEc5k0o4z4lTC8A==", "dependencies": { - "@radix-ui/react-visually-hidden": "1.2.2" + "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -1650,19 +3365,18 @@ } }, "node_modules/@radix-ui/react-accordion": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.10.tgz", - "integrity": "sha512-x+URzV1siKmeXPSUIQ22L81qp2eOhjpy3tgteF+zOr4d1u0qJnFuyBF4MoQRhmKP6ivDxlvDAvqaF77gh7DOIw==", - "license": "MIT", + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.11.tgz", + "integrity": "sha512-l3W5D54emV2ues7jjeG1xcyN7S3jnK3zE2zHqgn0CmMsy9lNJwmgcrmaxS+7ipw15FAivzKNzH3d5EcGoFKw0A==", "dependencies": { "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-collapsible": "1.1.10", - "@radix-ui/react-collection": "1.1.6", + "@radix-ui/react-collapsible": "1.1.11", + "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { @@ -1681,17 +3395,17 @@ } }, "node_modules/@radix-ui/react-alert-dialog": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.13.tgz", - "integrity": "sha512-/uPs78OwxGxslYOG5TKeUsv9fZC0vo376cXSADdKirTmsLJU2au6L3n34c3p6W26rFDDDze/hwy4fYeNd0qdGA==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.14.tgz", + "integrity": "sha512-IOZfZ3nPvN6lXpJTBCunFQPRSvK8MDgSc1FB85xnIpUKOw9en0dJj8JmCAxV7BiZdtYlUpmrQjoTFkVYtdoWzQ==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dialog": "1.1.13", - "@radix-ui/react-primitive": "2.1.2", - "@radix-ui/react-slot": "1.2.2" + "@radix-ui/react-dialog": "1.1.14", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -1709,12 +3423,11 @@ } }, "node_modules/@radix-ui/react-arrow": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.6.tgz", - "integrity": "sha512-2JMfHJf/eVnwq+2dewT3C0acmCWD3XiVA1Da+jTDqo342UlU13WvXtqHhG+yJw5JeQmu4ue2eMy6gcEArLBlcw==", - "license": "MIT", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", + "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", "dependencies": { - "@radix-ui/react-primitive": "2.1.2" + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", @@ -1732,12 +3445,11 @@ } }, "node_modules/@radix-ui/react-aspect-ratio": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-aspect-ratio/-/react-aspect-ratio-1.1.6.tgz", - "integrity": "sha512-cZvNiIKqWQjf3DsQk1+wktF3DD73kUbWQ2E/XSh8m2IcpFGwg4IiIvGlVNdovxuozK/9+4QXd2zVlzUMiexSDg==", - "license": "MIT", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-aspect-ratio/-/react-aspect-ratio-1.1.7.tgz", + "integrity": "sha512-Yq6lvO9HQyPwev1onK1daHCHqXVLzPhSVjmsNjCa2Zcxy2f7uJD2itDtxknv6FzAKCwD1qQkeVDmX/cev13n/g==", "dependencies": { - "@radix-ui/react-primitive": "2.1.2" + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", @@ -1755,13 +3467,13 @@ } }, "node_modules/@radix-ui/react-avatar": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.9.tgz", - "integrity": "sha512-10tQokfvZdFvnvDkcOJPjm2pWiP8A0R4T83MoD7tb15bC/k2GU7B1YBuzJi8lNQ8V1QqhP8ocNqp27ByZaNagQ==", + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.10.tgz", + "integrity": "sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog==", "license": "MIT", "dependencies": { "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-is-hydrated": "0.1.0", "@radix-ui/react-use-layout-effect": "1.1.1" @@ -1782,16 +3494,15 @@ } }, "node_modules/@radix-ui/react-checkbox": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.1.tgz", - "integrity": "sha512-xTaLKAO+XXMPK/BpVTSaAAhlefmvMSACjIhK9mGsImvX2ljcTDm8VGR1CuS1uYcNdR5J+oiOhoJZc5un6bh3VQ==", - "license": "MIT", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.2.tgz", + "integrity": "sha512-yd+dI56KZqawxKZrJ31eENUwqc1QSqg4OZ15rybGjF2ZNwMO+wCyHzAVLRp9qoYJf7kYy0YpZ2b0JCzJ42HZpA==", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-presence": "1.1.4", - "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" @@ -1812,17 +3523,16 @@ } }, "node_modules/@radix-ui/react-collapsible": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.10.tgz", - "integrity": "sha512-O2mcG3gZNkJ/Ena34HurA3llPOEA/M4dJtIRMa6y/cknRDC8XY5UZBInKTsUwW5cUue9A4k0wi1XU5fKBzKe1w==", - "license": "MIT", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.11.tgz", + "integrity": "sha512-2qrRsVGSCYasSz1RFOorXwl0H7g7J1frQtgpQgYrt+MOidtPAINHn9CPovQXb83r8ahapdx3Tu0fa/pdFFSdPg==", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.4", - "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, @@ -1842,15 +3552,14 @@ } }, "node_modules/@radix-ui/react-collection": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.6.tgz", - "integrity": "sha512-PbhRFK4lIEw9ADonj48tiYWzkllz81TM7KVYyyMMw2cwHO7D5h4XKEblL8NlaRisTK3QTe6tBEhDccFUryxHBQ==", - "license": "MIT", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", + "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.2", - "@radix-ui/react-slot": "1.2.2" + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -1871,7 +3580,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", - "license": "MIT", "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -1886,7 +3594,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", - "license": "MIT", "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -1898,15 +3605,14 @@ } }, "node_modules/@radix-ui/react-context-menu": { - "version": "2.2.14", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context-menu/-/react-context-menu-2.2.14.tgz", - "integrity": "sha512-RUHvrJE2qKAd9pQ50HZZsePio4SMWEh8v6FWQwg/4t6K1fuxfb4Ec40VEVvni6V7nFxmj9srU4UZc7aYp8x0LQ==", - "license": "MIT", + "version": "2.2.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context-menu/-/react-context-menu-2.2.15.tgz", + "integrity": "sha512-UsQUMjcYTsBjTSXw0P3GO0werEQvUY2plgRQuKoCTtkNr45q1DiL51j4m7gxhABzZ0BadoXNsIbg7F3KwiUBbw==", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-menu": "2.1.14", - "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-menu": "2.1.15", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, @@ -1926,22 +3632,22 @@ } }, "node_modules/@radix-ui/react-dialog": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.13.tgz", - "integrity": "sha512-ARFmqUyhIVS3+riWzwGTe7JLjqwqgnODBUZdqpWar/z1WFs9z76fuOs/2BOWCR+YboRn4/WN9aoaGVwqNRr8VA==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.14.tgz", + "integrity": "sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.9", + "@radix-ui/react-dismissable-layer": "1.1.10", "@radix-ui/react-focus-guards": "1.1.2", - "@radix-ui/react-focus-scope": "1.1.6", + "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-portal": "1.1.8", + "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.4", - "@radix-ui/react-primitive": "2.1.2", - "@radix-ui/react-slot": "1.2.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" @@ -1965,7 +3671,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", - "license": "MIT", "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -1977,14 +3682,13 @@ } }, "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.9.tgz", - "integrity": "sha512-way197PiTvNp+WBP7svMJasHl+vibhWGQDb6Mgf5mhEWJkgb85z7Lfl9TUdkqpWsf8GRNmoopx9ZxCyDzmgRMQ==", - "license": "MIT", + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.10.tgz", + "integrity": "sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, @@ -2004,17 +3708,17 @@ } }, "node_modules/@radix-ui/react-dropdown-menu": { - "version": "2.1.14", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.14.tgz", - "integrity": "sha512-lzuyNjoWOoaMFE/VC5FnAAYM16JmQA8ZmucOXtlhm2kKR5TSU95YLAueQ4JYuRmUJmBvSqXaVFGIfuukybwZJQ==", + "version": "2.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.15.tgz", + "integrity": "sha512-mIBnOjgwo9AH3FyKaSWoSu/dYj6VdhJ7frEPiGTeXCdUFHjl9h3mFh2wwhEtINOmYXWhdpf1rY2minFsmaNgVQ==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-menu": "2.1.14", - "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-menu": "2.1.15", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { @@ -2036,7 +3740,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.2.tgz", "integrity": "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==", - "license": "MIT", "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -2048,13 +3751,12 @@ } }, "node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.6.tgz", - "integrity": "sha512-r9zpYNUQY+2jWHWZGyddQLL9YHkM/XvSFHVcWs7bdVuxMAnCwTAuy6Pf47Z4nw7dYcUou1vg/VgjjrrH03VeBw==", - "license": "MIT", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { @@ -2073,17 +3775,16 @@ } }, "node_modules/@radix-ui/react-form": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-form/-/react-form-0.1.6.tgz", - "integrity": "sha512-7AMSeVvepeJU8dIUSDlR92Pm8mScmqWBaiYw0oIAcN8wU/H5muJGcZdU/sYRHNws3b7eCoHyq4FTLrstVtCacQ==", - "license": "MIT", + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-form/-/react-form-0.1.7.tgz", + "integrity": "sha512-IXLKFnaYvFg/KkeV5QfOX7tRnwHXp127koOFUjLWMTrRv5Rny3DQcAtIFFeA/Cli4HHM8DuJCXAUsgnFVJndlw==", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-label": "2.1.6", - "@radix-ui/react-primitive": "2.1.2" + "@radix-ui/react-label": "2.1.7", + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", @@ -2101,19 +3802,18 @@ } }, "node_modules/@radix-ui/react-hover-card": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.1.13.tgz", - "integrity": "sha512-Wtjvx0d/6Bgd/jAYS1mW6IPSUQ25y0hkUSOS1z5/4+U8+DJPwKroqJlM/AlVFl3LywGoruiPmcvB9Aks9mSOQw==", - "license": "MIT", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.1.14.tgz", + "integrity": "sha512-CPYZ24Mhirm+g6D8jArmLzjYu4Eyg3TTUHswR26QgzXBHBe64BO/RHOJKzmF/Dxb4y4f9PKyJdwm/O/AhNkb+Q==", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.9", - "@radix-ui/react-popper": "1.2.6", - "@radix-ui/react-portal": "1.1.8", + "@radix-ui/react-dismissable-layer": "1.1.10", + "@radix-ui/react-popper": "1.2.7", + "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.4", - "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { @@ -2135,7 +3835,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", - "license": "MIT", "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, @@ -2150,12 +3849,11 @@ } }, "node_modules/@radix-ui/react-label": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.6.tgz", - "integrity": "sha512-S/hv1mTlgcPX2gCTJrWuTjSXf7ER3Zf7zWGtOprxhIIY93Qin3n5VgNA0Ez9AgrK/lEtlYgzLd4f5x6AVar4Yw==", - "license": "MIT", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.7.tgz", + "integrity": "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==", "dependencies": { - "@radix-ui/react-primitive": "2.1.2" + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", @@ -2173,26 +3871,25 @@ } }, "node_modules/@radix-ui/react-menu": { - "version": "2.1.14", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.14.tgz", - "integrity": "sha512-0zSiBAIFq9GSKoSH5PdEaQeRB3RnEGxC+H2P0egtnKoKKLNBH8VBHyVO6/jskhjAezhOIplyRUj7U2lds9A+Yg==", - "license": "MIT", + "version": "2.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.15.tgz", + "integrity": "sha512-tVlmA3Vb9n8SZSd+YSbuFR66l87Wiy4du+YE+0hzKQEANA+7cWKH1WgqcEX4pXqxUFQKrWQGHdvEfw00TjFiew==", "dependencies": { "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-collection": "1.1.6", + "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.9", + "@radix-ui/react-dismissable-layer": "1.1.10", "@radix-ui/react-focus-guards": "1.1.2", - "@radix-ui/react-focus-scope": "1.1.6", + "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.6", - "@radix-ui/react-portal": "1.1.8", + "@radix-ui/react-popper": "1.2.7", + "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.4", - "@radix-ui/react-primitive": "2.1.2", - "@radix-ui/react-roving-focus": "1.1.9", - "@radix-ui/react-slot": "1.2.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.10", + "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" @@ -2213,20 +3910,19 @@ } }, "node_modules/@radix-ui/react-menubar": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menubar/-/react-menubar-1.1.14.tgz", - "integrity": "sha512-nWLOS7EG3iYhT/zlE/Pbip17rrMnV/0AS7ueb3pKHTSAnpA6/N9rXQYowulZw4owZ9P+qSilHsFzSx/kU7yplQ==", - "license": "MIT", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menubar/-/react-menubar-1.1.15.tgz", + "integrity": "sha512-Z71C7LGD+YDYo3TV81paUs8f3Zbmkvg6VLRQpKYfzioOE6n7fOhA3ApK/V/2Odolxjoc4ENk8AYCjohCNayd5A==", "dependencies": { "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-collection": "1.1.6", + "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-menu": "2.1.14", - "@radix-ui/react-primitive": "2.1.2", - "@radix-ui/react-roving-focus": "1.1.9", + "@radix-ui/react-menu": "2.1.15", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.10", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { @@ -2245,25 +3941,24 @@ } }, "node_modules/@radix-ui/react-navigation-menu": { - "version": "1.2.12", - "resolved": "https://registry.npmjs.org/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.2.12.tgz", - "integrity": "sha512-iExvawdu7n6DidDJRU5pMTdi+Z3DaVPN4UZbAGuTs7nJA8P4RvvkEz+XYI2UJjb/Hh23RrH19DakgZNLdaq9Bw==", - "license": "MIT", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.2.13.tgz", + "integrity": "sha512-WG8wWfDiJlSF5hELjwfjSGOXcBR/ZMhBFCGYe8vERpC39CQYZeq1PQ2kaYHdye3V95d06H89KGMsVCIE4LWo3g==", "dependencies": { "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-collection": "1.1.6", + "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.9", + "@radix-ui/react-dismissable-layer": "1.1.10", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.4", - "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", - "@radix-ui/react-visually-hidden": "1.2.2" + "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -2281,19 +3976,18 @@ } }, "node_modules/@radix-ui/react-one-time-password-field": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-one-time-password-field/-/react-one-time-password-field-0.1.6.tgz", - "integrity": "sha512-hLjEmrZ7Ld++eL/hUOqfmBA4pEk78Sf7iXvEWs9t3aAuvWmtI24FuEfiMYbiXVJuUjzpo3vND6eUTAPFvG44Gg==", - "license": "MIT", + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-one-time-password-field/-/react-one-time-password-field-0.1.7.tgz", + "integrity": "sha512-w1vm7AGI8tNXVovOK7TYQHrAGpRF7qQL+ENpT1a743De5Zmay2RbWGKAiYDKIyIuqptns+znCKwNztE2xl1n0Q==", "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-collection": "1.1.6", + "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-primitive": "2.1.2", - "@radix-ui/react-roving-focus": "1.1.9", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.10", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-is-hydrated": "0.1.0", @@ -2315,16 +4009,15 @@ } }, "node_modules/@radix-ui/react-password-toggle-field": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-password-toggle-field/-/react-password-toggle-field-0.1.1.tgz", - "integrity": "sha512-p5IJUTuyknUMv5VPGEa3fZvjb77cPzCK9w+Em/xHLaTqCVfIhykvdzAe8+X5BmboE9NwxDEBmbWnceFVw4tDdg==", - "license": "MIT", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-password-toggle-field/-/react-password-toggle-field-0.1.2.tgz", + "integrity": "sha512-F90uYnlBsLPU1UbSLciLsWQmk8+hdWa6SFw4GXaIdNWxFxI5ITKVdAG64f+Twaa9ic6xE7pqxPyUmodrGjT4pQ==", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-is-hydrated": "0.1.0" @@ -2345,23 +4038,22 @@ } }, "node_modules/@radix-ui/react-popover": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.13.tgz", - "integrity": "sha512-84uqQV3omKDR076izYgcha6gdpN8m3z6w/AeJ83MSBJYVG/AbOHdLjAgsPZkeC/kt+k64moXFCnio8BbqXszlw==", - "license": "MIT", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.14.tgz", + "integrity": "sha512-ODz16+1iIbGUfFEfKx2HTPKizg2MN39uIOV8MXeHnmdd3i/N9Wt7vU46wbHsqA0xoaQyXVcs0KIlBdOA2Y95bw==", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.9", + "@radix-ui/react-dismissable-layer": "1.1.10", "@radix-ui/react-focus-guards": "1.1.2", - "@radix-ui/react-focus-scope": "1.1.6", + "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.6", - "@radix-ui/react-portal": "1.1.8", + "@radix-ui/react-popper": "1.2.7", + "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.4", - "@radix-ui/react-primitive": "2.1.2", - "@radix-ui/react-slot": "1.2.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" @@ -2382,16 +4074,15 @@ } }, "node_modules/@radix-ui/react-popper": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.6.tgz", - "integrity": "sha512-7iqXaOWIjDBfIG7aq8CUEeCSsQMLFdn7VEE8TaFz704DtEzpPHR7w/uuzRflvKgltqSAImgcmxQ7fFX3X7wasg==", - "license": "MIT", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.7.tgz", + "integrity": "sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==", "dependencies": { "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.1.6", + "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", @@ -2414,12 +4105,11 @@ } }, "node_modules/@radix-ui/react-portal": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.8.tgz", - "integrity": "sha512-hQsTUIn7p7fxCPvao/q6wpbxmCwgLrlz+nOrJgC+RwfZqWY/WN+UMqkXzrtKbPrF82P43eCTl3ekeKuyAQbFeg==", - "license": "MIT", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", "dependencies": { - "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { @@ -2441,7 +4131,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.4.tgz", "integrity": "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==", - "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" @@ -2462,12 +4151,11 @@ } }, "node_modules/@radix-ui/react-primitive": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.2.tgz", - "integrity": "sha512-uHa+l/lKfxuDD2zjN/0peM/RhhSmRjr5YWdk/37EnSv1nJ88uvG85DPexSm8HdFQROd2VdERJ6ynXbkCFi+APw==", - "license": "MIT", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", "dependencies": { - "@radix-ui/react-slot": "1.2.2" + "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -2485,13 +4173,12 @@ } }, "node_modules/@radix-ui/react-progress": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.6.tgz", - "integrity": "sha512-QzN9a36nKk2eZKMf9EBCia35x3TT+SOgZuzQBVIHyRrmYYi73VYBRK3zKwdJ6az/F5IZ6QlacGJBg7zfB85liA==", - "license": "MIT", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.7.tgz", + "integrity": "sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg==", "dependencies": { "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.2" + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", @@ -2509,18 +4196,17 @@ } }, "node_modules/@radix-ui/react-radio-group": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.3.6.tgz", - "integrity": "sha512-1tfTAqnYZNVwSpFhCT273nzK8qGBReeYnNTPspCggqk1fvIrfVxJekIuBFidNivzpdiMqDwVGnQvHqXrRPM4Og==", - "license": "MIT", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.3.7.tgz", + "integrity": "sha512-9w5XhD0KPOrm92OTTE0SysH3sYzHsSTHNvZgUBo/VZ80VdYyB5RneDbc0dKpURS24IxkoFRu/hI0i4XyfFwY6g==", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-presence": "1.1.4", - "@radix-ui/react-primitive": "2.1.2", - "@radix-ui/react-roving-focus": "1.1.9", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.10", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" @@ -2541,18 +4227,17 @@ } }, "node_modules/@radix-ui/react-roving-focus": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.9.tgz", - "integrity": "sha512-ZzrIFnMYHHCNqSNCsuN6l7wlewBEq0O0BCSBkabJMFXVO51LRUTq71gLP1UxFvmrXElqmPjA5VX7IqC9VpazAQ==", - "license": "MIT", + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.10.tgz", + "integrity": "sha512-dT9aOXUen9JSsxnMPv/0VqySQf5eDQ6LCk5Sw28kamz8wSOW2bJdlX2Bg5VUIIcV+6XlHpWTIuTPCf/UNIyq8Q==", "dependencies": { "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-collection": "1.1.6", + "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, @@ -2572,10 +4257,9 @@ } }, "node_modules/@radix-ui/react-scroll-area": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.8.tgz", - "integrity": "sha512-K5h1RkYA6M0Sn61BV5LQs686zqBsSC0sGzL4/Gw4mNnjzrQcGSc6YXfC6CRFNaGydSdv5+M8cb0eNsOGo0OXtQ==", - "license": "MIT", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.9.tgz", + "integrity": "sha512-YSjEfBXnhUELsO2VzjdtYYD4CfQjvao+lhhrX5XsHD7/cyUNzljF1FHEbgTPN7LH2MClfwRMIsYlqTYpKTTe2A==", "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.2", @@ -2583,7 +4267,7 @@ "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-presence": "1.1.4", - "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1" }, @@ -2603,30 +4287,30 @@ } }, "node_modules/@radix-ui/react-select": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.4.tgz", - "integrity": "sha512-/OOm58Gil4Ev5zT8LyVzqfBcij4dTHYdeyuF5lMHZ2bIp0Lk9oETocYiJ5QC0dHekEQnK6L/FNJCceeb4AkZ6Q==", + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.5.tgz", + "integrity": "sha512-HnMTdXEVuuyzx63ME0ut4+sEMYW6oouHWNGUZc7ddvUWIcfCva/AMoqEW/3wnEllriMWBa0RHspCYnfCWJQYmA==", "license": "MIT", "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-collection": "1.1.6", + "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.9", + "@radix-ui/react-dismissable-layer": "1.1.10", "@radix-ui/react-focus-guards": "1.1.2", - "@radix-ui/react-focus-scope": "1.1.6", + "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.6", - "@radix-ui/react-portal": "1.1.8", - "@radix-ui/react-primitive": "2.1.2", - "@radix-ui/react-slot": "1.2.2", + "@radix-ui/react-popper": "1.2.7", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", - "@radix-ui/react-visually-hidden": "1.2.2", + "@radix-ui/react-visually-hidden": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, @@ -2646,12 +4330,11 @@ } }, "node_modules/@radix-ui/react-separator": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.6.tgz", - "integrity": "sha512-Izof3lPpbCfTM7WDta+LRkz31jem890VjEvpVRoWQNKpDUMMVffuyq854XPGP1KYGWWmjmYvHvPFeocWhFCy1w==", - "license": "MIT", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.7.tgz", + "integrity": "sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==", "dependencies": { - "@radix-ui/react-primitive": "2.1.2" + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", @@ -2669,18 +4352,17 @@ } }, "node_modules/@radix-ui/react-slider": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.3.4.tgz", - "integrity": "sha512-Cp6hEmQtRJFci285vkdIJ+HCDLTRDk+25VhFwa1fcubywjMUE3PynBgtN5RLudOgSCYMlT4jizCXdmV+8J7Y2w==", - "license": "MIT", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.3.5.tgz", + "integrity": "sha512-rkfe2pU2NBAYfGaxa3Mqosi7VZEWX5CxKaanRv0vZd4Zhl9fvQrg0VM93dv3xGLGfrHuoTRF3JXH8nb9g+B3fw==", "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-collection": "1.1.6", + "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", @@ -2702,9 +4384,9 @@ } }, "node_modules/@radix-ui/react-slot": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.2.tgz", - "integrity": "sha512-y7TBO4xN4Y94FvcWIOIh18fM4R1A8S4q1jhoz4PNzOoHsFcN8pogcFmZrTYAm4F9VRUrWP/Mw7xSKybIeRI+CQ==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" @@ -2720,15 +4402,14 @@ } }, "node_modules/@radix-ui/react-switch": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.4.tgz", - "integrity": "sha512-yZCky6XZFnR7pcGonJkr9VyNRu46KcYAbyg1v/gVVCZUr8UJ4x+RpncC27hHtiZ15jC+3WS8Yg/JSgyIHnYYsQ==", - "license": "MIT", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.5.tgz", + "integrity": "sha512-5ijLkak6ZMylXsaImpZ8u4Rlf5grRmoc0p0QeX9VJtlrM4f5m3nCTX8tWga/zOA8PZYIR/t0p2Mnvd7InrJ6yQ==", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" @@ -2749,18 +4430,17 @@ } }, "node_modules/@radix-ui/react-tabs": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.11.tgz", - "integrity": "sha512-4FiKSVoXqPP/KfzlB7lwwqoFV6EPwkrrqGp9cUYXjwDYHhvpnqq79P+EPHKcdoTE7Rl8w/+6s9rTlsfXHES9GA==", - "license": "MIT", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.12.tgz", + "integrity": "sha512-GTVAlRVrQrSw3cEARM0nAx73ixrWDPNZAruETn3oHCNP6SbZ/hNxdxp+u7VkIEv3/sFoLq1PfcHrl7Pnp0CDpw==", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.4", - "@radix-ui/react-primitive": "2.1.2", - "@radix-ui/react-roving-focus": "1.1.9", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.10", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { @@ -2779,23 +4459,22 @@ } }, "node_modules/@radix-ui/react-toast": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.13.tgz", - "integrity": "sha512-e/e43mQAwgYs8BY4y9l99xTK6ig1bK2uXsFLOMn9IZ16lAgulSTsotcPHVT2ZlSb/ye6Sllq7IgyDB8dGhpeXQ==", - "license": "MIT", + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.14.tgz", + "integrity": "sha512-nAP5FBxBJGQ/YfUB+r+O6USFVkWq3gAInkxyEnmvEV5jtSbfDhfa4hwX8CraCnbjMLsE7XSf/K75l9xXY7joWg==", "dependencies": { "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-collection": "1.1.6", + "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.9", - "@radix-ui/react-portal": "1.1.8", + "@radix-ui/react-dismissable-layer": "1.1.10", + "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.4", - "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", - "@radix-ui/react-visually-hidden": "1.2.2" + "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -2813,13 +4492,12 @@ } }, "node_modules/@radix-ui/react-toggle": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.8.tgz", - "integrity": "sha512-hrpa59m3zDnsa35LrTOH5s/a3iGv/VD+KKQjjiCTo/W4r0XwPpiWQvAv6Xl1nupSoaZeNNxW6sJH9ZydsjKdYQ==", - "license": "MIT", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.9.tgz", + "integrity": "sha512-ZoFkBBz9zv9GWer7wIjvdRxmh2wyc2oKWw6C6CseWd6/yq1DK/l5lJ+wnsmFwJZbBYqr02mrf8A2q/CVCuM3ZA==", "dependencies": { "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-primitive": "2.1.2", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { @@ -2838,17 +4516,16 @@ } }, "node_modules/@radix-ui/react-toggle-group": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.1.9.tgz", - "integrity": "sha512-HJ6gXdYVN38q/5KDdCcd+JTuXUyFZBMJbwXaU/82/Gi+V2ps6KpiZ2sQecAeZCV80POGRfkUBdUIj6hIdF6/MQ==", - "license": "MIT", + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.1.10.tgz", + "integrity": "sha512-kiU694Km3WFLTC75DdqgM/3Jauf3rD9wxeS9XtyWFKsBUeZA337lC+6uUazT7I1DhanZ5gyD5Stf8uf2dbQxOQ==", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-primitive": "2.1.2", - "@radix-ui/react-roving-focus": "1.1.9", - "@radix-ui/react-toggle": "1.1.8", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.10", + "@radix-ui/react-toggle": "1.1.9", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { @@ -2867,18 +4544,17 @@ } }, "node_modules/@radix-ui/react-toolbar": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toolbar/-/react-toolbar-1.1.9.tgz", - "integrity": "sha512-qqGkE9h018CSbpO4ag4rR6ZuOc/A9wM3dUv2jHrkfwUqspuvZmPegBPElVimH0FPWrYn4Alt4QTOptRjbwJnKw==", - "license": "MIT", + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toolbar/-/react-toolbar-1.1.10.tgz", + "integrity": "sha512-jiwQsduEL++M4YBIurjSa+voD86OIytCod0/dbIxFZDLD8NfO1//keXYMfsW8BPcfqwoNjt+y06XcJqAb4KR7A==", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-primitive": "2.1.2", - "@radix-ui/react-roving-focus": "1.1.9", - "@radix-ui/react-separator": "1.1.6", - "@radix-ui/react-toggle-group": "1.1.9" + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.10", + "@radix-ui/react-separator": "1.1.7", + "@radix-ui/react-toggle-group": "1.1.10" }, "peerDependencies": { "@types/react": "*", @@ -2896,23 +4572,22 @@ } }, "node_modules/@radix-ui/react-tooltip": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.6.tgz", - "integrity": "sha512-zYb+9dc9tkoN2JjBDIIPLQtk3gGyz8FMKoqYTb8EMVQ5a5hBcdHPECrsZVI4NpPAUOixhkoqg7Hj5ry5USowfA==", - "license": "MIT", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.7.tgz", + "integrity": "sha512-Ap+fNYwKTYJ9pzqW+Xe2HtMRbQ/EeWkj2qykZ6SuEV4iS/o1bZI5ssJbk4D2r8XuDuOBVz/tIx2JObtuqU+5Zw==", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.9", + "@radix-ui/react-dismissable-layer": "1.1.10", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.6", - "@radix-ui/react-portal": "1.1.8", + "@radix-ui/react-popper": "1.2.7", + "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.4", - "@radix-ui/react-primitive": "2.1.2", - "@radix-ui/react-slot": "1.2.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-visually-hidden": "1.2.2" + "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -2933,7 +4608,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", - "license": "MIT", "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -2948,7 +4622,6 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", - "license": "MIT", "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" @@ -2967,7 +4640,6 @@ "version": "0.0.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", - "license": "MIT", "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, @@ -2985,7 +4657,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", - "license": "MIT", "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, @@ -3003,7 +4674,6 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.0.tgz", "integrity": "sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==", - "license": "MIT", "dependencies": { "use-sync-external-store": "^1.5.0" }, @@ -3021,7 +4691,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", - "license": "MIT", "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -3036,7 +4705,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", - "license": "MIT", "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -3051,7 +4719,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", - "license": "MIT", "dependencies": { "@radix-ui/rect": "1.1.1" }, @@ -3069,7 +4736,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", - "license": "MIT", "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, @@ -3084,12 +4750,11 @@ } }, "node_modules/@radix-ui/react-visually-hidden": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.2.tgz", - "integrity": "sha512-ORCmRUbNiZIv6uV5mhFrhsIKw4UX/N3syZtyqvry61tbGm4JlgQuSn0hk5TwCARsCjkcnuRkSdCE3xfb+ADHew==", - "license": "MIT", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz", + "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==", "dependencies": { - "@radix-ui/react-primitive": "2.1.2" + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", @@ -3109,14 +4774,12 @@ "node_modules/@radix-ui/rect": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", - "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", - "license": "MIT" + "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==" }, "node_modules/@radix-ui/themes": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/@radix-ui/themes/-/themes-3.2.1.tgz", "integrity": "sha512-WJL2YKAGItkunwm3O4cLTFKCGJTfAfF6Hmq7f5bCo1ggqC9qJQ/wfg/25AAN72aoEM1yqXZQ+pslsw48AFR0Xg==", - "license": "MIT", "dependencies": { "@radix-ui/colors": "^3.0.0", "classnames": "^2.3.2", @@ -3138,65 +4801,315 @@ } } }, - "node_modules/@rtsao/scc": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", - "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true + }, + "node_modules/@rushstack/eslint-patch": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.11.0.tgz", + "integrity": "sha512-zxnHvoMQVqewTJr/W4pKjF0bMGiKJv1WX7bSrkl46Hg0QjESbzBROWK0Wg4RphzSOS5Jiy7eFimmM3UgMrMZbQ==", + "dev": true + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@svgr/babel-plugin-add-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", + "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", + "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-dynamic-title": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", + "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-svg-em-dimensions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", + "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-react-native-svg": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", + "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-svg-component": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", + "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-preset": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", + "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", + "dev": true, + "dependencies": { + "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", + "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", + "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", + "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", + "@svgr/babel-plugin-transform-svg-component": "8.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/core": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", + "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^8.1.3", + "snake-case": "^3.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/hast-util-to-babel-ast": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", + "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", + "dev": true, + "dependencies": { + "@babel/types": "^7.21.3", + "entities": "^4.4.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, + "node_modules/@svgr/plugin-jsx": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", + "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", "dev": true, - "license": "MIT" + "dependencies": { + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "@svgr/hast-util-to-babel-ast": "8.0.0", + "svg-parser": "^2.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@svgr/core": "*" + } }, - "node_modules/@rushstack/eslint-patch": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.11.0.tgz", - "integrity": "sha512-zxnHvoMQVqewTJr/W4pKjF0bMGiKJv1WX7bSrkl46Hg0QjESbzBROWK0Wg4RphzSOS5Jiy7eFimmM3UgMrMZbQ==", + "node_modules/@svgr/plugin-svgo": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz", + "integrity": "sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==", "dev": true, - "license": "MIT" + "dependencies": { + "cosmiconfig": "^8.1.3", + "deepmerge": "^4.3.1", + "svgo": "^3.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@svgr/core": "*" + } }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "node_modules/@svgr/webpack": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-8.1.0.tgz", + "integrity": "sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==", "dev": true, - "license": "MIT" + "dependencies": { + "@babel/core": "^7.21.3", + "@babel/plugin-transform-react-constant-elements": "^7.21.3", + "@babel/preset-env": "^7.20.2", + "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.21.0", + "@svgr/core": "8.1.0", + "@svgr/plugin-jsx": "8.1.0", + "@svgr/plugin-svgo": "8.1.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } }, "node_modules/@swc/counter": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", - "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", - "license": "Apache-2.0" + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" }, "node_modules/@swc/helpers": { "version": "0.5.15", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", - "license": "Apache-2.0", "dependencies": { "tslib": "^2.8.0" } }, "node_modules/@tailwindcss/node": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.6.tgz", - "integrity": "sha512-ed6zQbgmKsjsVvodAS1q1Ld2BolEuxJOSyyNc+vhkjdmfNUDCmQnlXBfQkHrlzNmslxHsQU/bFmzcEbv4xXsLg==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.10.tgz", + "integrity": "sha512-2ACf1znY5fpRBwRhMgj9ZXvb2XZW8qs+oTfotJ2C5xR0/WNL7UHZ7zXl6s+rUqedL1mNi+0O+WQr5awGowS3PQ==", "dev": true, - "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.3.0", "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", - "lightningcss": "1.29.2", + "lightningcss": "1.30.1", "magic-string": "^0.30.17", "source-map-js": "^1.2.1", - "tailwindcss": "4.1.6" + "tailwindcss": "4.1.10" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.6.tgz", - "integrity": "sha512-0bpEBQiGx+227fW4G0fLQ8vuvyy5rsB1YIYNapTq3aRsJ9taF3f5cCaovDjN5pUGKKzcpMrZst/mhNaKAPOHOA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.10.tgz", + "integrity": "sha512-v0C43s7Pjw+B9w21htrQwuFObSkio2aV/qPx/mhrRldbqxbWJK6KizM+q7BF1/1CmuLqZqX3CeYF7s7P9fbA8Q==", "dev": true, "hasInstallScript": true, - "license": "MIT", "dependencies": { "detect-libc": "^2.0.4", "tar": "^7.4.3" @@ -3205,29 +5118,28 @@ "node": ">= 10" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.6", - "@tailwindcss/oxide-darwin-arm64": "4.1.6", - "@tailwindcss/oxide-darwin-x64": "4.1.6", - "@tailwindcss/oxide-freebsd-x64": "4.1.6", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.6", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.6", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.6", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.6", - "@tailwindcss/oxide-linux-x64-musl": "4.1.6", - "@tailwindcss/oxide-wasm32-wasi": "4.1.6", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.6", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.6" + "@tailwindcss/oxide-android-arm64": "4.1.10", + "@tailwindcss/oxide-darwin-arm64": "4.1.10", + "@tailwindcss/oxide-darwin-x64": "4.1.10", + "@tailwindcss/oxide-freebsd-x64": "4.1.10", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.10", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.10", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.10", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.10", + "@tailwindcss/oxide-linux-x64-musl": "4.1.10", + "@tailwindcss/oxide-wasm32-wasi": "4.1.10", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.10", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.10" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.6.tgz", - "integrity": "sha512-VHwwPiwXtdIvOvqT/0/FLH/pizTVu78FOnI9jQo64kSAikFSZT7K4pjyzoDpSMaveJTGyAKvDjuhxJxKfmvjiQ==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.10.tgz", + "integrity": "sha512-VGLazCoRQ7rtsCzThaI1UyDu/XRYVyH4/EWiaSX6tFglE+xZB5cvtC5Omt0OQ+FfiIVP98su16jDVHDEIuH4iQ==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" @@ -3237,14 +5149,13 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.6.tgz", - "integrity": "sha512-weINOCcqv1HVBIGptNrk7c6lWgSFFiQMcCpKM4tnVi5x8OY2v1FrV76jwLukfT6pL1hyajc06tyVmZFYXoxvhQ==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.10.tgz", + "integrity": "sha512-ZIFqvR1irX2yNjWJzKCqTCcHZbgkSkSkZKbRM3BPzhDL/18idA8uWCoopYA2CSDdSGFlDAxYdU2yBHwAwx8euQ==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -3254,14 +5165,13 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.6.tgz", - "integrity": "sha512-3FzekhHG0ww1zQjQ1lPoq0wPrAIVXAbUkWdWM8u5BnYFZgb9ja5ejBqyTgjpo5mfy0hFOoMnMuVDI+7CXhXZaQ==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.10.tgz", + "integrity": "sha512-eCA4zbIhWUFDXoamNztmS0MjXHSEJYlvATzWnRiTqJkcUteSjO94PoRHJy1Xbwp9bptjeIxxBHh+zBWFhttbrQ==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -3271,14 +5181,13 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.6.tgz", - "integrity": "sha512-4m5F5lpkBZhVQJq53oe5XgJ+aFYWdrgkMwViHjRsES3KEu2m1udR21B1I77RUqie0ZYNscFzY1v9aDssMBZ/1w==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.10.tgz", + "integrity": "sha512-8/392Xu12R0cc93DpiJvNpJ4wYVSiciUlkiOHOSOQNH3adq9Gi/dtySK7dVQjXIOzlpSHjeCL89RUUI8/GTI6g==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "freebsd" @@ -3288,14 +5197,13 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.6.tgz", - "integrity": "sha512-qU0rHnA9P/ZoaDKouU1oGPxPWzDKtIfX7eOGi5jOWJKdxieUJdVV+CxWZOpDWlYTd4N3sFQvcnVLJWJ1cLP5TA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.10.tgz", + "integrity": "sha512-t9rhmLT6EqeuPT+MXhWhlRYIMSfh5LZ6kBrC4FS6/+M1yXwfCtp24UumgCWOAJVyjQwG+lYva6wWZxrfvB+NhQ==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -3305,14 +5213,13 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.6.tgz", - "integrity": "sha512-jXy3TSTrbfgyd3UxPQeXC3wm8DAgmigzar99Km9Sf6L2OFfn/k+u3VqmpgHQw5QNfCpPe43em6Q7V76Wx7ogIQ==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.10.tgz", + "integrity": "sha512-3oWrlNlxLRxXejQ8zImzrVLuZ/9Z2SeKoLhtCu0hpo38hTO2iL86eFOu4sVR8cZc6n3z7eRXXqtHJECa6mFOvA==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -3322,14 +5229,13 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.6.tgz", - "integrity": "sha512-8kjivE5xW0qAQ9HX9reVFmZj3t+VmljDLVRJpVBEoTR+3bKMnvC7iLcoSGNIUJGOZy1mLVq7x/gerVg0T+IsYw==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.10.tgz", + "integrity": "sha512-saScU0cmWvg/Ez4gUmQWr9pvY9Kssxt+Xenfx1LG7LmqjcrvBnw4r9VjkFcqmbBb7GCBwYNcZi9X3/oMda9sqQ==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -3339,14 +5245,13 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.6.tgz", - "integrity": "sha512-A4spQhwnWVpjWDLXnOW9PSinO2PTKJQNRmL/aIl2U/O+RARls8doDfs6R41+DAXK0ccacvRyDpR46aVQJJCoCg==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.10.tgz", + "integrity": "sha512-/G3ao/ybV9YEEgAXeEg28dyH6gs1QG8tvdN9c2MNZdUXYBaIY/Gx0N6RlJzfLy/7Nkdok4kaxKPHKJUlAaoTdA==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -3356,14 +5261,13 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.6.tgz", - "integrity": "sha512-YRee+6ZqdzgiQAHVSLfl3RYmqeeaWVCk796MhXhLQu2kJu2COHBkqlqsqKYx3p8Hmk5pGCQd2jTAoMWWFeyG2A==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.10.tgz", + "integrity": "sha512-LNr7X8fTiKGRtQGOerSayc2pWJp/9ptRYAa4G+U+cjw9kJZvkopav1AQc5HHD+U364f71tZv6XamaHKgrIoVzA==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -3373,9 +5277,9 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.6.tgz", - "integrity": "sha512-qAp4ooTYrBQ5pk5jgg54/U1rCJ/9FLYOkkQ/nTE+bVMseMfB6O7J8zb19YTpWuu4UdfRf5zzOrNKfl6T64MNrQ==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.10.tgz", + "integrity": "sha512-d6ekQpopFQJAcIK2i7ZzWOYGZ+A6NzzvQ3ozBvWFdeyqfOZdYHU66g5yr+/HC4ipP1ZgWsqa80+ISNILk+ae/Q==", "bundleDependencies": [ "@napi-rs/wasm-runtime", "@emnapi/core", @@ -3388,13 +5292,12 @@ "wasm32" ], "dev": true, - "license": "MIT", "optional": true, "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@emnapi/wasi-threads": "^1.0.2", - "@napi-rs/wasm-runtime": "^0.2.9", + "@napi-rs/wasm-runtime": "^0.2.10", "@tybys/wasm-util": "^0.9.0", "tslib": "^2.8.0" }, @@ -3403,14 +5306,13 @@ } }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.6.tgz", - "integrity": "sha512-nqpDWk0Xr8ELO/nfRUDjk1pc9wDJ3ObeDdNMHLaymc4PJBWj11gdPCWZFKSK2AVKjJQC7J2EfmSmf47GN7OuLg==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.10.tgz", + "integrity": "sha512-i1Iwg9gRbwNVOCYmnigWCCgow8nDWSFmeTUU5nbNx3rqbe4p0kRbEqLwLJbYZKmSSp23g4N6rCDmm7OuPBXhDA==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -3420,14 +5322,13 @@ } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.6.tgz", - "integrity": "sha512-5k9xF33xkfKpo9wCvYcegQ21VwIBU1/qEbYlVukfEIyQbEA47uK8AAwS7NVjNE3vHzcmxMYwd0l6L4pPjjm1rQ==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.10.tgz", + "integrity": "sha512-sGiJTjcBSfGq2DVRtaSljq5ZgZS2SDHSIfhOylkBvHVjwOsodBhnb3HdmiKkVuUGKD0I7G63abMOVaskj1KpOA==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -3437,25 +5338,23 @@ } }, "node_modules/@tailwindcss/postcss": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.6.tgz", - "integrity": "sha512-ELq+gDMBuRXPJlpE3PEen+1MhnHAQQrh2zF0dI1NXOlEWfr2qWf2CQdr5jl9yANv8RErQaQ2l6nIFO9OSCVq/g==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.10.tgz", + "integrity": "sha512-B+7r7ABZbkXJwpvt2VMnS6ujcDoR2OOcFaqrLIo1xbcdxje4Vf+VgJdBzNNbrAjBj/rLZ66/tlQ1knIGNLKOBQ==", "dev": true, - "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", - "@tailwindcss/node": "4.1.6", - "@tailwindcss/oxide": "4.1.6", + "@tailwindcss/node": "4.1.10", + "@tailwindcss/oxide": "4.1.10", "postcss": "^8.4.41", - "tailwindcss": "4.1.6" + "tailwindcss": "4.1.10" } }, "node_modules/@tanstack/eslint-plugin-query": { - "version": "5.74.7", - "resolved": "https://registry.npmjs.org/@tanstack/eslint-plugin-query/-/eslint-plugin-query-5.74.7.tgz", - "integrity": "sha512-EeHuaaYiCOD+XOGyB7LMNEx9OEByAa5lkgP+S3ZggjKJpmIO6iRWeoIYYDKo2F8uc3qXcVhTfC7pn7NddQiNtA==", + "version": "5.78.0", + "resolved": "https://registry.npmjs.org/@tanstack/eslint-plugin-query/-/eslint-plugin-query-5.78.0.tgz", + "integrity": "sha512-hYkhWr3UP0CkAsn/phBVR98UQawbw8CmTSgWtdgEBUjI60/GBaEIkpgi/Bp/2I8eIDK4+vdY7ac6jZx+GR+hEQ==", "dev": true, - "license": "MIT", "dependencies": { "@typescript-eslint/utils": "^8.18.1" }, @@ -3468,11 +5367,10 @@ } }, "node_modules/@tanstack/query-core": { - "version": "5.75.7", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.75.7.tgz", - "integrity": "sha512-4BHu0qnxUHOSnTn3ow9fIoBKTelh0GY08yn1IO9cxjBTsGvnxz1ut42CHZqUE3Vl/8FAjcHsj8RNJMoXvjgHEA==", + "version": "5.80.7", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.80.7.tgz", + "integrity": "sha512-s09l5zeUKC8q7DCCCIkVSns8zZrK4ZDT6ryEjxNBFi68G4z2EBobBS7rdOY3r6W1WbUDpc1fe5oY+YO/+2UVUg==", "dev": true, - "license": "MIT", "peer": true, "funding": { "type": "github", @@ -3480,25 +5378,23 @@ } }, "node_modules/@tanstack/query-devtools": { - "version": "5.74.7", - "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.74.7.tgz", - "integrity": "sha512-nSNlfuGdnHf4yB0S+BoNYOE1o3oAH093weAYZolIHfS2stulyA/gWfSk/9H4ZFk5mAAHb5vNqAeJOmbdcGPEQw==", + "version": "5.80.0", + "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.80.0.tgz", + "integrity": "sha512-D6gH4asyjaoXrCOt5vG5Og/YSj0D/TxwNQgtLJIgWbhbWCC/emu2E92EFoVHh4ppVWg1qT2gKHvKyQBEFZhCuA==", "dev": true, - "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" } }, "node_modules/@tanstack/react-query": { - "version": "5.75.7", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.75.7.tgz", - "integrity": "sha512-JYcH1g5pNjKXNQcvvnCU/PueaYg05uKBDHlWIyApspv7r5C0BM12n6ysa2QF2T+1tlPnNXOob8vr8o96Nx0GxQ==", + "version": "5.80.7", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.80.7.tgz", + "integrity": "sha512-u2F0VK6+anItoEvB3+rfvTO9GEh2vb00Je05OwlUe/A0lkJBgW1HckiY3f9YZa+jx6IOe4dHPh10dyp9aY3iRQ==", "dev": true, - "license": "MIT", "peer": true, "dependencies": { - "@tanstack/query-core": "5.75.7" + "@tanstack/query-core": "5.80.7" }, "funding": { "type": "github", @@ -3509,29 +5405,63 @@ } }, "node_modules/@tanstack/react-query-devtools": { - "version": "5.75.7", - "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.75.7.tgz", - "integrity": "sha512-VUzHvxcUAz7oSeX/TlVyDgNxajLAF+b12Z3OfSxCrAdWynELfWohwzCn1iT2NEjnGTb3X3ryzQxeWuWMyMwCmQ==", + "version": "5.80.7", + "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.80.7.tgz", + "integrity": "sha512-7Dz/19fVo0i+jgLVBabV5vfGOlLyN5L1w8w1/ogFhe6ItNNsNA+ZgNTbtiKpbR3CcX2WDRRTInz1uMSmHzTsoQ==", "dev": true, - "license": "MIT", "dependencies": { - "@tanstack/query-devtools": "5.74.7" + "@tanstack/query-devtools": "5.80.0" }, "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" }, "peerDependencies": { - "@tanstack/react-query": "^5.75.7", + "@tanstack/react-query": "^5.80.7", "react": "^18 || ^19" } }, + "node_modules/@tokenizer/inflate": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz", + "integrity": "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==", + "dependencies": { + "debug": "^4.4.0", + "fflate": "^0.8.2", + "token-types": "^6.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" + }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==" + }, + "node_modules/@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/@tybys/wasm-util": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "tslib": "^2.4.0" @@ -3540,14 +5470,12 @@ "node_modules/@types/d3-color": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", - "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", - "license": "MIT" + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==" }, "node_modules/@types/d3-drag": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", - "license": "MIT", "dependencies": { "@types/d3-selection": "*" } @@ -3556,7 +5484,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", - "license": "MIT", "dependencies": { "@types/d3-color": "*" } @@ -3564,14 +5491,12 @@ "node_modules/@types/d3-selection": { "version": "3.0.11", "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", - "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==", - "license": "MIT" + "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==" }, "node_modules/@types/d3-transition": { "version": "3.0.9", "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", - "license": "MIT", "dependencies": { "@types/d3-selection": "*" } @@ -3580,77 +5505,87 @@ "version": "3.0.8", "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", - "license": "MIT", "dependencies": { "@types/d3-interpolate": "*", "@types/d3-selection": "*" } }, "node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", - "dev": true, - "license": "MIT" + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.9.tgz", + "integrity": "sha512-uoe+GxEuHbvy12OUQct2X9JenKM3qAscquYymuQN4fMWG9DBQtykrQEFcAbVACF7qaLw9BePSodUL0kquqBJpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", "dev": true, "license": "MIT" }, "node_modules/@types/node": { - "version": "20.17.46", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.46.tgz", - "integrity": "sha512-0PQHLhZPWOxGW4auogW0eOQAuNIlCYvibIpG67ja0TOJ6/sehu+1en7sfceUn+QQtx4Rk3GxbLNwPh0Cav7TWw==", + "version": "24.0.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.1.tgz", + "integrity": "sha512-MX4Zioh39chHlDJbKmEgydJDS3tspMP/lnQC67G3SWsTnb9NeYVWOjkxpOSy4oMfPs4StcWHwBrvUb4ybfnuaw==", "dev": true, - "license": "MIT", "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~7.8.0" } }, "node_modules/@types/react": { - "version": "19.1.3", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.3.tgz", - "integrity": "sha512-dLWQ+Z0CkIvK1J8+wrDPwGxEYFA4RAyHoZPxHVGspYmFVnwGSNT24cGIhFJrtfRnWVuW8X7NO52gCXmhkVUWGQ==", + "version": "19.1.8", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz", + "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==", "devOptional": true, - "license": "MIT", "dependencies": { "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { - "version": "19.1.3", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.3.tgz", - "integrity": "sha512-rJXC08OG0h3W6wDMFxQrZF00Kq6qQvw0djHRdzl3U5DnIERz0MRce3WVc7IS6JYBwtaP/DwYtRRjVlvivNveKg==", + "version": "19.1.6", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.6.tgz", + "integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==", "devOptional": true, - "license": "MIT", "peerDependencies": { "@types/react": "^19.0.0" } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.32.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.0.tgz", - "integrity": "sha512-/jU9ettcntkBFmWUzzGgsClEi2ZFiikMX5eEQsmxIAWMOn4H3D4rvHssstmAHGVvrYnaMqdWWWg0b5M6IN/MTQ==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.34.0.tgz", + "integrity": "sha512-QXwAlHlbcAwNlEEMKQS2RCgJsgXrTJdjXT08xEgbPFa2yYQgVjBymxP5DrfrE7X7iodSzd9qBUHUycdyVJTW1w==", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.32.0", - "@typescript-eslint/type-utils": "8.32.0", - "@typescript-eslint/utils": "8.32.0", - "@typescript-eslint/visitor-keys": "8.32.0", + "@typescript-eslint/scope-manager": "8.34.0", + "@typescript-eslint/type-utils": "8.34.0", + "@typescript-eslint/utils": "8.34.0", + "@typescript-eslint/visitor-keys": "8.34.0", "graphemer": "^1.4.0", - "ignore": "^5.3.1", + "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, @@ -3662,22 +5597,30 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "@typescript-eslint/parser": "^8.34.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, "node_modules/@typescript-eslint/parser": { - "version": "8.32.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.32.0.tgz", - "integrity": "sha512-B2MdzyWxCE2+SqiZHAjPphft+/2x2FlO9YBx7eKE1BCb+rqBlQdhtAEhzIEdozHd55DXPmxBdpMygFJjfjjA9A==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.34.0.tgz", + "integrity": "sha512-vxXJV1hVFx3IXz/oy2sICsJukaBrtDEQSBiV48/YIV5KWjX1dO+bcIr/kCPrW6weKXvsaGKFNlwH0v2eYdRRbA==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.32.0", - "@typescript-eslint/types": "8.32.0", - "@typescript-eslint/typescript-estree": "8.32.0", - "@typescript-eslint/visitor-keys": "8.32.0", + "@typescript-eslint/scope-manager": "8.34.0", + "@typescript-eslint/types": "8.34.0", + "@typescript-eslint/typescript-estree": "8.34.0", + "@typescript-eslint/visitor-keys": "8.34.0", "debug": "^4.3.4" }, "engines": { @@ -3692,33 +5635,68 @@ "typescript": ">=4.8.4 <5.9.0" } }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.34.0.tgz", + "integrity": "sha512-iEgDALRf970/B2YExmtPMPF54NenZUf4xpL3wsCRx/lgjz6ul/l13R81ozP/ZNuXfnLCS+oPmG7JIxfdNYKELw==", + "dev": true, + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.34.0", + "@typescript-eslint/types": "^8.34.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.32.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.32.0.tgz", - "integrity": "sha512-jc/4IxGNedXkmG4mx4nJTILb6TMjL66D41vyeaPWvDUmeYQzF3lKtN15WsAeTr65ce4mPxwopPSo1yUUAWw0hQ==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.34.0.tgz", + "integrity": "sha512-9Ac0X8WiLykl0aj1oYQNcLZjHgBojT6cW68yAgZ19letYu+Hxd0rE0veI1XznSSst1X5lwnxhPbVdwjDRIomRw==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.32.0", - "@typescript-eslint/visitor-keys": "8.32.0" + "@typescript-eslint/types": "8.34.0", + "@typescript-eslint/visitor-keys": "8.34.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.34.0.tgz", + "integrity": "sha512-+W9VYHKFIzA5cBeooqQxqNriAP0QeQ7xTiDuIOr71hzgffm3EL2hxwWBIIj4GuofIbKxGNarpKqIq6Q6YrShOA==", + "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.32.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.32.0.tgz", - "integrity": "sha512-t2vouuYQKEKSLtJaa5bB4jHeha2HJczQ6E5IXPDPgIty9EqcJxpr1QHQ86YyIPwDwxvUmLfP2YADQ5ZY4qddZg==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.34.0.tgz", + "integrity": "sha512-n7zSmOcUVhcRYC75W2pnPpbO1iwhJY3NLoHEtbJwJSNlVAZuwqu05zY3f3s2SDWWDSo9FdN5szqc73DCtDObAg==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.32.0", - "@typescript-eslint/utils": "8.32.0", + "@typescript-eslint/typescript-estree": "8.34.0", + "@typescript-eslint/utils": "8.34.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -3735,11 +5713,10 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.32.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.32.0.tgz", - "integrity": "sha512-O5Id6tGadAZEMThM6L9HmVf5hQUXNSxLVKeGJYWNhhVseps/0LddMkp7//VDkzwJ69lPL0UmZdcZwggj9akJaA==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.34.0.tgz", + "integrity": "sha512-9V24k/paICYPniajHfJ4cuAWETnt7Ssy+R0Rbcqo5sSFr3QEZ/8TSoUi9XeXVBGXCaLtwTOKSLGcInCAvyZeMA==", "dev": true, - "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -3749,14 +5726,15 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.32.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.0.tgz", - "integrity": "sha512-pU9VD7anSCOIoBFnhTGfOzlVFQIA1XXiQpH/CezqOBaDppRwTglJzCC6fUQGpfwey4T183NKhF1/mfatYmjRqQ==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.34.0.tgz", + "integrity": "sha512-rOi4KZxI7E0+BMqG7emPSK1bB4RICCpF7QD3KCLXn9ZvWoESsOMlHyZPAHyG04ujVplPaHbmEvs34m+wjgtVtg==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.32.0", - "@typescript-eslint/visitor-keys": "8.32.0", + "@typescript-eslint/project-service": "8.34.0", + "@typescript-eslint/tsconfig-utils": "8.34.0", + "@typescript-eslint/types": "8.34.0", + "@typescript-eslint/visitor-keys": "8.34.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -3776,51 +5754,19 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -3831,17 +5777,28 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@typescript-eslint/utils": { - "version": "8.32.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.32.0.tgz", - "integrity": "sha512-8S9hXau6nQ/sYVtC3D6ISIDoJzS1NsCK+gluVhLN2YkBPX+/1wkwyUiDKnxRh15579WoOIyVWnoyIf3yGI9REw==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.34.0.tgz", + "integrity": "sha512-8L4tWatGchV9A1cKbjaavS6mwYwp39jql8xUmIIKJdm+qiaeHy5KMKlBrf30akXAWBzn2SqKsNOtSENWUwg7XQ==", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.32.0", - "@typescript-eslint/types": "8.32.0", - "@typescript-eslint/typescript-estree": "8.32.0" + "@typescript-eslint/scope-manager": "8.34.0", + "@typescript-eslint/types": "8.34.0", + "@typescript-eslint/typescript-estree": "8.34.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3856,13 +5813,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.32.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.0.tgz", - "integrity": "sha512-1rYQTCLFFzOI5Nl0c8LUpJT8HxpwVRn9E4CkMsYfuN6ctmQqExjSTzzSk0Tz2apmXy7WU6/6fyaZVVA/thPN+w==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.34.0.tgz", + "integrity": "sha512-qHV7pW7E85A0x6qyrFn+O+q1k1p3tQCsqIZ1KZ5ESLXY57aTvUd3/a4rdPTeXisvhXn2VQG0VSKUqs8KHF2zcA==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.32.0", + "@typescript-eslint/types": "8.34.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -3873,261 +5829,280 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@ungap/structured-clone": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.9.0.tgz", + "integrity": "sha512-h1T2c2Di49ekF2TE8ZCoJkb+jwETKUIPDJ/nO3tJBKlLFPu+fyd93f0rGP/BvArKx2k2HlRM4kqkNarj3dvZlg==", + "cpu": [ + "arm" + ], "dev": true, - "license": "ISC" + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.9.0.tgz", + "integrity": "sha512-sG1NHtgXtX8owEkJ11yn34vt0Xqzi3k9TJ8zppDmyG8GZV4kVWw44FHwKwHeEFl07uKPeC4ZoyuQaGh5ruJYPA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] }, "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.7.2.tgz", - "integrity": "sha512-vxtBno4xvowwNmO/ASL0Y45TpHqmNkAaDtz4Jqb+clmcVSSl8XCG/PNFFkGsXXXS6AMjP+ja/TtNCFFa1QwLRg==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.9.0.tgz", + "integrity": "sha512-nJ9z47kfFnCxN1z/oYZS7HSNsFh43y2asePzTEZpEvK7kGyuShSl3RRXnm/1QaqFL+iP+BjMwuB+DYUymOkA5A==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.7.2.tgz", - "integrity": "sha512-qhVa8ozu92C23Hsmv0BF4+5Dyyd5STT1FolV4whNgbY6mj3kA0qsrGPe35zNR3wAN7eFict3s4Rc2dDTPBTuFQ==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.9.0.tgz", + "integrity": "sha512-TK+UA1TTa0qS53rjWn7cVlEKVGz2B6JYe0C++TdQjvWYIyx83ruwh0wd4LRxYBM5HeuAzXcylA9BH2trARXJTw==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.7.2.tgz", - "integrity": "sha512-zKKdm2uMXqLFX6Ac7K5ElnnG5VIXbDlFWzg4WJ8CGUedJryM5A3cTgHuGMw1+P5ziV8CRhnSEgOnurTI4vpHpg==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.9.0.tgz", + "integrity": "sha512-6uZwzMRFcD7CcCd0vz3Hp+9qIL2jseE/bx3ZjaLwn8t714nYGwiE84WpaMCYjU+IQET8Vu/+BNAGtYD7BG/0yA==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "freebsd" ] }, "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.7.2.tgz", - "integrity": "sha512-8N1z1TbPnHH+iDS/42GJ0bMPLiGK+cUqOhNbMKtWJ4oFGzqSJk/zoXFzcQkgtI63qMcUI7wW1tq2usZQSb2jxw==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.9.0.tgz", + "integrity": "sha512-bPUBksQfrgcfv2+mm+AZinaKq8LCFvt5PThYqRotqSuuZK1TVKkhbVMS/jvSRfYl7jr3AoZLYbDkItxgqMKRkg==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.7.2.tgz", - "integrity": "sha512-tjYzI9LcAXR9MYd9rO45m1s0B/6bJNuZ6jeOxo1pq1K6OBuRMMmfyvJYval3s9FPPGmrldYA3mi4gWDlWuTFGA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.9.0.tgz", + "integrity": "sha512-uT6E7UBIrTdCsFQ+y0tQd3g5oudmrS/hds5pbU3h4s2t/1vsGWbbSKhBSCD9mcqaqkBwoqlECpUrRJCmldl8PA==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.7.2.tgz", - "integrity": "sha512-jon9M7DKRLGZ9VYSkFMflvNqu9hDtOCEnO2QAryFWgT6o6AXU8du56V7YqnaLKr6rAbZBWYsYpikF226v423QA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.9.0.tgz", + "integrity": "sha512-vdqBh911wc5awE2bX2zx3eflbyv8U9xbE/jVKAm425eRoOVv/VseGZsqi3A3SykckSpF4wSROkbQPvbQFn8EsA==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.7.2.tgz", - "integrity": "sha512-c8Cg4/h+kQ63pL43wBNaVMmOjXI/X62wQmru51qjfTvI7kmCy5uHTJvK/9LrF0G8Jdx8r34d019P1DVJmhXQpA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.9.0.tgz", + "integrity": "sha512-/8JFZ/SnuDr1lLEVsxsuVwrsGquTvT51RZGvyDB/dOK3oYK2UqeXzgeyq6Otp8FZXQcEYqJwxb9v+gtdXn03eQ==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.7.2.tgz", - "integrity": "sha512-A+lcwRFyrjeJmv3JJvhz5NbcCkLQL6Mk16kHTNm6/aGNc4FwPHPE4DR9DwuCvCnVHvF5IAd9U4VIs/VvVir5lg==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.9.0.tgz", + "integrity": "sha512-FkJjybtrl+rajTw4loI3L6YqSOpeZfDls4SstL/5lsP2bka9TiHUjgMBjygeZEis1oC8LfJTS8FSgpKPaQx2tQ==", "cpu": [ "ppc64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.7.2.tgz", - "integrity": "sha512-hQQ4TJQrSQW8JlPm7tRpXN8OCNP9ez7PajJNjRD1ZTHQAy685OYqPrKjfaMw/8LiHCt8AZ74rfUVHP9vn0N69Q==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.9.0.tgz", + "integrity": "sha512-w/NZfHNeDusbqSZ8r/hp8iL4S39h4+vQMc9/vvzuIKMWKppyUGKm3IST0Qv0aOZ1rzIbl9SrDeIqK86ZpUK37w==", "cpu": [ "riscv64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.7.2.tgz", - "integrity": "sha512-NoAGbiqrxtY8kVooZ24i70CjLDlUFI7nDj3I9y54U94p+3kPxwd2L692YsdLa+cqQ0VoqMWoehDFp21PKRUoIQ==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.9.0.tgz", + "integrity": "sha512-bEPBosut8/8KQbUixPry8zg/fOzVOWyvwzOfz0C0Rw6dp+wIBseyiHKjkcSyZKv/98edrbMknBaMNJfA/UEdqw==", "cpu": [ "riscv64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.7.2.tgz", - "integrity": "sha512-KaZByo8xuQZbUhhreBTW+yUnOIHUsv04P8lKjQ5otiGoSJ17ISGYArc+4vKdLEpGaLbemGzr4ZeUbYQQsLWFjA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.9.0.tgz", + "integrity": "sha512-LDtMT7moE3gK753gG4pc31AAqGUC86j3AplaFusc717EUGF9ZFJ356sdQzzZzkBk1XzMdxFyZ4f/i35NKM/lFA==", "cpu": [ "s390x" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.7.2.tgz", - "integrity": "sha512-dEidzJDubxxhUCBJ/SHSMJD/9q7JkyfBMT77Px1npl4xpg9t0POLvnWywSk66BgZS/b2Hy9Y1yFaoMTFJUe9yg==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.9.0.tgz", + "integrity": "sha512-WmFd5KINHIXj8o1mPaT8QRjA9HgSXhN1gl9Da4IZihARihEnOylu4co7i/yeaIpcfsI6sYs33cNZKyHYDh0lrA==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.7.2.tgz", - "integrity": "sha512-RvP+Ux3wDjmnZDT4XWFfNBRVG0fMsc+yVzNFUqOflnDfZ9OYujv6nkh+GOr+watwrW4wdp6ASfG/e7bkDradsw==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.9.0.tgz", + "integrity": "sha512-CYuXbANW+WgzVRIl8/QvZmDaZxrqvOldOwlbUjIM4pQ46FJ0W5cinJ/Ghwa/Ng1ZPMJMk1VFdsD/XwmCGIXBWg==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.7.2.tgz", - "integrity": "sha512-y797JBmO9IsvXVRCKDXOxjyAE4+CcZpla2GSoBQ33TVb3ILXuFnMrbR/QQZoauBYeOFuu4w3ifWLw52sdHGz6g==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.9.0.tgz", + "integrity": "sha512-6Rp2WH0OoitMYR57Z6VE8Y6corX8C6QEMWLgOV6qXiJIeZ1F9WGXY/yQ8yDC4iTraotyLOeJ2Asea0urWj2fKQ==", "cpu": [ "wasm32" ], "dev": true, - "license": "MIT", "optional": true, "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.9" + "@napi-rs/wasm-runtime": "^0.2.11" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.7.2.tgz", - "integrity": "sha512-gtYTh4/VREVSLA+gHrfbWxaMO/00y+34htY7XpioBTy56YN2eBjkPrY1ML1Zys89X3RJDKVaogzwxlM1qU7egg==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.9.0.tgz", + "integrity": "sha512-rknkrTRuvujprrbPmGeHi8wYWxmNVlBoNW8+4XF2hXUnASOjmuC9FNF1tGbDiRQWn264q9U/oGtixyO3BT8adQ==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.7.2.tgz", - "integrity": "sha512-Ywv20XHvHTDRQs12jd3MY8X5C8KLjDbg/jyaal/QLKx3fAShhJyD4blEANInsjxW3P7isHx1Blt56iUDDJO3jg==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.9.0.tgz", + "integrity": "sha512-Ceymm+iBl+bgAICtgiHyMLz6hjxmLJKqBim8tDzpX61wpZOx2bPK6Gjuor7I2RiUynVjvvkoRIkrPyMwzBzF3A==", "cpu": [ "ia32" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@unrs/resolver-binding-win32-x64-msvc": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.7.2.tgz", - "integrity": "sha512-friS8NEQfHaDbkThxopGk+LuE5v3iY0StruifjQEt7SLbA46OnfgMO15sOTkbpJkol6RB+1l1TYPXh0sCddpvA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.9.0.tgz", + "integrity": "sha512-k59o9ZyeyS0hAlcaKFezYSH2agQeRFEB7KoQLXl3Nb3rgkqT1NY9Vwy+SqODiLmYnEjxWJVRE/yq2jFVqdIxZw==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@xyflow/react": { - "version": "12.6.1", - "resolved": "https://registry.npmjs.org/@xyflow/react/-/react-12.6.1.tgz", - "integrity": "sha512-DQs1LOaxSIdsoxsfZSLBoID93eQvfBXDraBwalpKaVcWTueWfjnW9mQ7jviwC3zPLwyx/ioPh+C45/Ez7+CHUQ==", - "license": "MIT", + "version": "12.7.0", + "resolved": "https://registry.npmjs.org/@xyflow/react/-/react-12.7.0.tgz", + "integrity": "sha512-U6VMEbYjiCg1byHrR7S+b5ZdHTjgCFX4KpBc634G/WtEBUvBLoMQdlCD6uJHqodnOAxpt3+G2wiDeTmXAFJzgQ==", "dependencies": { - "@xyflow/system": "0.0.58", + "@xyflow/system": "0.0.62", "classcat": "^5.0.3", "zustand": "^4.4.0" }, @@ -4137,40 +6112,26 @@ } }, "node_modules/@xyflow/system": { - "version": "0.0.58", - "resolved": "https://registry.npmjs.org/@xyflow/system/-/system-0.0.58.tgz", - "integrity": "sha512-f4l+/AAdWejcFrkaCbKWRWyL64G7gMR0xrwRlbG6oF4KIOMcygGFxOXdOV8QCMcQ9u++QIDpsogpUhexX4vi1Q==", - "license": "MIT", + "version": "0.0.62", + "resolved": "https://registry.npmjs.org/@xyflow/system/-/system-0.0.62.tgz", + "integrity": "sha512-Z2ufbnvuYxIOCGyzE/8eX8TAEM8Lpzc/JafjD1Tzy6ZJs/E7KGVU17Q1F5WDHVW+dbztJAdyXMG0ejR9bwSUAA==", "dependencies": { "@types/d3-drag": "^3.0.7", + "@types/d3-interpolate": "^3.0.4", "@types/d3-selection": "^3.0.10", "@types/d3-transition": "^3.0.8", "@types/d3-zoom": "^3.0.8", "d3-drag": "^3.0.0", + "d3-interpolate": "^3.0.1", "d3-selection": "^3.0.0", "d3-zoom": "^3.0.0" } }, - "node_modules/accepts": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", - "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-types": "^3.0.0", - "negotiator": "^1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/acorn": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", - "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, - "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -4183,17 +6144,23 @@ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, - "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "engines": { + "node": ">= 14" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -4201,26 +6168,36 @@ "uri-js": "^4.2.2" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true, - "license": "MIT", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -4235,14 +6212,12 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" + "dev": true }, "node_modules/aria-hidden": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", - "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", - "license": "MIT", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", "dependencies": { "tslib": "^2.0.0" }, @@ -4255,7 +6230,6 @@ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", "dev": true, - "license": "Apache-2.0", "engines": { "node": ">= 0.4" } @@ -4265,7 +6239,6 @@ "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "is-array-buffer": "^3.0.5" @@ -4278,18 +6251,19 @@ } }, "node_modules/array-includes": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", - "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", "dev": true, - "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "is-string": "^1.0.7" + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -4303,7 +6277,6 @@ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -4313,7 +6286,6 @@ "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -4334,7 +6306,6 @@ "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", @@ -4356,7 +6327,6 @@ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", @@ -4375,7 +6345,6 @@ "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", @@ -4394,7 +6363,6 @@ "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -4411,7 +6379,6 @@ "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", "dev": true, - "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.8", @@ -4433,34 +6400,46 @@ "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/ast-types-flow": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/async-function": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "dev": true, - "license": "MIT", "dependencies": { "possible-typed-array-names": "^1.0.0" }, @@ -4476,62 +6455,128 @@ "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.3.tgz", "integrity": "sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==", "dev": true, - "license": "MPL-2.0", "engines": { "node": ">=4" } }, + "node_modules/axios": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz", + "integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/axobject-query": { "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" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.13", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.13.tgz", + "integrity": "sha512-3sX/eOms8kd3q2KZ6DAhKPc0dgm525Gqq5NtWKZ7QYYZEv57OQ54KtblzJzH1lQF/eQxO8KjWGIK9IPUJNus5g==", "dev": true, - "license": "MIT" + "dependencies": { + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.6.4", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } }, - "node_modules/body-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", - "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz", + "integrity": "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==", "dev": true, - "license": "MIT", "dependencies": { - "bytes": "^3.1.2", - "content-type": "^1.0.5", - "debug": "^4.4.0", - "http-errors": "^2.0.0", - "iconv-lite": "^0.6.3", - "on-finished": "^2.4.1", - "qs": "^6.14.0", - "raw-body": "^3.0.0", - "type-is": "^2.0.0" + "@babel/helper-define-polyfill-provider": "^0.6.3", + "core-js-compat": "^3.40.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.4.tgz", + "integrity": "sha512-7gD3pRadPrbjhjLyxebmx/WrFYcuSjZ0XbdUujQMZ/fcE9oeewk2U/7PCvez84UeuK3oSjmPZ0Ch0dlupQvGzw==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.4" }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/basic-ftp": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", + "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", "engines": { - "node": ">=18" + "node": ">=10.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" } }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, "node_modules/boolify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/boolify/-/boolify-1.0.1.tgz", "integrity": "sha512-ma2q0Tc760dW54CdOyJjhrg/a54317o1zYADQJFgperNGKIKgAUGIcKnuMiff8z57+yGlrGNEt4lPgZfCgTJgA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4542,7 +6587,6 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, - "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, @@ -4550,6 +6594,67 @@ "node": ">=8" } }, + "node_modules/browserslist": { + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz", + "integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001718", + "electron-to-chromium": "^1.5.160", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -4561,22 +6666,11 @@ "node": ">=10.16.0" } }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", "dev": true, - "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", @@ -4594,8 +6688,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, - "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" @@ -4609,7 +6701,6 @@ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "dev": true, - "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" @@ -4626,19 +6717,17 @@ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/camelcase": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", - "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, - "license": "MIT", "engines": { - "node": ">=16" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -4649,7 +6738,6 @@ "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-9.1.3.tgz", "integrity": "sha512-Rircqi9ch8AnZscQcsA1C47NFdaO3wukpmIRzYcDOrmvgt78hM/sj5pZhZNec2NM12uk5vTwRHZ4anGcrC4ZTg==", "dev": true, - "license": "MIT", "dependencies": { "camelcase": "^8.0.0", "map-obj": "5.0.0", @@ -4663,12 +6751,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/camelcase-keys/node_modules/camelcase": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", + "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/camelcase-keys/node_modules/type-fest": { "version": "4.41.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", "dev": true, - "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=16" }, @@ -4677,9 +6776,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001717", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001717.tgz", - "integrity": "sha512-auPpttCq6BDEG8ZAuHJIplGw6GODhjw+/11e7IjpnYCxZcW/ONgPs0KVBJ0d1bY3e2+7PRe5RCLyP+PfwVgkYw==", + "version": "1.0.30001723", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001723.tgz", + "integrity": "sha512-1R/elMjtehrFejxwmexeXAtae5UO9iSyFn6G/I806CYC/BLyyBk1EPhrKBkWhy6wM6Xnm47dSJQec+tLJ39WHw==", "funding": [ { "type": "opencollective", @@ -4693,15 +6792,12 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ], - "license": "CC-BY-4.0" + ] }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -4713,12 +6809,16 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + }, "node_modules/chownr": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", "dev": true, - "license": "BlueOak-1.0.0", "engines": { "node": ">=18" } @@ -4727,7 +6827,6 @@ "version": "0.7.1", "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", - "license": "Apache-2.0", "dependencies": { "clsx": "^2.1.1" }, @@ -4738,64 +6837,86 @@ "node_modules/classcat": { "version": "5.0.5", "resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz", - "integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==", - "license": "MIT" + "integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==" }, "node_modules/classnames": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", - "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", - "license": "MIT" + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "engines": { + "node": ">= 10" + } }, "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", - "license": "MIT" + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" }, "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dependencies": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", + "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" } }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "engines": { - "node": ">=8" + "node": ">=0.8" } }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", "engines": { "node": ">=6" } @@ -4804,7 +6925,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "license": "MIT", "optional": true, "dependencies": { "color-convert": "^2.0.1", @@ -4818,8 +6938,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "devOptional": true, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -4830,105 +6948,190 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "devOptional": true, - "license": "MIT" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/color-string": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "license": "MIT", "optional": true, "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "engines": { + "node": ">= 12" + } + }, "node_modules/common-tags": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", "dev": true, - "license": "MIT", "engines": { "node": ">=4.0.0" } }, + "node_modules/compare-versions": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-4.1.4.tgz", + "integrity": "sha512-FemMreK9xNyL8gQevsdRMrvO4lFCkQP7qbuktn1q8ndcNk1+0mz7lgE7b/sNvbhVgY4w6tMN1FDp6aADjqw2rw==" + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" + "dev": true }, - "node_modules/content-disposition": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", - "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", - "dev": true, - "license": "MIT", + "node_modules/concurrently": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-6.5.1.tgz", + "integrity": "sha512-FlSwNpGjWQfRwPLXvJ/OgysbBxPkWpiVjy1042b0U7on7S7qwwMIILRj7WTN1mTgqa582bG6NFuScOoh6Zgdag==", "dependencies": { - "safe-buffer": "5.2.1" + "chalk": "^4.1.0", + "date-fns": "^2.16.1", + "lodash": "^4.17.21", + "rxjs": "^6.6.3", + "spawn-command": "^0.0.2-1", + "supports-color": "^8.1.0", + "tree-kill": "^1.2.2", + "yargs": "^16.2.0" + }, + "bin": { + "concurrently": "bin/concurrently.js" }, "engines": { - "node": ">= 0.6" + "node": ">=10.0.0" } }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "dev": true, - "license": "MIT", + "node_modules/concurrently/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dependencies": { + "tslib": "^1.9.0" + }, "engines": { - "node": ">= 0.6" + "npm": ">=2.0.0" + } + }, + "node_modules/concurrently/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/console.table": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/console.table/-/console.table-0.10.0.tgz", + "integrity": "sha512-dPyZofqggxuvSf7WXvNjuRfnsOk1YazkVP8FdxH4tcH2c37wc79/Yl6Bhr7Lsu00KMgy2ql/qCMuNu8xctZM8g==", + "dependencies": { + "easy-table": "1.1.0" + }, + "engines": { + "node": "> 0.10" } }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, "node_modules/cookie": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" } }, - "node_modules/cookie-signature": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "node_modules/core-js": { + "version": "3.43.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.43.0.tgz", + "integrity": "sha512-N6wEbTTZSYOY2rYAn85CuvWWkCK6QweMn7/4Nr3w+gDBeBhk/x4EJeY6FPo4QzDoJZxVTv8U7CMvgWk6pOHHqA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.6.0" + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" } }, - "node_modules/core-js": { - "version": "3.42.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.42.0.tgz", - "integrity": "sha512-Sz4PP4ZA+Rq4II21qkNqOEDTDrCvcANId3xpIgB34NDkWc3UduWj2dqEtN9yZIq8Dk3HyPI33x9sqqU5C8sr0g==", + "node_modules/core-js-compat": { + "version": "3.43.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.43.0.tgz", + "integrity": "sha512-2GML2ZsCc5LR7hZYz4AXmjQw8zuy2T//2QntwdnpuYI7jteT6GVYJL7F6C2C57R7gSYrcqVW3lAALefdbhBLDA==", "dev": true, - "hasInstallScript": true, - "license": "MIT", + "dependencies": { + "browserslist": "^4.25.0" + }, "funding": { "type": "opencollective", "url": "https://opencollective.com/core-js" } }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", "dev": true, - "license": "MIT", "dependencies": { - "object-assign": "^4", - "vary": "^1" + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" }, "engines": { - "node": ">= 0.10" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/cross-spawn": { @@ -4936,7 +7139,6 @@ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, - "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -4946,18 +7148,90 @@ "node": ">= 8" } }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dev": true, + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "dev": true, + "dependencies": { + "css-tree": "~2.2.0" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "dev": true, + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "dev": true + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "devOptional": true, - "license": "MIT" + "devOptional": true }, "node_modules/d3-color": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", - "license": "ISC", "engines": { "node": ">=12" } @@ -4966,7 +7240,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", - "license": "ISC", "engines": { "node": ">=12" } @@ -4975,7 +7248,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", - "license": "ISC", "dependencies": { "d3-dispatch": "1 - 3", "d3-selection": "3" @@ -4988,7 +7260,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", - "license": "BSD-3-Clause", "engines": { "node": ">=12" } @@ -4997,7 +7268,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", - "license": "ISC", "dependencies": { "d3-color": "1 - 3" }, @@ -5009,7 +7279,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", - "license": "ISC", "engines": { "node": ">=12" } @@ -5018,7 +7287,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", - "license": "ISC", "engines": { "node": ">=12" } @@ -5027,7 +7295,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", - "license": "ISC", "dependencies": { "d3-color": "1 - 3", "d3-dispatch": "1 - 3", @@ -5046,7 +7313,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", - "license": "ISC", "dependencies": { "d3-dispatch": "1 - 3", "d3-drag": "2 - 3", @@ -5062,15 +7328,21 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", - "dev": true, - "license": "BSD-2-Clause" + "dev": true + }, + "node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "engines": { + "node": ">= 14" + } }, "node_modules/data-view-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", @@ -5088,7 +7360,6 @@ "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", @@ -5106,7 +7377,6 @@ "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -5119,12 +7389,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, - "license": "MIT", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "dependencies": { "ms": "^2.1.3" }, @@ -5141,15 +7424,33 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "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==", "dev": true, - "license": "MIT" + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dev": true, - "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -5167,7 +7468,6 @@ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, - "license": "MIT", "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", @@ -5180,14 +7480,25 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true, - "license": "MIT", + "node_modules/degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, "engines": { - "node": ">= 0.8" + "node": ">= 14" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" } }, "node_modules/detect-libc": { @@ -5195,7 +7506,6 @@ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", "devOptional": true, - "license": "Apache-2.0", "engines": { "node": ">=8" } @@ -5203,15 +7513,13 @@ "node_modules/detect-node-es": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", - "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", - "license": "MIT" + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, - "license": "MIT", "dependencies": { "path-type": "^4.0.0" }, @@ -5223,15 +7531,13 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, - "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" }, @@ -5239,12 +7545,75 @@ "node": ">=0.10.0" } }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, - "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", @@ -5258,39 +7627,42 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" + "dev": true }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "dev": true, - "license": "MIT" + "node_modules/easy-table": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.1.0.tgz", + "integrity": "sha512-oq33hWOSSnl2Hoh00tZWaIPi1ievrD9aFG82/IgjlycAnW9hHx5PkJiXpxPsgEE+H7BsbVQXFVFST8TEXS6/pA==", + "optionalDependencies": { + "wcwidth": ">=1.0.1" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.167", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.167.tgz", + "integrity": "sha512-LxcRvnYO5ez2bMOFpbuuVuAI5QNeY1ncVytE/KXaL6ZNfzX1yPlAO0nSOyIHx2fVAuUprMqPs/TdVhUFZy7SIQ==", + "dev": true }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } + "dev": true }, "node_modules/enhanced-resolve": { "version": "5.18.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", "dev": true, - "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -5299,29 +7671,49 @@ "node": ">=10.13.0" } }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, "node_modules/es-abstract": { - "version": "1.23.9", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", - "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", + "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", "dev": true, - "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", - "call-bound": "^1.0.3", + "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", + "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.0", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", @@ -5333,21 +7725,24 @@ "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", + "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", - "is-weakref": "^1.1.0", + "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", - "object-inspect": "^1.13.3", + "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", - "regexp.prototype.flags": "^1.5.3", + "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", @@ -5356,7 +7751,7 @@ "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", - "which-typed-array": "^1.1.18" + "which-typed-array": "^1.1.19" }, "engines": { "node": ">= 0.4" @@ -5369,8 +7764,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" } @@ -5379,8 +7772,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" } @@ -5390,7 +7781,6 @@ "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", @@ -5417,8 +7807,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, - "license": "MIT", "dependencies": { "es-errors": "^1.3.0" }, @@ -5430,8 +7818,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, - "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", @@ -5447,7 +7833,6 @@ "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", "dev": true, - "license": "MIT", "dependencies": { "hasown": "^2.0.2" }, @@ -5460,7 +7845,6 @@ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", "dev": true, - "license": "MIT", "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", @@ -5477,25 +7861,15 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "dev": true, - "license": "MIT" - }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -5503,25 +7877,43 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, "node_modules/eslint": { - "version": "9.26.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.26.0.tgz", - "integrity": "sha512-Hx0MOjPh6uK9oq9nVsATZKE/Wlbai7KFjfCuw9UHaguDW3x+HF0O5nIi3ud39TWgrTjTO5nHxmL3R1eANinWHQ==", + "version": "9.29.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.29.0.tgz", + "integrity": "sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.20.0", + "@eslint/config-array": "^0.20.1", "@eslint/config-helpers": "^0.2.1", - "@eslint/core": "^0.13.0", + "@eslint/core": "^0.14.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.26.0", - "@eslint/plugin-kit": "^0.2.8", + "@eslint/js": "9.29.0", + "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", - "@modelcontextprotocol/sdk": "^1.8.0", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", @@ -5529,9 +7921,9 @@ "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.3.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -5545,8 +7937,7 @@ "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "zod": "^3.24.2" + "optionator": "^0.9.3" }, "bin": { "eslint": "bin/eslint.js" @@ -5567,13 +7958,12 @@ } }, "node_modules/eslint-config-next": { - "version": "15.3.2", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.3.2.tgz", - "integrity": "sha512-FerU4DYccO4FgeYFFglz0SnaKRe1ejXQrDb8kWUkTAg036YWi+jUsgg4sIGNCDhAsDITsZaL4MzBWKB6f4G1Dg==", + "version": "15.3.3", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.3.3.tgz", + "integrity": "sha512-QJLv/Ouk2vZnxL4b67njJwTLjTf7uZRltI0LL4GERYR4qMF5z08+gxkfODAeaK7TiC6o+cER91bDaEnwrTWV6Q==", "dev": true, - "license": "MIT", "dependencies": { - "@next/eslint-plugin-next": "15.3.2", + "@next/eslint-plugin-next": "15.3.3", "@rushstack/eslint-patch": "^1.10.3", "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", @@ -5599,7 +7989,6 @@ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", "dev": true, - "license": "MIT", "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.13.0", @@ -5611,7 +8000,6 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, - "license": "MIT", "dependencies": { "ms": "^2.1.1" } @@ -5621,7 +8009,6 @@ "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", "dev": true, - "license": "ISC", "dependencies": { "@nolyfill/is-core-module": "1.0.39", "debug": "^4.4.0", @@ -5656,7 +8043,6 @@ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", "dev": true, - "license": "MIT", "dependencies": { "debug": "^3.2.7" }, @@ -5674,7 +8060,6 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, - "license": "MIT", "dependencies": { "ms": "^2.1.1" } @@ -5684,7 +8069,6 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", "dev": true, - "license": "MIT", "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.8", @@ -5718,27 +8102,15 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, - "license": "MIT", "dependencies": { "ms": "^2.1.1" } }, - "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/eslint-plugin-jsx-a11y": { "version": "6.10.2", "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", "dev": true, - "license": "MIT", "dependencies": { "aria-query": "^5.3.2", "array-includes": "^3.1.8", @@ -5768,7 +8140,6 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", "dev": true, - "license": "MIT", "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", @@ -5801,7 +8172,6 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -5814,7 +8184,6 @@ "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "dev": true, - "license": "MIT", "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -5827,22 +8196,11 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/eslint-scope": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", - "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -5855,11 +8213,22 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, - "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -5868,16 +8237,27 @@ } }, "node_modules/espree": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.14.0", + "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -5885,12 +8265,23 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/esquery": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" }, @@ -5903,7 +8294,6 @@ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -5915,8 +8305,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } @@ -5925,123 +8313,40 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/eventsource": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", - "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eventsource-parser": "^3.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/eventsource-parser": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.1.tgz", - "integrity": "sha512-VARTJ9CYeuQYb0pZEPbzi740OWFgpHe7AYJ2WFZVnUDUQp5Dk2yJUgF36YsZ81cOyxT0QxmXD2EQpapAouzWVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/express": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", - "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", - "dev": true, - "license": "MIT", + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", "dependencies": { - "accepts": "^2.0.0", - "body-parser": "^2.2.0", - "content-disposition": "^1.0.0", - "content-type": "^1.0.5", - "cookie": "^0.7.1", - "cookie-signature": "^1.2.1", - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "finalhandler": "^2.1.0", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "merge-descriptors": "^2.0.0", - "mime-types": "^3.0.0", - "on-finished": "^2.4.1", - "once": "^1.4.0", - "parseurl": "^1.3.3", - "proxy-addr": "^2.0.7", - "qs": "^6.14.0", - "range-parser": "^1.2.1", - "router": "^2.2.0", - "send": "^1.1.0", - "serve-static": "^2.2.0", - "statuses": "^2.0.1", - "type-is": "^2.0.1", - "vary": "^1.1.2" - }, - "engines": { - "node": ">= 18" + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/express-rate-limit": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz", - "integrity": "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==", - "dev": true, - "license": "MIT", "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/express-rate-limit" - }, - "peerDependencies": { - "express": "^4.11 || 5 || ^5.0.0-beta.1" + "node": ">=4" } }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/fast-glob": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", - "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, - "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -6052,7 +8357,6 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, - "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -6064,32 +8368,60 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" + "dev": true + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" }, "node_modules/fastq": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", "dev": true, - "license": "ISC", "dependencies": { "reusify": "^1.0.4" } }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==" + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, - "license": "MIT", "dependencies": { "flat-cache": "^4.0.0" }, @@ -6097,12 +8429,28 @@ "node": ">=16.0.0" } }, + "node_modules/file-type": { + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-20.5.0.tgz", + "integrity": "sha512-BfHZtG/l9iMm4Ecianu7P8HRD2tBHLtjXinm4X62XBOYzi7CYA7jyqfJzOvXHqzVrVPYqBo2/GvbARMaaJkKVg==", + "dependencies": { + "@tokenizer/inflate": "^0.2.6", + "strtok3": "^10.2.0", + "token-types": "^6.0.0", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, - "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -6110,30 +8458,11 @@ "node": ">=8" } }, - "node_modules/finalhandler": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", - "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "on-finished": "^2.4.1", - "parseurl": "^1.3.3", - "statuses": "^2.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, - "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -6150,7 +8479,6 @@ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, - "license": "MIT", "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" @@ -6163,15 +8491,32 @@ "version": "3.3.3", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true, - "license": "ISC" + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "dev": true, - "license": "MIT", "dependencies": { "is-callable": "^1.2.7" }, @@ -6187,7 +8532,6 @@ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dev": true, - "license": "ISC", "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" @@ -6199,39 +8543,82 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, - "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", - "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", - "dev": true, + "node_modules/form-data": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", + "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/framer-motion": { + "version": "12.23.6", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.6.tgz", + "integrity": "sha512-dsJ389QImVE3lQvM8Mnk99/j8tiZDM/7706PCqvkQ8sSCnpmWxsgX+g0lj7r5OBVL0U36pIecCTBoIWcM2RuKw==", "license": "MIT", + "dependencies": { + "motion-dom": "^12.23.6", + "motion-utils": "^12.23.6", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/fs-extra": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", + "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, "engines": { - "node": ">= 0.8" + "node": ">=14.14" } }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -6241,7 +8628,6 @@ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", @@ -6262,17 +8648,23 @@ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, - "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -6281,8 +8673,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, - "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", @@ -6306,7 +8696,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", - "license": "MIT", "engines": { "node": ">=6" } @@ -6315,8 +8704,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, - "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" @@ -6330,7 +8717,6 @@ "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -6343,7 +8729,6 @@ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", @@ -6357,11 +8742,10 @@ } }, "node_modules/get-tsconfig": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", - "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", + "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", "dev": true, - "license": "MIT", "dependencies": { "resolve-pkg-maps": "^1.0.0" }, @@ -6369,23 +8753,31 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/get-uri": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.4.tgz", + "integrity": "sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ==", + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", + "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==", "dependencies": { "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -6396,7 +8788,6 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, - "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, @@ -6404,12 +8795,33 @@ "node": ">=10.13.0" } }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.4.tgz", + "integrity": "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=18" }, @@ -6422,7 +8834,6 @@ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, - "license": "MIT", "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" @@ -6439,7 +8850,6 @@ "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, - "license": "MIT", "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", @@ -6459,8 +8869,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -6471,23 +8879,19 @@ "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", "dev": true, - "license": "MIT", "dependencies": { "ansi-regex": "^2.0.0" }, @@ -6495,12 +8899,20 @@ "node": ">=0.10.0" } }, + "node_modules/has-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -6512,8 +8924,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -6523,7 +8933,6 @@ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, - "license": "MIT", "dependencies": { "es-define-property": "^1.0.0" }, @@ -6536,7 +8945,6 @@ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", "dev": true, - "license": "MIT", "dependencies": { "dunder-proto": "^1.0.0" }, @@ -6551,8 +8959,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -6564,8 +8970,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, - "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" }, @@ -6580,8 +8984,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -6589,42 +8991,65 @@ "node": ">= 0.4" } }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dev": true, - "license": "MIT", + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "agent-base": "^7.1.0", + "debug": "^4.3.4" }, "engines": { - "node": ">= 0.8" + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" } }, "node_modules/iconv-lite": { - "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", + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "safer-buffer": ">= 2.1.2 < 3" }, "engines": { "node": ">=0.10.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, - "license": "MIT", "engines": { "node": ">= 4" } @@ -6634,7 +9059,6 @@ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, - "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -6651,7 +9075,6 @@ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.8.19" } @@ -6661,7 +9084,6 @@ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -6672,7 +9094,6 @@ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, - "license": "ISC", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -6681,16 +9102,38 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "license": "ISC" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/inquirer": { + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", + "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^6.0.1" + }, + "engines": { + "node": ">=12.0.0" + } }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dev": true, - "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", @@ -6700,14 +9143,16 @@ "node": ">= 0.4" } }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "dev": true, - "license": "MIT", + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, "engines": { - "node": ">= 0.10" + "node": ">= 12" } }, "node_modules/is-array-buffer": { @@ -6715,7 +9160,6 @@ "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", @@ -6729,18 +9173,16 @@ } }, "node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "license": "MIT", - "optional": true + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true }, "node_modules/is-async-function": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", "dev": true, - "license": "MIT", "dependencies": { "async-function": "^1.0.0", "call-bound": "^1.0.3", @@ -6760,7 +9202,6 @@ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", "dev": true, - "license": "MIT", "dependencies": { "has-bigints": "^1.0.2" }, @@ -6776,7 +9217,6 @@ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" @@ -6793,17 +9233,27 @@ "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", "dev": true, - "license": "MIT", "dependencies": { "semver": "^7.7.1" } }, + "node_modules/is-bun-module/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -6816,7 +9266,6 @@ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dev": true, - "license": "MIT", "dependencies": { "hasown": "^2.0.2" }, @@ -6832,7 +9281,6 @@ "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", @@ -6850,7 +9298,6 @@ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" @@ -6867,7 +9314,6 @@ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -6877,7 +9323,6 @@ "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3" }, @@ -6892,8 +9337,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -6903,7 +9346,6 @@ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "get-proto": "^1.0.0", @@ -6922,7 +9364,6 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, - "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -6930,12 +9371,31 @@ "node": ">=0.10.0" } }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "engines": { + "node": ">=8" + } + }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "dev": true, - "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, "engines": { "node": ">= 0.4" }, @@ -6948,7 +9408,6 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -6958,7 +9417,6 @@ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" @@ -6975,24 +9433,15 @@ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/is-promise": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "dev": true, - "license": "MIT" - }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", @@ -7011,7 +9460,6 @@ "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -7024,7 +9472,6 @@ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3" }, @@ -7040,7 +9487,6 @@ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" @@ -7057,7 +9503,6 @@ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "has-symbols": "^1.1.0", @@ -7075,7 +9520,6 @@ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", "dev": true, - "license": "MIT", "dependencies": { "which-typed-array": "^1.1.16" }, @@ -7086,12 +9530,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-weakmap": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -7104,7 +9558,6 @@ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3" }, @@ -7120,7 +9573,6 @@ "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" @@ -7136,22 +9588,27 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" + "dev": true + }, + "node_modules/iterare": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz", + "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==", + "engines": { + "node": ">=6" + } }, "node_modules/iterator.prototype": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", "dev": true, - "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", "es-object-atoms": "^1.0.0", @@ -7169,7 +9626,6 @@ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, - "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -7185,23 +9641,30 @@ "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", "dev": true, - "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/jose": { + "version": "4.15.9", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", + "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" + "dev": true }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, - "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -7209,38 +9672,102 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", "license": "MIT", "dependencies": { - "minimist": "^1.2.0" + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", "bin": { - "json5": "lib/cli.js" + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/jsx-ast-utils": { @@ -7248,7 +9775,6 @@ "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", "dev": true, - "license": "MIT", "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", @@ -7259,12 +9785,32 @@ "node": ">=4.0" } }, + "node_modules/jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, - "license": "MIT", "dependencies": { "json-buffer": "3.0.1" } @@ -7273,15 +9819,13 @@ "version": "0.3.23", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", - "dev": true, - "license": "CC0-1.0" + "dev": true }, "node_modules/language-tags": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", "dev": true, - "license": "MIT", "dependencies": { "language-subtag-registry": "^0.3.20" }, @@ -7294,7 +9838,6 @@ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, - "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -7304,11 +9847,10 @@ } }, "node_modules/lightningcss": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.29.2.tgz", - "integrity": "sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", "dev": true, - "license": "MPL-2.0", "dependencies": { "detect-libc": "^2.0.3" }, @@ -7320,27 +9862,26 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "lightningcss-darwin-arm64": "1.29.2", - "lightningcss-darwin-x64": "1.29.2", - "lightningcss-freebsd-x64": "1.29.2", - "lightningcss-linux-arm-gnueabihf": "1.29.2", - "lightningcss-linux-arm64-gnu": "1.29.2", - "lightningcss-linux-arm64-musl": "1.29.2", - "lightningcss-linux-x64-gnu": "1.29.2", - "lightningcss-linux-x64-musl": "1.29.2", - "lightningcss-win32-arm64-msvc": "1.29.2", - "lightningcss-win32-x64-msvc": "1.29.2" + "lightningcss-darwin-arm64": "1.30.1", + "lightningcss-darwin-x64": "1.30.1", + "lightningcss-freebsd-x64": "1.30.1", + "lightningcss-linux-arm-gnueabihf": "1.30.1", + "lightningcss-linux-arm64-gnu": "1.30.1", + "lightningcss-linux-arm64-musl": "1.30.1", + "lightningcss-linux-x64-gnu": "1.30.1", + "lightningcss-linux-x64-musl": "1.30.1", + "lightningcss-win32-arm64-msvc": "1.30.1", + "lightningcss-win32-x64-msvc": "1.30.1" } }, "node_modules/lightningcss-darwin-arm64": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.29.2.tgz", - "integrity": "sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", "cpu": [ "arm64" ], "dev": true, - "license": "MPL-2.0", "optional": true, "os": [ "darwin" @@ -7354,14 +9895,13 @@ } }, "node_modules/lightningcss-darwin-x64": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.29.2.tgz", - "integrity": "sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", "cpu": [ "x64" ], "dev": true, - "license": "MPL-2.0", "optional": true, "os": [ "darwin" @@ -7375,14 +9915,13 @@ } }, "node_modules/lightningcss-freebsd-x64": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.29.2.tgz", - "integrity": "sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", "cpu": [ "x64" ], "dev": true, - "license": "MPL-2.0", "optional": true, "os": [ "freebsd" @@ -7396,14 +9935,13 @@ } }, "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.29.2.tgz", - "integrity": "sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", "cpu": [ "arm" ], "dev": true, - "license": "MPL-2.0", "optional": true, "os": [ "linux" @@ -7417,14 +9955,13 @@ } }, "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.29.2.tgz", - "integrity": "sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", "cpu": [ "arm64" ], "dev": true, - "license": "MPL-2.0", "optional": true, "os": [ "linux" @@ -7438,14 +9975,13 @@ } }, "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.29.2.tgz", - "integrity": "sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", "cpu": [ "arm64" ], "dev": true, - "license": "MPL-2.0", "optional": true, "os": [ "linux" @@ -7459,14 +9995,13 @@ } }, "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.29.2.tgz", - "integrity": "sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", "cpu": [ "x64" ], "dev": true, - "license": "MPL-2.0", "optional": true, "os": [ "linux" @@ -7480,14 +10015,13 @@ } }, "node_modules/lightningcss-linux-x64-musl": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.29.2.tgz", - "integrity": "sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", "cpu": [ "x64" ], "dev": true, - "license": "MPL-2.0", "optional": true, "os": [ "linux" @@ -7501,14 +10035,13 @@ } }, "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.29.2.tgz", - "integrity": "sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", "cpu": [ "arm64" ], "dev": true, - "license": "MPL-2.0", "optional": true, "os": [ "win32" @@ -7522,14 +10055,13 @@ } }, "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.29.2.tgz", - "integrity": "sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", "cpu": [ "x64" ], "dev": true, - "license": "MPL-2.0", "optional": true, "os": [ "win32" @@ -7542,12 +10074,35 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/load-esm": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/load-esm/-/load-esm-1.0.2.tgz", + "integrity": "sha512-nVAvWk/jeyrWyXEAs84mpQCYccxRqgKY4OznLuJhJCa0XsPSfdOIr2zvBZEj3IHEHbX97jjscKRRV539bW0Gpw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + }, + { + "type": "buymeacoffee", + "url": "https://buymeacoffee.com/borewit" + } + ], + "engines": { + "node": ">=13.2.0" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, - "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -7562,29 +10117,88 @@ "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, + "license": "MIT" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", "license": "MIT" }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, + "dev": true + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", "license": "MIT" }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/loglevel": { "version": "1.9.2", "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz", "integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6.0" }, @@ -7598,18 +10212,25 @@ "resolved": "https://registry.npmjs.org/loglevel-colored-level-prefix/-/loglevel-colored-level-prefix-1.0.0.tgz", "integrity": "sha512-u45Wcxxc+SdAlh4yeF/uKlC1SPUPCy0gullSNKXod5I4bmifzk+Q4lSLExNEVn19tGaJipbZ4V4jbFn79/6mVA==", "dev": true, - "license": "MIT", "dependencies": { "chalk": "^1.1.3", "loglevel": "^1.4.1" } }, + "node_modules/loglevel-colored-level-prefix/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/loglevel-colored-level-prefix/node_modules/ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -7619,7 +10240,6 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", @@ -7636,17 +10256,27 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.8.0" } }, + "node_modules/loglevel-colored-level-prefix/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/loglevel-colored-level-prefix/node_modules/supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.8.0" } @@ -7655,7 +10285,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "license": "MIT", + "dev": true, "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -7663,18 +10293,28 @@ "loose-envify": "cli.js" } }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, "node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, - "license": "ISC" + "dependencies": { + "yallist": "^3.0.2" + } }, "node_modules/lucide-react": { - "version": "0.509.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.509.0.tgz", - "integrity": "sha512-xCJHn6Uh5qF6PGml25vveCTrHJZcqS1G1MVzWZK54ZQsOiCVJk4fwY3oyo5EXS2S+aqvTpWYIfJN+PesJ0quxg==", - "license": "ISC", + "version": "0.515.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.515.0.tgz", + "integrity": "sha512-Sy7bY0MeicRm2pzrnoHm2h6C1iVoeHyBU2fjdQDsXGP51fhkhau1/ZV/dzrcxEmAKsxYb6bGaIsMnGHuQ5s0dw==", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } @@ -7684,7 +10324,6 @@ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", "dev": true, - "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } @@ -7693,15 +10332,13 @@ "version": "7.4.0", "resolved": "https://registry.npmjs.org/make-plural/-/make-plural-7.4.0.tgz", "integrity": "sha512-4/gC9KVNTV6pvYg2gFeQYTW3mWaoJt7WZE5vrp1KnQDgW92JtYZnzmZT81oj/dUTqAIu0ufI2x3dkgu3bB1tYg==", - "dev": true, - "license": "Unicode-DFS-2016" + "dev": true }, "node_modules/map-obj": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-5.0.0.tgz", "integrity": "sha512-2L3MIgJynYrZ3TYMriLDLWocz15okFakV6J12HXvMXDHui2x/zgChzg1u9mFFGbbGWE+GsLpQByt4POb9Or+uA==", "dev": true, - "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -7713,41 +10350,21 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" } }, - "node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/merge-descriptors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", - "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 8" } @@ -7757,7 +10374,6 @@ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, - "license": "MIT", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -7767,34 +10383,37 @@ } }, "node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "dev": true, - "license": "MIT", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", - "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", - "dev": true, - "license": "MIT", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dependencies": { - "mime-db": "^1.54.0" + "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" } }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -7807,19 +10426,16 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, - "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", + "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=8" } }, "node_modules/minizlib": { @@ -7827,7 +10443,6 @@ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", "dev": true, - "license": "MIT", "dependencies": { "minipass": "^7.1.2" }, @@ -7835,12 +10450,20 @@ "node": ">= 18" } }, + "node_modules/minizlib/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/mkdirp": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", "dev": true, - "license": "MIT", "bin": { "mkdirp": "dist/cjs/src/bin.js" }, @@ -7855,15 +10478,32 @@ "version": "0.5.2", "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz", "integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==", - "dev": true, - "license": "BSD-3-Clause" + "dev": true + }, + "node_modules/motion-dom": { + "version": "12.23.6", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.6.tgz", + "integrity": "sha512-G2w6Nw7ZOVSzcQmsdLc0doMe64O/Sbuc2bVAbgMz6oP/6/pRStKRiVRV4bQfHp5AHYAKEGhEdVHTM+R3FDgi5w==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.23.6" + } + }, + "node_modules/motion-utils": { + "version": "12.23.6", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz", + "integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==", + "license": "MIT" }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" }, "node_modules/nanoid": { "version": "3.3.11", @@ -7875,7 +10515,6 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -7884,11 +10523,10 @@ } }, "node_modules/napi-postinstall": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.2.3.tgz", - "integrity": "sha512-Mi7JISo/4Ij2tDZ2xBE2WH+/KvVlkhA6juEjpEeRAVPNCpN3nxJo/5FhDNKgBcdmcmhaH6JjgST4xY/23ZYK0w==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.2.4.tgz", + "integrity": "sha512-ZEzHJwBhZ8qQSbknHqYcdtQVr8zUgGyM/q6h6qAyhtyVMNrSgDhrC4disf03dYW0e+czXyLnZINnCTEkWy0eJg==", "dev": true, - "license": "MIT", "bin": { "napi-postinstall": "lib/cli.js" }, @@ -7903,26 +10541,22 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" + "dev": true }, - "node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "dev": true, - "license": "MIT", + "node_modules/netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", "engines": { - "node": ">= 0.6" + "node": ">= 0.4.0" } }, "node_modules/next": { - "version": "15.3.2", - "resolved": "https://registry.npmjs.org/next/-/next-15.3.2.tgz", - "integrity": "sha512-CA3BatMyHkxZ48sgOCLdVHjFU36N7TF1HhqAHLFOkV6buwZnvMI84Cug8xD56B9mCuKrqXnLn94417GrZ/jjCQ==", - "license": "MIT", + "version": "15.3.3", + "resolved": "https://registry.npmjs.org/next/-/next-15.3.3.tgz", + "integrity": "sha512-JqNj29hHNmCLtNvd090SyRbXJiivQ+58XjCcrC50Crb5g5u2zi7Y2YivbsEfzk6AtVI80akdOQbaMZwWB1Hthw==", "dependencies": { - "@next/env": "15.3.2", + "@next/env": "15.3.3", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", @@ -7937,14 +10571,14 @@ "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "15.3.2", - "@next/swc-darwin-x64": "15.3.2", - "@next/swc-linux-arm64-gnu": "15.3.2", - "@next/swc-linux-arm64-musl": "15.3.2", - "@next/swc-linux-x64-gnu": "15.3.2", - "@next/swc-linux-x64-musl": "15.3.2", - "@next/swc-win32-arm64-msvc": "15.3.2", - "@next/swc-win32-x64-msvc": "15.3.2", + "@next/swc-darwin-arm64": "15.3.3", + "@next/swc-darwin-x64": "15.3.3", + "@next/swc-linux-arm64-gnu": "15.3.3", + "@next/swc-linux-arm64-musl": "15.3.3", + "@next/swc-linux-x64-gnu": "15.3.3", + "@next/swc-linux-x64-musl": "15.3.3", + "@next/swc-win32-arm64-msvc": "15.3.3", + "@next/swc-win32-x64-msvc": "15.3.3", "sharp": "^0.34.1" }, "peerDependencies": { @@ -7970,6 +10604,47 @@ } } }, + "node_modules/next-auth": { + "version": "4.24.11", + "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.24.11.tgz", + "integrity": "sha512-pCFXzIDQX7xmHFs4KVH4luCjaCbuPRtZ9oBUjUhOk84mZ9WVPf94n87TxYI4rSRf9HmfHEF8Yep3JrYDVOo3Cw==", + "license": "ISC", + "dependencies": { + "@babel/runtime": "^7.20.13", + "@panva/hkdf": "^1.0.2", + "cookie": "^0.7.0", + "jose": "^4.15.5", + "oauth": "^0.9.15", + "openid-client": "^5.4.0", + "preact": "^10.6.3", + "preact-render-to-string": "^5.1.19", + "uuid": "^8.3.2" + }, + "peerDependencies": { + "@auth/core": "0.34.2", + "next": "^12.2.5 || ^13 || ^14 || ^15", + "nodemailer": "^6.6.5", + "react": "^17.0.2 || ^18 || ^19", + "react-dom": "^17.0.2 || ^18 || ^19" + }, + "peerDependenciesMeta": { + "@auth/core": { + "optional": true + }, + "nodemailer": { + "optional": true + } + } + }, + "node_modules/next-auth/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/next/node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", @@ -7988,7 +10663,6 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", @@ -7998,22 +10672,82 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/oauth": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", + "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==", + "license": "MIT" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, + "node_modules/object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -8026,7 +10760,6 @@ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" } @@ -8036,7 +10769,6 @@ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", @@ -8057,7 +10789,6 @@ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", @@ -8073,7 +10804,6 @@ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -8092,7 +10822,6 @@ "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -8107,7 +10836,6 @@ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", @@ -8115,41 +10843,82 @@ "es-object-atoms": "^1.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/oidc-token-hash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.1.0.tgz", + "integrity": "sha512-y0W+X7Ppo7oZX6eovsRkuzcSM40Bicg2JEJkDJ4irIt1wsYAP5MLSNv+QAogO8xivMffw/9OvV3um1pxXgt1uA==", + "license": "MIT", + "engines": { + "node": "^10.13.0 || >=12.0.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dev": true, + "node_modules/openid-client": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.7.1.tgz", + "integrity": "sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew==", "license": "MIT", "dependencies": { - "ee-first": "1.1.1" + "jose": "^4.15.9", + "lru-cache": "^6.0.0", + "object-hash": "^2.2.0", + "oidc-token-hash": "^5.0.3" }, - "engines": { - "node": ">= 0.8" + "funding": { + "url": "https://github.com/sponsors/panva" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, + "node_modules/openid-client/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "license": "ISC", "dependencies": { - "wrappy": "1" + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, + "node_modules/openid-client/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, - "license": "MIT", "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -8162,12 +10931,41 @@ "node": ">= 0.8.0" } }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/own-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", "dev": true, - "license": "MIT", "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", @@ -8185,7 +10983,6 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, - "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -8201,7 +10998,6 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, - "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -8212,19 +11008,47 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pac-proxy-agent": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", + "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", + "dependencies": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.6", + "pac-resolver": "^7.0.1", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-resolver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "dependencies": { + "degenerator": "^5.0.0", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, - "license": "BlueOak-1.0.0" + "dev": true }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, - "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -8232,14 +11056,22 @@ "node": ">=6" } }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, - "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, "engines": { - "node": ">= 0.8" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/path-exists": { @@ -8247,7 +11079,6 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -8257,7 +11088,6 @@ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -8267,7 +11097,6 @@ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -8276,15 +11105,12 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/path-scurry": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -8296,12 +11122,23 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" + }, + "node_modules/path-scurry/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/path-to-regexp": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", - "dev": true, - "license": "MIT", "engines": { "node": ">=16" } @@ -8311,7 +11148,6 @@ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -8319,15 +11155,13 @@ "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, - "license": "MIT", "engines": { "node": ">=8.6" }, @@ -8335,30 +11169,19 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pkce-challenge": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz", - "integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=16.20.0" - } - }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/postcss": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "version": "8.5.5", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.5.tgz", + "integrity": "sha512-d/jtm+rdNT8tpXuHY5MMtcbJFBkhXE6593XVR9UoGCH8jSFGci7jGvMGH5RYd5PBJW+00NZQt6gf7CbagJCrhg==", "dev": true, "funding": [ { @@ -8374,9 +11197,8 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { - "nanoid": "^3.3.8", + "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -8384,12 +11206,39 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/preact": { + "version": "10.26.9", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.26.9.tgz", + "integrity": "sha512-SSjF9vcnF27mJK1XyFMNJzFd5u3pQiATFqoaDy03XuN00u4ziveVVEGt5RKJrDR8MHE/wJo9Nnad56RLzS2RMA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/preact-render-to-string": { + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz", + "integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==", + "license": "MIT", + "dependencies": { + "pretty-format": "^3.8.0" + }, + "peerDependencies": { + "preact": ">=10" + } + }, + "node_modules/preact-render-to-string/node_modules/pretty-format": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz", + "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==", + "license": "MIT" + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.8.0" } @@ -8399,7 +11248,6 @@ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "dev": true, - "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" }, @@ -8415,7 +11263,6 @@ "resolved": "https://registry.npmjs.org/prettier-eslint/-/prettier-eslint-16.4.2.tgz", "integrity": "sha512-vtJAQEkaN8fW5QKl08t7A5KCjlZuDUNeIlr9hgolMS5s3+uzbfRHDwaRnzrdqnY2YpHDmeDS/8zY0MKQHXJtaA==", "dev": true, - "license": "MIT", "dependencies": { "@typescript-eslint/parser": "^6.21.0", "common-tags": "^1.8.2", @@ -8454,7 +11301,6 @@ "resolved": "https://registry.npmjs.org/prettier-eslint-cli/-/prettier-eslint-cli-8.0.1.tgz", "integrity": "sha512-jru4JUDHzWEtM/SOxqagU7hQTVP8BVrxO2J0qNauWZuPRld6Ea2eyNaEzIGx6I+yjmOLCsjNM+vU1AJgaW1ZSQ==", "dev": true, - "license": "MIT", "dependencies": { "@messageformat/core": "^3.2.0", "@prettier/eslint": "npm:prettier-eslint@^16.1.0", @@ -8495,7 +11341,6 @@ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, - "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -8519,29 +11364,31 @@ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, - "license": "MIT", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/prettier-eslint-cli/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/prettier-eslint-cli/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "balanced-match": "^1.0.0" } }, - "node_modules/prettier-eslint-cli/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "node_modules/prettier-eslint-cli/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, - "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" } }, "node_modules/prettier-eslint-cli/node_modules/doctrine": { @@ -8549,7 +11396,6 @@ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, - "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" }, @@ -8563,7 +11409,6 @@ "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -8619,7 +11464,6 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -8631,25 +11475,11 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/prettier-eslint-cli/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/prettier-eslint-cli/node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", @@ -8667,7 +11497,6 @@ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, - "license": "MIT", "dependencies": { "flat-cache": "^3.0.4" }, @@ -8680,7 +11509,6 @@ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, - "license": "MIT", "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", @@ -8695,7 +11523,6 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, - "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -8716,7 +11543,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -8732,7 +11558,6 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, - "license": "MIT", "dependencies": { "type-fest": "^0.20.2" }, @@ -8743,17 +11568,69 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/prettier-eslint-cli/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/prettier-eslint-cli/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/prettier-eslint-cli/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/prettier-eslint-cli/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/prettier-eslint-cli/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/prettier-eslint-cli/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" } }, "node_modules/prettier-eslint/node_modules/@eslint/eslintrc": { @@ -8761,7 +11638,6 @@ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, - "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -8785,7 +11661,6 @@ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, - "license": "MIT", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } @@ -8795,7 +11670,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", @@ -8824,7 +11698,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", "dev": true, - "license": "MIT", "dependencies": { "@typescript-eslint/types": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0" @@ -8842,7 +11715,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", "dev": true, - "license": "MIT", "engines": { "node": "^16.0.0 || >=18.0.0" }, @@ -8856,7 +11728,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "@typescript-eslint/types": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0", @@ -8885,7 +11756,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -8901,7 +11771,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", "dev": true, - "license": "MIT", "dependencies": { "@typescript-eslint/types": "6.21.0", "eslint-visitor-keys": "^3.4.1" @@ -8914,22 +11783,11 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/prettier-eslint/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/prettier-eslint/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -8939,7 +11797,6 @@ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, - "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" }, @@ -8953,7 +11810,6 @@ "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -9009,7 +11865,6 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -9021,25 +11876,11 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/prettier-eslint/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/prettier-eslint/node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", @@ -9057,7 +11898,6 @@ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, - "license": "MIT", "dependencies": { "flat-cache": "^3.0.4" }, @@ -9070,7 +11910,6 @@ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, - "license": "MIT", "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", @@ -9085,7 +11924,6 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, - "license": "MIT", "dependencies": { "type-fest": "^0.20.2" }, @@ -9096,17 +11934,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/prettier-eslint/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/prettier-eslint/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">=8" + "node": ">=10" } }, "node_modules/prettier-eslint/node_modules/ts-api-utils": { @@ -9114,7 +11951,6 @@ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", "dev": true, - "license": "MIT", "engines": { "node": ">=16" }, @@ -9122,12 +11958,102 @@ "typescript": ">=4.2.0" } }, + "node_modules/prettier-eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/prettier-plugin-tailwindcss": { + "version": "0.6.13", + "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.6.13.tgz", + "integrity": "sha512-uQ0asli1+ic8xrrSmIOaElDu0FacR4x69GynTh2oZjFY10JUt6EEumTQl5tB4fMeD6I1naKd+4rXQQ7esT2i1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.21.3" + }, + "peerDependencies": { + "@ianvs/prettier-plugin-sort-imports": "*", + "@prettier/plugin-pug": "*", + "@shopify/prettier-plugin-liquid": "*", + "@trivago/prettier-plugin-sort-imports": "*", + "@zackad/prettier-plugin-twig": "*", + "prettier": "^3.0", + "prettier-plugin-astro": "*", + "prettier-plugin-css-order": "*", + "prettier-plugin-import-sort": "*", + "prettier-plugin-jsdoc": "*", + "prettier-plugin-marko": "*", + "prettier-plugin-multiline-arrays": "*", + "prettier-plugin-organize-attributes": "*", + "prettier-plugin-organize-imports": "*", + "prettier-plugin-sort-imports": "*", + "prettier-plugin-style-order": "*", + "prettier-plugin-svelte": "*" + }, + "peerDependenciesMeta": { + "@ianvs/prettier-plugin-sort-imports": { + "optional": true + }, + "@prettier/plugin-pug": { + "optional": true + }, + "@shopify/prettier-plugin-liquid": { + "optional": true + }, + "@trivago/prettier-plugin-sort-imports": { + "optional": true + }, + "@zackad/prettier-plugin-twig": { + "optional": true + }, + "prettier-plugin-astro": { + "optional": true + }, + "prettier-plugin-css-order": { + "optional": true + }, + "prettier-plugin-import-sort": { + "optional": true + }, + "prettier-plugin-jsdoc": { + "optional": true + }, + "prettier-plugin-marko": { + "optional": true + }, + "prettier-plugin-multiline-arrays": { + "optional": true + }, + "prettier-plugin-organize-attributes": { + "optional": true + }, + "prettier-plugin-organize-imports": { + "optional": true + }, + "prettier-plugin-sort-imports": { + "optional": true + }, + "prettier-plugin-style-order": { + "optional": true + }, + "prettier-plugin-svelte": { + "optional": true + } + } + }, "node_modules/pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, - "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -9142,7 +12068,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -9150,65 +12075,63 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/pretty-format/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "dev": true, - "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dev": true, - "license": "MIT", + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + }, + "node_modules/proxy-agent": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", + "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.6", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.1.0", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.5" }, "engines": { - "node": ">= 0.10" + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "engines": { + "node": ">=12" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, - "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -9227,15 +12150,13 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] }, "node_modules/quick-lru": { "version": "6.1.2", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.2.tgz", "integrity": "sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=12" }, @@ -9244,58 +12165,57 @@ } }, "node_modules/radix-ui": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/radix-ui/-/radix-ui-1.4.1.tgz", - "integrity": "sha512-xG1aeAgvAiVglxHXMpHyk7RqLGnc8VnDUZvzpE8rZ8GAhuGeNm/+7YbIwCV+rKKRpsSgxdnvfUObQidK2EnTzw==", - "license": "MIT", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/radix-ui/-/radix-ui-1.4.2.tgz", + "integrity": "sha512-fT/3YFPJzf2WUpqDoQi005GS8EpCi+53VhcLaHUj5fwkPYiZAjk1mSxFvbMA8Uq71L03n+WysuYC+mlKkXxt/Q==", "dependencies": { "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-accessible-icon": "1.1.6", - "@radix-ui/react-accordion": "1.2.10", - "@radix-ui/react-alert-dialog": "1.1.13", - "@radix-ui/react-arrow": "1.1.6", - "@radix-ui/react-aspect-ratio": "1.1.6", - "@radix-ui/react-avatar": "1.1.9", - "@radix-ui/react-checkbox": "1.3.1", - "@radix-ui/react-collapsible": "1.1.10", - "@radix-ui/react-collection": "1.1.6", + "@radix-ui/react-accessible-icon": "1.1.7", + "@radix-ui/react-accordion": "1.2.11", + "@radix-ui/react-alert-dialog": "1.1.14", + "@radix-ui/react-arrow": "1.1.7", + "@radix-ui/react-aspect-ratio": "1.1.7", + "@radix-ui/react-avatar": "1.1.10", + "@radix-ui/react-checkbox": "1.3.2", + "@radix-ui/react-collapsible": "1.1.11", + "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-context-menu": "2.2.14", - "@radix-ui/react-dialog": "1.1.13", + "@radix-ui/react-context-menu": "2.2.15", + "@radix-ui/react-dialog": "1.1.14", "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.9", - "@radix-ui/react-dropdown-menu": "2.1.14", + "@radix-ui/react-dismissable-layer": "1.1.10", + "@radix-ui/react-dropdown-menu": "2.1.15", "@radix-ui/react-focus-guards": "1.1.2", - "@radix-ui/react-focus-scope": "1.1.6", - "@radix-ui/react-form": "0.1.6", - "@radix-ui/react-hover-card": "1.1.13", - "@radix-ui/react-label": "2.1.6", - "@radix-ui/react-menu": "2.1.14", - "@radix-ui/react-menubar": "1.1.14", - "@radix-ui/react-navigation-menu": "1.2.12", - "@radix-ui/react-one-time-password-field": "0.1.6", - "@radix-ui/react-password-toggle-field": "0.1.1", - "@radix-ui/react-popover": "1.1.13", - "@radix-ui/react-popper": "1.2.6", - "@radix-ui/react-portal": "1.1.8", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-form": "0.1.7", + "@radix-ui/react-hover-card": "1.1.14", + "@radix-ui/react-label": "2.1.7", + "@radix-ui/react-menu": "2.1.15", + "@radix-ui/react-menubar": "1.1.15", + "@radix-ui/react-navigation-menu": "1.2.13", + "@radix-ui/react-one-time-password-field": "0.1.7", + "@radix-ui/react-password-toggle-field": "0.1.2", + "@radix-ui/react-popover": "1.1.14", + "@radix-ui/react-popper": "1.2.7", + "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.4", - "@radix-ui/react-primitive": "2.1.2", - "@radix-ui/react-progress": "1.1.6", - "@radix-ui/react-radio-group": "1.3.6", - "@radix-ui/react-roving-focus": "1.1.9", - "@radix-ui/react-scroll-area": "1.2.8", - "@radix-ui/react-select": "2.2.4", - "@radix-ui/react-separator": "1.1.6", - "@radix-ui/react-slider": "1.3.4", - "@radix-ui/react-slot": "1.2.2", - "@radix-ui/react-switch": "1.2.4", - "@radix-ui/react-tabs": "1.1.11", - "@radix-ui/react-toast": "1.2.13", - "@radix-ui/react-toggle": "1.1.8", - "@radix-ui/react-toggle-group": "1.1.9", - "@radix-ui/react-toolbar": "1.1.9", - "@radix-ui/react-tooltip": "1.2.6", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-progress": "1.1.7", + "@radix-ui/react-radio-group": "1.3.7", + "@radix-ui/react-roving-focus": "1.1.10", + "@radix-ui/react-scroll-area": "1.2.9", + "@radix-ui/react-select": "2.2.5", + "@radix-ui/react-separator": "1.1.7", + "@radix-ui/react-slider": "1.3.5", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-switch": "1.2.5", + "@radix-ui/react-tabs": "1.1.12", + "@radix-ui/react-toast": "1.2.14", + "@radix-ui/react-toggle": "1.1.9", + "@radix-ui/react-toggle-group": "1.1.10", + "@radix-ui/react-toolbar": "1.1.10", + "@radix-ui/react-tooltip": "1.2.7", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-effect-event": "0.0.2", @@ -9303,7 +12223,7 @@ "@radix-ui/react-use-is-hydrated": "0.1.0", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", - "@radix-ui/react-visually-hidden": "1.2.2" + "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -9320,69 +12240,44 @@ } } }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", - "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.6.3", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - }, + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", + "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", "engines": { "node": ">=0.10.0" } }, + "node_modules/react-colorful": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz", + "integrity": "sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==", + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", - "license": "MIT", + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", + "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" + "scheduler": "^0.26.0" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^19.1.0" } }, "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true, - "license": "MIT" + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true }, "node_modules/react-remove-scroll": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.3.tgz", - "integrity": "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ==", - "license": "MIT", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz", + "integrity": "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==", "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", @@ -9407,7 +12302,6 @@ "version": "2.3.8", "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", - "license": "MIT", "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" @@ -9429,7 +12323,6 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", - "license": "MIT", "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" @@ -9447,12 +12340,29 @@ } } }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==" + }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", @@ -9470,12 +12380,29 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/regexp.prototype.flags": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", @@ -9491,12 +12418,57 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/regexpu-core": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", + "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.12.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "dev": true + }, + "node_modules/regjsparser": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", + "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", + "dev": true, + "dependencies": { + "jsesc": "~3.0.2" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -9505,15 +12477,13 @@ "version": "0.8.7", "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", "integrity": "sha512-AKGr4qvHiryxRb19m3PsLRGuKVAbJLUD7E6eOaHkfKhwc+vSgVOCY5xNvm9EkolBKTOf0GrQAZKLimOCz81Khg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "dev": true, - "license": "MIT", "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", @@ -9534,7 +12504,6 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } @@ -9544,17 +12513,27 @@ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "dev": true, - "license": "MIT", "funding": { "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "dev": true, - "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -9566,7 +12545,6 @@ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, - "license": "ISC", "dependencies": { "glob": "^7.1.3" }, @@ -9577,21 +12555,33 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/router": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", - "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, - "license": "MIT", "dependencies": { - "debug": "^4.4.0", - "depd": "^2.0.0", - "is-promise": "^4.0.0", - "parseurl": "^1.3.3", - "path-to-regexp": "^8.0.0" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": ">= 18" + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "engines": { + "node": ">=0.12.0" } }, "node_modules/run-parallel": { @@ -9613,7 +12603,6 @@ "url": "https://feross.org/support" } ], - "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } @@ -9622,8 +12611,6 @@ "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", - "dev": true, - "license": "Apache-2.0", "dependencies": { "tslib": "^2.1.0" } @@ -9633,7 +12620,6 @@ "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", @@ -9652,7 +12638,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -9666,22 +12651,19 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] }, "node_modules/safe-identifier": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/safe-identifier/-/safe-identifier-0.4.2.tgz", "integrity": "sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/safe-push-apply": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", "dev": true, - "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "isarray": "^2.0.5" @@ -9698,7 +12680,6 @@ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -9706,77 +12687,28 @@ }, "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safer-buffer": { - "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/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - } - }, - "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", - "devOptional": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", - "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "mime-types": "^3.0.1", - "ms": "^2.1.3", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "statuses": "^2.0.1" - }, - "engines": { - "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/serve-static": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", - "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "license": "MIT", - "dependencies": { - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "parseurl": "^1.3.3", - "send": "^1.2.0" - }, - "engines": { - "node": ">= 18" + "bin": { + "semver": "bin/semver.js" } }, "node_modules/set-function-length": { @@ -9784,7 +12716,6 @@ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dev": true, - "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -9802,7 +12733,6 @@ "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dev": true, - "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -9818,7 +12748,6 @@ "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", "dev": true, - "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", "es-errors": "^1.3.0", @@ -9828,24 +12757,16 @@ "node": ">= 0.4" } }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true, - "license": "ISC" - }, "node_modules/sharp": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.1.tgz", - "integrity": "sha512-1j0w61+eVxu7DawFJtnfYcvSv6qPFvfTaqzTQ2BLknVhHTwGS8sc63ZBF4rzkWMBVKybo4S5OBtDdZahh2A1xg==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.2.tgz", + "integrity": "sha512-lszvBmB9QURERtyKT2bNmsgxXK0ShJrL/fvqlonCo7e6xBF8nT8xU6pW+PMIbLsz0RxQk3rgH9kd8UmvOzlMJg==", "hasInstallScript": true, - "license": "Apache-2.0", "optional": true, "dependencies": { "color": "^4.2.3", - "detect-libc": "^2.0.3", - "semver": "^7.7.1" + "detect-libc": "^2.0.4", + "semver": "^7.7.2" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" @@ -9854,8 +12775,8 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.1", - "@img/sharp-darwin-x64": "0.34.1", + "@img/sharp-darwin-arm64": "0.34.2", + "@img/sharp-darwin-x64": "0.34.2", "@img/sharp-libvips-darwin-arm64": "1.1.0", "@img/sharp-libvips-darwin-x64": "1.1.0", "@img/sharp-libvips-linux-arm": "1.1.0", @@ -9865,15 +12786,28 @@ "@img/sharp-libvips-linux-x64": "1.1.0", "@img/sharp-libvips-linuxmusl-arm64": "1.1.0", "@img/sharp-libvips-linuxmusl-x64": "1.1.0", - "@img/sharp-linux-arm": "0.34.1", - "@img/sharp-linux-arm64": "0.34.1", - "@img/sharp-linux-s390x": "0.34.1", - "@img/sharp-linux-x64": "0.34.1", - "@img/sharp-linuxmusl-arm64": "0.34.1", - "@img/sharp-linuxmusl-x64": "0.34.1", - "@img/sharp-wasm32": "0.34.1", - "@img/sharp-win32-ia32": "0.34.1", - "@img/sharp-win32-x64": "0.34.1" + "@img/sharp-linux-arm": "0.34.2", + "@img/sharp-linux-arm64": "0.34.2", + "@img/sharp-linux-s390x": "0.34.2", + "@img/sharp-linux-x64": "0.34.2", + "@img/sharp-linuxmusl-arm64": "0.34.2", + "@img/sharp-linuxmusl-x64": "0.34.2", + "@img/sharp-wasm32": "0.34.2", + "@img/sharp-win32-arm64": "0.34.2", + "@img/sharp-win32-ia32": "0.34.2", + "@img/sharp-win32-x64": "0.34.2" + } + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/shebang-command": { @@ -9881,7 +12815,6 @@ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, - "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -9894,7 +12827,6 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -9904,7 +12836,6 @@ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dev": true, - "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", @@ -9924,7 +12855,6 @@ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", "dev": true, - "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" @@ -9941,7 +12871,6 @@ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -9960,7 +12889,6 @@ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -9976,62 +12904,123 @@ } }, "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "node_modules/simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "license": "MIT", "optional": true, "dependencies": { "is-arrayish": "^0.3.1" } }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "optional": true + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "dev": true, + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/socks": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.5.tgz", + "integrity": "sha512-iF+tNDQla22geJdTyJB1wM/qrX9DMRwWrciEPwWLPRWAUEM8sQiyxgckLxWT1f7+9VabJS0jTGGr4QgBuvi6Ww==", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "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==", - "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, + "node_modules/spawn-command": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz", + "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==" + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" + }, "node_modules/stable-hash": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", - "dev": true, - "license": "MIT" + "dev": true }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", "dev": true, - "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, "engines": { - "node": ">= 0.8" + "node": ">= 0.4" } }, "node_modules/streamsearch": { @@ -10042,12 +13031,18 @@ "node": ">=10.0.0" } }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -10063,7 +13058,6 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, - "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -10073,72 +13067,22 @@ "node": ">=8" } }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/string-width-cjs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } + "dev": true }, "node_modules/string-width/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/string.prototype.includes": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -10153,7 +13097,6 @@ "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", @@ -10181,7 +13124,6 @@ "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", "dev": true, - "license": "MIT", "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" @@ -10192,7 +13134,6 @@ "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", @@ -10214,7 +13155,6 @@ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", @@ -10233,7 +13173,6 @@ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -10247,16 +13186,14 @@ } }, "node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "dev": true, - "license": "MIT", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dependencies": { - "ansi-regex": "^2.0.0" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/strip-ansi-cjs": { @@ -10265,7 +13202,6 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -10273,22 +13209,11 @@ "node": ">=8" } }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } @@ -10298,7 +13223,6 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" }, @@ -10306,11 +13230,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strtok3": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.1.tgz", + "integrity": "sha512-3JWEZM6mfix/GCJBBUrkA8p2Id2pBkyTkVCJKto55w080QBKZ+8R171fGrbiSp+yMO/u6F8/yUh7K4V9K+YCnw==", + "dependencies": { + "@tokenizer/token": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/styled-jsx": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", - "license": "MIT", "dependencies": { "client-only": "0.0.1" }, @@ -10333,8 +13271,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -10347,7 +13283,6 @@ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -10355,29 +13290,66 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/svg-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", + "dev": true + }, + "node_modules/svgo": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", + "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", + "dev": true, + "dependencies": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^5.1.0", + "css-tree": "^2.3.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.0.0" + }, + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/svgo/node_modules/commander": { + "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, + "engines": { + "node": ">= 10" + } + }, "node_modules/tailwind-merge": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.2.0.tgz", - "integrity": "sha512-FQT/OVqCD+7edmmJpsgCsY820RTD5AkBryuG5IUqR5YQZSdj5xlH5nLgH7YPths7WsLPSpSBNneJdM8aS8aeFA==", - "license": "MIT", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.1.tgz", + "integrity": "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==", "funding": { "type": "github", "url": "https://github.com/sponsors/dcastil" } }, "node_modules/tailwindcss": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.6.tgz", - "integrity": "sha512-j0cGLTreM6u4OWzBeLBpycK0WIh8w7kSwcUsQZoGLHZ7xDTdM69lN64AgoIEEwFi0tnhs4wSykUa5YWxAzgFYg==", - "dev": true, - "license": "MIT" + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.10.tgz", + "integrity": "sha512-P3nr6WkvKV/ONsTzj6Gb57sWPMX29EPNPopo7+FcpkQaNsrNpZ1pv8QmrYI2RqEKD7mlGqLnGovlcYnBK0IqUA==", + "dev": true }, "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } @@ -10387,7 +13359,6 @@ "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", "dev": true, - "license": "ISC", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", @@ -10400,19 +13371,40 @@ "node": ">=18" } }, + "node_modules/tar/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "engines": { + "node": ">=18" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true, - "license": "MIT" + "dev": true + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" }, "node_modules/tinyglobby": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", - "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", "dev": true, - "license": "MIT", "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" @@ -10425,11 +13417,10 @@ } }, "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.4.4", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", - "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", "dev": true, - "license": "MIT", "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -10444,7 +13435,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, - "license": "MIT", "engines": { "node": ">=12" }, @@ -10452,12 +13442,22 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -10465,14 +13465,33 @@ "node": ">=8.0" } }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true, - "license": "MIT", + "node_modules/token-types": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.0.0.tgz", + "integrity": "sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA==", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, "engines": { - "node": ">=0.6" + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "bin": { + "tree-kill": "cli.js" } }, "node_modules/ts-api-utils": { @@ -10480,7 +13499,6 @@ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=18.12" }, @@ -10493,7 +13511,6 @@ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", "dev": true, - "license": "MIT", "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", @@ -10501,18 +13518,28 @@ "strip-bom": "^3.0.0" } }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/tw-animate-css": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.2.9.tgz", - "integrity": "sha512-9O4k1at9pMQff9EAcCEuy1UNO43JmaPQvq+0lwza9Y0BQ6LB38NiMj+qHqjoQf40355MX+gs6wtlR6H9WsSXFg==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.3.4.tgz", + "integrity": "sha512-dd1Ht6/YQHcNbq0znIT6dG8uhO7Ce+VIIhZUhjsryXsMPJQz3bZg7Q2eNzLwipb25bRZslGb2myio5mScd1TFg==", "dev": true, - "license": "MIT", "funding": { "url": "https://github.com/sponsors/Wombosvideo" } @@ -10522,7 +13549,6 @@ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, - "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" }, @@ -10531,11 +13557,9 @@ } }, "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "license": "(MIT OR CC0-1.0)", + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "engines": { "node": ">=10" }, @@ -10543,27 +13567,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/type-is": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", - "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", - "dev": true, - "license": "MIT", - "dependencies": { - "content-type": "^1.0.5", - "media-typer": "^1.1.0", - "mime-types": "^3.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/typed-array-buffer": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", @@ -10578,7 +13586,6 @@ "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "for-each": "^0.3.3", @@ -10598,7 +13605,6 @@ "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", "dev": true, - "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", @@ -10620,7 +13626,6 @@ "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", "dev": true, - "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", @@ -10641,7 +13646,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, - "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -10650,12 +13654,33 @@ "node": ">=14.17" } }, + "node_modules/uid": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz", + "integrity": "sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==", + "dependencies": { + "@lukeed/csprng": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/uint8array-extras": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.4.0.tgz", + "integrity": "sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/unbox-primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", @@ -10670,53 +13695,121 @@ } }, "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "dev": true + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", "dev": true, - "license": "MIT" + "engines": { + "node": ">=4" + } }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", "dev": true, - "license": "MIT", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, "engines": { - "node": ">= 0.8" + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "engines": { + "node": ">= 10.0.0" } }, "node_modules/unrs-resolver": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.7.2.tgz", - "integrity": "sha512-BBKpaylOW8KbHsu378Zky/dGh4ckT/4NW/0SHRABdqRLcQJ2dAOjDo9g97p04sWflm0kqPqpUatxReNV/dqI5A==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.9.0.tgz", + "integrity": "sha512-wqaRu4UnzBD2ABTC1kLfBjAqIDZ5YUTr/MLGa7By47JV1bJDSW7jq/ZSLigB7enLe7ubNaJhtnBXgrc/50cEhg==", "dev": true, "hasInstallScript": true, - "license": "MIT", "dependencies": { "napi-postinstall": "^0.2.2" }, "funding": { - "url": "https://github.com/sponsors/JounQin" + "url": "https://opencollective.com/unrs-resolver" }, "optionalDependencies": { - "@unrs/resolver-binding-darwin-arm64": "1.7.2", - "@unrs/resolver-binding-darwin-x64": "1.7.2", - "@unrs/resolver-binding-freebsd-x64": "1.7.2", - "@unrs/resolver-binding-linux-arm-gnueabihf": "1.7.2", - "@unrs/resolver-binding-linux-arm-musleabihf": "1.7.2", - "@unrs/resolver-binding-linux-arm64-gnu": "1.7.2", - "@unrs/resolver-binding-linux-arm64-musl": "1.7.2", - "@unrs/resolver-binding-linux-ppc64-gnu": "1.7.2", - "@unrs/resolver-binding-linux-riscv64-gnu": "1.7.2", - "@unrs/resolver-binding-linux-riscv64-musl": "1.7.2", - "@unrs/resolver-binding-linux-s390x-gnu": "1.7.2", - "@unrs/resolver-binding-linux-x64-gnu": "1.7.2", - "@unrs/resolver-binding-linux-x64-musl": "1.7.2", - "@unrs/resolver-binding-wasm32-wasi": "1.7.2", - "@unrs/resolver-binding-win32-arm64-msvc": "1.7.2", - "@unrs/resolver-binding-win32-ia32-msvc": "1.7.2", - "@unrs/resolver-binding-win32-x64-msvc": "1.7.2" + "@unrs/resolver-binding-android-arm-eabi": "1.9.0", + "@unrs/resolver-binding-android-arm64": "1.9.0", + "@unrs/resolver-binding-darwin-arm64": "1.9.0", + "@unrs/resolver-binding-darwin-x64": "1.9.0", + "@unrs/resolver-binding-freebsd-x64": "1.9.0", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.9.0", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.9.0", + "@unrs/resolver-binding-linux-arm64-gnu": "1.9.0", + "@unrs/resolver-binding-linux-arm64-musl": "1.9.0", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.9.0", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.9.0", + "@unrs/resolver-binding-linux-riscv64-musl": "1.9.0", + "@unrs/resolver-binding-linux-s390x-gnu": "1.9.0", + "@unrs/resolver-binding-linux-x64-gnu": "1.9.0", + "@unrs/resolver-binding-linux-x64-musl": "1.9.0", + "@unrs/resolver-binding-wasm32-wasi": "1.9.0", + "@unrs/resolver-binding-win32-arm64-msvc": "1.9.0", + "@unrs/resolver-binding-win32-ia32-msvc": "1.9.0", + "@unrs/resolver-binding-win32-x64-msvc": "1.9.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, "node_modules/uri-js": { @@ -10724,7 +13817,6 @@ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } @@ -10733,7 +13825,6 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", - "license": "MIT", "dependencies": { "tslib": "^2.0.0" }, @@ -10754,7 +13845,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", - "license": "MIT", "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" @@ -10776,19 +13866,26 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", - "license": "MIT", "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "dev": true, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "license": "MIT", - "engines": { - "node": ">= 0.8" + "bin": { + "uuid": "dist/esm/bin/uuid" } }, "node_modules/vue-eslint-parser": { @@ -10796,7 +13893,6 @@ "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz", "integrity": "sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==", "dev": true, - "license": "MIT", "dependencies": { "debug": "^4.3.4", "eslint-scope": "^7.1.1", @@ -10821,7 +13917,6 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -10833,25 +13928,11 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/vue-eslint-parser/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/vue-eslint-parser/node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", @@ -10864,12 +13945,45 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/vue-eslint-parser/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -10885,7 +13999,6 @@ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", "dev": true, - "license": "MIT", "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", @@ -10905,7 +14018,6 @@ "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", "dev": true, - "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", @@ -10933,7 +14045,6 @@ "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, - "license": "MIT", "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", @@ -10952,7 +14063,6 @@ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", "dev": true, - "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", @@ -10974,27 +14084,21 @@ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": ">=8" } }, "node_modules/wrap-ansi-cjs": { @@ -11003,7 +14107,6 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -11016,106 +14119,49 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", "engines": { "node": ">=10" } }, "node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true }, "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dependencies": { - "cliui": "^8.0.1", + "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.3", + "string-width": "^4.2.0", "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" + "yargs-parser": "^20.2.2" }, "engines": { - "node": ">=12" + "node": ">=10" } }, "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "engines": { - "node": ">=12" + "node": ">=10" } }, "node_modules/yocto-queue": { @@ -11123,7 +14169,6 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" }, @@ -11132,30 +14177,18 @@ } }, "node_modules/zod": { - "version": "3.24.4", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.4.tgz", - "integrity": "sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==", - "dev": true, + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.0.5.tgz", + "integrity": "sha512-/5UuuRPStvHXu7RS+gmvRf4NXrNxpSllGwDnCBcJZtQsKrviYXm54yDGV2KYNLT5kq0lHGcl7lqWJLgSaG+tgA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" } }, - "node_modules/zod-to-json-schema": { - "version": "3.24.5", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz", - "integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==", - "dev": true, - "license": "ISC", - "peerDependencies": { - "zod": "^3.24.1" - } - }, "node_modules/zustand": { - "version": "4.5.6", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.6.tgz", - "integrity": "sha512-ibr/n1hBzLLj5Y+yUcU7dYw8p6WnIVzdJbnX+1YpaScvZVF2ziugqHs+LAmHw4lWO9c/zRj+K1ncgWDQuthEdQ==", - "license": "MIT", + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz", + "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", "dependencies": { "use-sync-external-store": "^1.2.2" }, diff --git a/client/package.json b/client/package.json index ae03a797..5188ec3b 100644 --- a/client/package.json +++ b/client/package.json @@ -7,35 +7,59 @@ "build": "next build", "start": "next start", "lint": "next lint", - "format": "prettier --write \"src/**/*.{ts,tsx}\"" + "format": "prettier --write \"src/**/*.{ts,tsx}\"", + "openapi:generate:main": "openapi-generator-cli generate -i http://server:9091/v3/api-docs -g typescript-axios -o src/api/main/generated", + "openapi:generate:genai": "openapi-generator-cli generate -i http://genai:8000/v3/api-docs -g typescript-axios -o src/api/genai/generated", + "openapi:generate:realtime": "openapi-generator-cli generate -i http://realtime:9090/swagger/doc.json -g typescript-axios -o src/api/realtime/generated" }, "dependencies": { - "@radix-ui/react-slot": "^1.2.2", + "@openapitools/openapi-generator-cli": "^2.20.2", + "@radix-ui/react-alert-dialog": "^1.1.14", + "@radix-ui/react-avatar": "^1.1.10", + "@radix-ui/react-dialog": "^1.1.14", + "@radix-ui/react-dropdown-menu": "^2.1.15", + "@radix-ui/react-popover": "^1.1.14", + "@radix-ui/react-select": "^2.2.5", + "@radix-ui/react-separator": "^1.1.7", + "@radix-ui/react-slider": "^1.3.5", + "@radix-ui/react-slot": "^1.2.3", + "@radix-ui/react-tabs": "^1.1.12", + "@radix-ui/react-tooltip": "^1.2.7", "@radix-ui/themes": "^3.2.1", - "@xyflow/react": "^12.6.1", + "@xyflow/react": "^12.7.0", + "axios": "^1.10.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", - "lucide-react": "^0.509.0", - "next": "15.3.2", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "tailwind-merge": "^3.2.0" + "framer-motion": "^12.23.6", + "jsonwebtoken": "^9.0.2", + "lucide-react": "^0.515.0", + "next": "15.3.3", + "next-auth": "^4.24.11", + "react": "^19.1.0", + "react-colorful": "^5.6.1", + "react-dom": "^19.1.0", + "tailwind-merge": "^3.3.1", + "uuid": "^11.1.0", + "zod": "^4.0.5" }, "devDependencies": { "@eslint/eslintrc": "^3", + "@svgr/webpack": "^8.1.0", "@tailwindcss/postcss": "^4", - "@tanstack/eslint-plugin-query": "^5.74.7", - "@tanstack/react-query-devtools": "^5.75.7", - "@types/node": "^20", + "@tanstack/eslint-plugin-query": "^5.78.0", + "@tanstack/react-query-devtools": "^5.80.7", + "@types/jsonwebtoken": "^9.0.9", + "@types/node": "^24", "@types/react": "^19", "@types/react-dom": "^19", - "eslint": "^9.26.0", - "eslint-config-next": "15.3.2", + "eslint": "^9.29.0", + "eslint-config-next": "15.3.3", "prettier": "^3.5.3", "prettier-eslint": "^16.4.2", "prettier-eslint-cli": "^8.0.1", + "prettier-plugin-tailwindcss": "^0.6.13", "tailwindcss": "^4", - "tw-animate-css": "^1.2.9", + "tw-animate-css": "^1.3.4", "typescript": "^5" } } diff --git a/genai/app/db/__init__.py b/client/public/.gitkeep similarity index 100% rename from genai/app/db/__init__.py rename to client/public/.gitkeep diff --git a/client/public/favicon.svg b/client/public/favicon.svg new file mode 100644 index 00000000..8ba9fa72 --- /dev/null +++ b/client/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/api/genai/generated/.gitignore b/client/src/api/genai/generated/.gitignore new file mode 100644 index 00000000..149b5765 --- /dev/null +++ b/client/src/api/genai/generated/.gitignore @@ -0,0 +1,4 @@ +wwwroot/*.js +node_modules +typings +dist diff --git a/client/src/api/genai/generated/.npmignore b/client/src/api/genai/generated/.npmignore new file mode 100644 index 00000000..999d88df --- /dev/null +++ b/client/src/api/genai/generated/.npmignore @@ -0,0 +1 @@ +# empty npmignore to ensure all required files (e.g., in the dist folder) are published by npm \ No newline at end of file diff --git a/client/src/api/genai/generated/.openapi-generator-ignore b/client/src/api/genai/generated/.openapi-generator-ignore new file mode 100644 index 00000000..7484ee59 --- /dev/null +++ b/client/src/api/genai/generated/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/client/src/api/genai/generated/.openapi-generator/FILES b/client/src/api/genai/generated/.openapi-generator/FILES new file mode 100644 index 00000000..0ca40080 --- /dev/null +++ b/client/src/api/genai/generated/.openapi-generator/FILES @@ -0,0 +1,14 @@ +.gitignore +.npmignore +api.ts +base.ts +common.ts +configuration.ts +docs/DefaultApi.md +docs/HTTPValidationError.md +docs/TextRequest.md +docs/TextResponse.md +docs/ValidationError.md +docs/ValidationErrorLocInner.md +git_push.sh +index.ts diff --git a/client/src/api/genai/generated/.openapi-generator/VERSION b/client/src/api/genai/generated/.openapi-generator/VERSION new file mode 100644 index 00000000..eb1dc6a5 --- /dev/null +++ b/client/src/api/genai/generated/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.13.0 diff --git a/client/src/api/genai/generated/api.ts b/client/src/api/genai/generated/api.ts new file mode 100644 index 00000000..74d24aba --- /dev/null +++ b/client/src/api/genai/generated/api.ts @@ -0,0 +1,679 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * LLM Service + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import type { Configuration } from "./configuration"; +import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from "axios"; +import globalAxios from "axios"; +// Some imports not used depending on template conditions +// @ts-ignore +import { + DUMMY_BASE_URL, + assertParamExists, + setApiKeyToObject, + setBasicAuthToObject, + setBearerAuthToObject, + setOAuthToObject, + setSearchParams, + serializeDataIfNeeded, + toPathString, + createRequestFunction, +} from "./common"; +import type { RequestArgs } from "./base"; +// @ts-ignore +import { + BASE_PATH, + COLLECTION_FORMATS, + BaseAPI, + RequiredError, + operationServerMap, +} from "./base"; + +/** + * + * @export + * @interface HTTPValidationError + */ +export interface HTTPValidationError { + /** + * + * @type {Array} + * @memberof HTTPValidationError + */ + detail?: Array; +} +/** + * + * @export + * @interface TextRequest + */ +export interface TextRequest { + /** + * + * @type {string} + * @memberof TextRequest + */ + user_text: string; +} +/** + * + * @export + * @interface TextResponse + */ +export interface TextResponse { + /** + * + * @type {string} + * @memberof TextResponse + */ + llm_response: string; +} +/** + * + * @export + * @interface ValidationError + */ +export interface ValidationError { + /** + * + * @type {Array} + * @memberof ValidationError + */ + loc: Array; + /** + * + * @type {string} + * @memberof ValidationError + */ + msg: string; + /** + * + * @type {string} + * @memberof ValidationError + */ + type: string; +} +/** + * + * @export + * @interface ValidationErrorLocInner + */ +export interface ValidationErrorLocInner {} + +/** + * DefaultApi - axios parameter creator + * @export + */ +export const DefaultApiAxiosParamCreator = function ( + configuration?: Configuration, +) { + return { + /** + * + * @summary Complete Text + * @param {TextRequest} textRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + completeTextCompletionPost: async ( + textRequest: TextRequest, + options: RawAxiosRequestConfig = {}, + ): Promise => { + // verify required parameter 'textRequest' is not null or undefined + assertParamExists( + "completeTextCompletionPost", + "textRequest", + textRequest, + ); + const localVarPath = `/completion`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "POST", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + localVarHeaderParameter["Content-Type"] = "application/json"; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + localVarRequestOptions.data = serializeDataIfNeeded( + textRequest, + localVarRequestOptions, + configuration, + ); + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary Health Check + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + healthCheckHealthGet: async ( + options: RawAxiosRequestConfig = {}, + ): Promise => { + const localVarPath = `/health`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "GET", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Endpoint that serves Prometheus metrics. + * @summary Metrics + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + metricsMetricsGet: async ( + options: RawAxiosRequestConfig = {}, + ): Promise => { + const localVarPath = `/metrics`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "GET", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary Rephrase Text + * @param {TextRequest} textRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + rephraseTextRephrasePost: async ( + textRequest: TextRequest, + options: RawAxiosRequestConfig = {}, + ): Promise => { + // verify required parameter 'textRequest' is not null or undefined + assertParamExists("rephraseTextRephrasePost", "textRequest", textRequest); + const localVarPath = `/rephrase`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "POST", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + localVarHeaderParameter["Content-Type"] = "application/json"; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + localVarRequestOptions.data = serializeDataIfNeeded( + textRequest, + localVarRequestOptions, + configuration, + ); + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary Summarize Text + * @param {TextRequest} textRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + summarizeTextSummarizationPost: async ( + textRequest: TextRequest, + options: RawAxiosRequestConfig = {}, + ): Promise => { + // verify required parameter 'textRequest' is not null or undefined + assertParamExists( + "summarizeTextSummarizationPost", + "textRequest", + textRequest, + ); + const localVarPath = `/summarization`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "POST", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + localVarHeaderParameter["Content-Type"] = "application/json"; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + localVarRequestOptions.data = serializeDataIfNeeded( + textRequest, + localVarRequestOptions, + configuration, + ); + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + }; +}; + +/** + * DefaultApi - functional programming interface + * @export + */ +export const DefaultApiFp = function (configuration?: Configuration) { + const localVarAxiosParamCreator = DefaultApiAxiosParamCreator(configuration); + return { + /** + * + * @summary Complete Text + * @param {TextRequest} textRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async completeTextCompletionPost( + textRequest: TextRequest, + options?: RawAxiosRequestConfig, + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.completeTextCompletionPost( + textRequest, + options, + ); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = + operationServerMap["DefaultApi.completeTextCompletionPost"]?.[ + localVarOperationServerIndex + ]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary Health Check + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async healthCheckHealthGet( + options?: RawAxiosRequestConfig, + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.healthCheckHealthGet(options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = + operationServerMap["DefaultApi.healthCheckHealthGet"]?.[ + localVarOperationServerIndex + ]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Endpoint that serves Prometheus metrics. + * @summary Metrics + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async metricsMetricsGet( + options?: RawAxiosRequestConfig, + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.metricsMetricsGet(options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = + operationServerMap["DefaultApi.metricsMetricsGet"]?.[ + localVarOperationServerIndex + ]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary Rephrase Text + * @param {TextRequest} textRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async rephraseTextRephrasePost( + textRequest: TextRequest, + options?: RawAxiosRequestConfig, + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.rephraseTextRephrasePost( + textRequest, + options, + ); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = + operationServerMap["DefaultApi.rephraseTextRephrasePost"]?.[ + localVarOperationServerIndex + ]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary Summarize Text + * @param {TextRequest} textRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async summarizeTextSummarizationPost( + textRequest: TextRequest, + options?: RawAxiosRequestConfig, + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.summarizeTextSummarizationPost( + textRequest, + options, + ); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = + operationServerMap["DefaultApi.summarizeTextSummarizationPost"]?.[ + localVarOperationServerIndex + ]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, localVarOperationServerBasePath || basePath); + }, + }; +}; + +/** + * DefaultApi - factory interface + * @export + */ +export const DefaultApiFactory = function ( + configuration?: Configuration, + basePath?: string, + axios?: AxiosInstance, +) { + const localVarFp = DefaultApiFp(configuration); + return { + /** + * + * @summary Complete Text + * @param {TextRequest} textRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + completeTextCompletionPost( + textRequest: TextRequest, + options?: RawAxiosRequestConfig, + ): AxiosPromise { + return localVarFp + .completeTextCompletionPost(textRequest, options) + .then((request) => request(axios, basePath)); + }, + /** + * + * @summary Health Check + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + healthCheckHealthGet(options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp + .healthCheckHealthGet(options) + .then((request) => request(axios, basePath)); + }, + /** + * Endpoint that serves Prometheus metrics. + * @summary Metrics + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + metricsMetricsGet(options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp + .metricsMetricsGet(options) + .then((request) => request(axios, basePath)); + }, + /** + * + * @summary Rephrase Text + * @param {TextRequest} textRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + rephraseTextRephrasePost( + textRequest: TextRequest, + options?: RawAxiosRequestConfig, + ): AxiosPromise { + return localVarFp + .rephraseTextRephrasePost(textRequest, options) + .then((request) => request(axios, basePath)); + }, + /** + * + * @summary Summarize Text + * @param {TextRequest} textRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + summarizeTextSummarizationPost( + textRequest: TextRequest, + options?: RawAxiosRequestConfig, + ): AxiosPromise { + return localVarFp + .summarizeTextSummarizationPost(textRequest, options) + .then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * DefaultApi - object-oriented interface + * @export + * @class DefaultApi + * @extends {BaseAPI} + */ +export class DefaultApi extends BaseAPI { + /** + * + * @summary Complete Text + * @param {TextRequest} textRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public completeTextCompletionPost( + textRequest: TextRequest, + options?: RawAxiosRequestConfig, + ) { + return DefaultApiFp(this.configuration) + .completeTextCompletionPost(textRequest, options) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary Health Check + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public healthCheckHealthGet(options?: RawAxiosRequestConfig) { + return DefaultApiFp(this.configuration) + .healthCheckHealthGet(options) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * Endpoint that serves Prometheus metrics. + * @summary Metrics + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public metricsMetricsGet(options?: RawAxiosRequestConfig) { + return DefaultApiFp(this.configuration) + .metricsMetricsGet(options) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary Rephrase Text + * @param {TextRequest} textRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public rephraseTextRephrasePost( + textRequest: TextRequest, + options?: RawAxiosRequestConfig, + ) { + return DefaultApiFp(this.configuration) + .rephraseTextRephrasePost(textRequest, options) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary Summarize Text + * @param {TextRequest} textRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public summarizeTextSummarizationPost( + textRequest: TextRequest, + options?: RawAxiosRequestConfig, + ) { + return DefaultApiFp(this.configuration) + .summarizeTextSummarizationPost(textRequest, options) + .then((request) => request(this.axios, this.basePath)); + } +} diff --git a/client/src/api/genai/generated/base.ts b/client/src/api/genai/generated/base.ts new file mode 100644 index 00000000..c63ab52f --- /dev/null +++ b/client/src/api/genai/generated/base.ts @@ -0,0 +1,91 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * LLM Service + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import type { Configuration } from "./configuration"; +// Some imports not used depending on template conditions +// @ts-ignore +import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from "axios"; +import globalAxios from "axios"; + +export const BASE_PATH = "http://localhost:8000".replace(/\/+$/, ""); + +/** + * + * @export + */ +export const COLLECTION_FORMATS = { + csv: ",", + ssv: " ", + tsv: "\t", + pipes: "|", +}; + +/** + * + * @export + * @interface RequestArgs + */ +export interface RequestArgs { + url: string; + options: RawAxiosRequestConfig; +} + +/** + * + * @export + * @class BaseAPI + */ +export class BaseAPI { + protected configuration: Configuration | undefined; + + constructor( + configuration?: Configuration, + protected basePath: string = BASE_PATH, + protected axios: AxiosInstance = globalAxios, + ) { + if (configuration) { + this.configuration = configuration; + this.basePath = configuration.basePath ?? basePath; + } + } +} + +/** + * + * @export + * @class RequiredError + * @extends {Error} + */ +export class RequiredError extends Error { + constructor( + public field: string, + msg?: string, + ) { + super(msg); + this.name = "RequiredError"; + } +} + +interface ServerMap { + [key: string]: { + url: string; + description: string; + }[]; +} + +/** + * + * @export + */ +export const operationServerMap: ServerMap = {}; diff --git a/client/src/api/genai/generated/common.ts b/client/src/api/genai/generated/common.ts new file mode 100644 index 00000000..c6747f76 --- /dev/null +++ b/client/src/api/genai/generated/common.ts @@ -0,0 +1,202 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * LLM Service + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import type { Configuration } from "./configuration"; +import type { RequestArgs } from "./base"; +import type { AxiosInstance, AxiosResponse } from "axios"; +import { RequiredError } from "./base"; + +/** + * + * @export + */ +export const DUMMY_BASE_URL = "https://example.com"; + +/** + * + * @throws {RequiredError} + * @export + */ +export const assertParamExists = function ( + functionName: string, + paramName: string, + paramValue: unknown, +) { + if (paramValue === null || paramValue === undefined) { + throw new RequiredError( + paramName, + `Required parameter ${paramName} was null or undefined when calling ${functionName}.`, + ); + } +}; + +/** + * + * @export + */ +export const setApiKeyToObject = async function ( + object: any, + keyParamName: string, + configuration?: Configuration, +) { + if (configuration && configuration.apiKey) { + const localVarApiKeyValue = + typeof configuration.apiKey === "function" + ? await configuration.apiKey(keyParamName) + : await configuration.apiKey; + object[keyParamName] = localVarApiKeyValue; + } +}; + +/** + * + * @export + */ +export const setBasicAuthToObject = function ( + object: any, + configuration?: Configuration, +) { + if (configuration && (configuration.username || configuration.password)) { + object["auth"] = { + username: configuration.username, + password: configuration.password, + }; + } +}; + +/** + * + * @export + */ +export const setBearerAuthToObject = async function ( + object: any, + configuration?: Configuration, +) { + if (configuration && configuration.accessToken) { + const accessToken = + typeof configuration.accessToken === "function" + ? await configuration.accessToken() + : await configuration.accessToken; + object["Authorization"] = "Bearer " + accessToken; + } +}; + +/** + * + * @export + */ +export const setOAuthToObject = async function ( + object: any, + name: string, + scopes: string[], + configuration?: Configuration, +) { + if (configuration && configuration.accessToken) { + const localVarAccessTokenValue = + typeof configuration.accessToken === "function" + ? await configuration.accessToken(name, scopes) + : await configuration.accessToken; + object["Authorization"] = "Bearer " + localVarAccessTokenValue; + } +}; + +function setFlattenedQueryParams( + urlSearchParams: URLSearchParams, + parameter: any, + key: string = "", +): void { + if (parameter == null) return; + if (typeof parameter === "object") { + if (Array.isArray(parameter)) { + (parameter as any[]).forEach((item) => + setFlattenedQueryParams(urlSearchParams, item, key), + ); + } else { + Object.keys(parameter).forEach((currentKey) => + setFlattenedQueryParams( + urlSearchParams, + parameter[currentKey], + `${key}${key !== "" ? "." : ""}${currentKey}`, + ), + ); + } + } else { + if (urlSearchParams.has(key)) { + urlSearchParams.append(key, parameter); + } else { + urlSearchParams.set(key, parameter); + } + } +} + +/** + * + * @export + */ +export const setSearchParams = function (url: URL, ...objects: any[]) { + const searchParams = new URLSearchParams(url.search); + setFlattenedQueryParams(searchParams, objects); + url.search = searchParams.toString(); +}; + +/** + * + * @export + */ +export const serializeDataIfNeeded = function ( + value: any, + requestOptions: any, + configuration?: Configuration, +) { + const nonString = typeof value !== "string"; + const needsSerialization = + nonString && configuration && configuration.isJsonMime + ? configuration.isJsonMime(requestOptions.headers["Content-Type"]) + : nonString; + return needsSerialization + ? JSON.stringify(value !== undefined ? value : {}) + : value || ""; +}; + +/** + * + * @export + */ +export const toPathString = function (url: URL) { + return url.pathname + url.search + url.hash; +}; + +/** + * + * @export + */ +export const createRequestFunction = function ( + axiosArgs: RequestArgs, + globalAxios: AxiosInstance, + BASE_PATH: string, + configuration?: Configuration, +) { + return >( + axios: AxiosInstance = globalAxios, + basePath: string = BASE_PATH, + ) => { + const axiosRequestArgs = { + ...axiosArgs.options, + url: + (axios.defaults.baseURL ? "" : (configuration?.basePath ?? basePath)) + + axiosArgs.url, + }; + return axios.request(axiosRequestArgs); + }; +}; diff --git a/client/src/api/genai/generated/configuration.ts b/client/src/api/genai/generated/configuration.ts new file mode 100644 index 00000000..3cb443df --- /dev/null +++ b/client/src/api/genai/generated/configuration.ts @@ -0,0 +1,137 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * LLM Service + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +export interface ConfigurationParameters { + apiKey?: + | string + | Promise + | ((name: string) => string) + | ((name: string) => Promise); + username?: string; + password?: string; + accessToken?: + | string + | Promise + | ((name?: string, scopes?: string[]) => string) + | ((name?: string, scopes?: string[]) => Promise); + basePath?: string; + serverIndex?: number; + baseOptions?: any; + formDataCtor?: new () => any; +} + +export class Configuration { + /** + * parameter for apiKey security + * @param name security name + * @memberof Configuration + */ + apiKey?: + | string + | Promise + | ((name: string) => string) + | ((name: string) => Promise); + /** + * parameter for basic security + * + * @type {string} + * @memberof Configuration + */ + username?: string; + /** + * parameter for basic security + * + * @type {string} + * @memberof Configuration + */ + password?: string; + /** + * parameter for oauth2 security + * @param name security name + * @param scopes oauth2 scope + * @memberof Configuration + */ + accessToken?: + | string + | Promise + | ((name?: string, scopes?: string[]) => string) + | ((name?: string, scopes?: string[]) => Promise); + /** + * override base path + * + * @type {string} + * @memberof Configuration + */ + basePath?: string; + /** + * override server index + * + * @type {number} + * @memberof Configuration + */ + serverIndex?: number; + /** + * base options for axios calls + * + * @type {any} + * @memberof Configuration + */ + baseOptions?: any; + /** + * The FormData constructor that will be used to create multipart form data + * requests. You can inject this here so that execution environments that + * do not support the FormData class can still run the generated client. + * + * @type {new () => FormData} + */ + formDataCtor?: new () => any; + + constructor(param: ConfigurationParameters = {}) { + this.apiKey = param.apiKey; + this.username = param.username; + this.password = param.password; + this.accessToken = param.accessToken; + this.basePath = param.basePath; + this.serverIndex = param.serverIndex; + this.baseOptions = { + ...param.baseOptions, + headers: { + ...param.baseOptions?.headers, + }, + }; + this.formDataCtor = param.formDataCtor; + } + + /** + * Check if the given MIME is a JSON MIME. + * JSON MIME examples: + * application/json + * application/json; charset=UTF8 + * APPLICATION/JSON + * application/vnd.company+json + * @param mime - MIME (Multipurpose Internet Mail Extensions) + * @return True if the given MIME is JSON, false otherwise. + */ + public isJsonMime(mime: string): boolean { + const jsonMime: RegExp = new RegExp( + "^(application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(;.*)?$", + "i", + ); + return ( + mime !== null && + (jsonMime.test(mime) || + mime.toLowerCase() === "application/json-patch+json") + ); + } +} diff --git a/client/src/api/genai/generated/docs/DefaultApi.md b/client/src/api/genai/generated/docs/DefaultApi.md new file mode 100644 index 00000000..f5b18353 --- /dev/null +++ b/client/src/api/genai/generated/docs/DefaultApi.md @@ -0,0 +1,255 @@ +# DefaultApi + +All URIs are relative to *http://localhost:8000* + +|Method | HTTP request | Description| +|------------- | ------------- | -------------| +|[**completeTextCompletionPost**](#completetextcompletionpost) | **POST** /completion | Complete Text| +|[**healthCheckHealthGet**](#healthcheckhealthget) | **GET** /health | Health Check| +|[**metricsMetricsGet**](#metricsmetricsget) | **GET** /metrics | Metrics| +|[**rephraseTextRephrasePost**](#rephrasetextrephrasepost) | **POST** /rephrase | Rephrase Text| +|[**summarizeTextSummarizationPost**](#summarizetextsummarizationpost) | **POST** /summarization | Summarize Text| + +# **completeTextCompletionPost** +> TextResponse completeTextCompletionPost(textRequest) + + +### Example + +```typescript +import { + DefaultApi, + Configuration, + TextRequest +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new DefaultApi(configuration); + +let textRequest: TextRequest; // + +const { status, data } = await apiInstance.completeTextCompletionPost( + textRequest +); +``` + +### Parameters + +|Name | Type | Description | Notes| +|------------- | ------------- | ------------- | -------------| +| **textRequest** | **TextRequest**| | | + + +### Return type + +**TextResponse** + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | Successful Response | - | +|**422** | Validation Error | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **healthCheckHealthGet** +> any healthCheckHealthGet() + + +### Example + +```typescript +import { + DefaultApi, + Configuration +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new DefaultApi(configuration); + +const { status, data } = await apiInstance.healthCheckHealthGet(); +``` + +### Parameters +This endpoint does not have any parameters. + + +### Return type + +**any** + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: application/json + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | Successful Response | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **metricsMetricsGet** +> any metricsMetricsGet() + +Endpoint that serves Prometheus metrics. + +### Example + +```typescript +import { + DefaultApi, + Configuration +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new DefaultApi(configuration); + +const { status, data } = await apiInstance.metricsMetricsGet(); +``` + +### Parameters +This endpoint does not have any parameters. + + +### Return type + +**any** + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: application/json + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | Successful Response | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **rephraseTextRephrasePost** +> TextResponse rephraseTextRephrasePost(textRequest) + + +### Example + +```typescript +import { + DefaultApi, + Configuration, + TextRequest +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new DefaultApi(configuration); + +let textRequest: TextRequest; // + +const { status, data } = await apiInstance.rephraseTextRephrasePost( + textRequest +); +``` + +### Parameters + +|Name | Type | Description | Notes| +|------------- | ------------- | ------------- | -------------| +| **textRequest** | **TextRequest**| | | + + +### Return type + +**TextResponse** + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | Successful Response | - | +|**422** | Validation Error | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **summarizeTextSummarizationPost** +> TextResponse summarizeTextSummarizationPost(textRequest) + + +### Example + +```typescript +import { + DefaultApi, + Configuration, + TextRequest +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new DefaultApi(configuration); + +let textRequest: TextRequest; // + +const { status, data } = await apiInstance.summarizeTextSummarizationPost( + textRequest +); +``` + +### Parameters + +|Name | Type | Description | Notes| +|------------- | ------------- | ------------- | -------------| +| **textRequest** | **TextRequest**| | | + + +### Return type + +**TextResponse** + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | Successful Response | - | +|**422** | Validation Error | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/client/src/api/genai/generated/docs/HTTPValidationError.md b/client/src/api/genai/generated/docs/HTTPValidationError.md new file mode 100644 index 00000000..7fe160d8 --- /dev/null +++ b/client/src/api/genai/generated/docs/HTTPValidationError.md @@ -0,0 +1,20 @@ +# HTTPValidationError + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**detail** | [**Array<ValidationError>**](ValidationError.md) | | [optional] [default to undefined] + +## Example + +```typescript +import { HTTPValidationError } from './api'; + +const instance: HTTPValidationError = { + detail, +}; +``` + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/client/src/api/genai/generated/docs/TextRequest.md b/client/src/api/genai/generated/docs/TextRequest.md new file mode 100644 index 00000000..98c5e74a --- /dev/null +++ b/client/src/api/genai/generated/docs/TextRequest.md @@ -0,0 +1,20 @@ +# TextRequest + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**user_text** | **string** | | [default to undefined] + +## Example + +```typescript +import { TextRequest } from './api'; + +const instance: TextRequest = { + user_text, +}; +``` + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/client/src/api/genai/generated/docs/TextResponse.md b/client/src/api/genai/generated/docs/TextResponse.md new file mode 100644 index 00000000..9637caaf --- /dev/null +++ b/client/src/api/genai/generated/docs/TextResponse.md @@ -0,0 +1,20 @@ +# TextResponse + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**llm_response** | **string** | | [default to undefined] + +## Example + +```typescript +import { TextResponse } from './api'; + +const instance: TextResponse = { + llm_response, +}; +``` + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/client/src/api/genai/generated/docs/ValidationError.md b/client/src/api/genai/generated/docs/ValidationError.md new file mode 100644 index 00000000..d2e7ec10 --- /dev/null +++ b/client/src/api/genai/generated/docs/ValidationError.md @@ -0,0 +1,24 @@ +# ValidationError + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**loc** | [**Array<ValidationErrorLocInner>**](ValidationErrorLocInner.md) | | [default to undefined] +**msg** | **string** | | [default to undefined] +**type** | **string** | | [default to undefined] + +## Example + +```typescript +import { ValidationError } from './api'; + +const instance: ValidationError = { + loc, + msg, + type, +}; +``` + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/client/src/api/genai/generated/docs/ValidationErrorLocInner.md b/client/src/api/genai/generated/docs/ValidationErrorLocInner.md new file mode 100644 index 00000000..8d54b451 --- /dev/null +++ b/client/src/api/genai/generated/docs/ValidationErrorLocInner.md @@ -0,0 +1,18 @@ +# ValidationErrorLocInner + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +## Example + +```typescript +import { ValidationErrorLocInner } from './api'; + +const instance: ValidationErrorLocInner = { +}; +``` + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/client/src/api/genai/generated/git_push.sh b/client/src/api/genai/generated/git_push.sh new file mode 100644 index 00000000..f53a75d4 --- /dev/null +++ b/client/src/api/genai/generated/git_push.sh @@ -0,0 +1,57 @@ +#!/bin/sh +# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ +# +# Usage example: /bin/sh ./git_push.sh wing328 openapi-petstore-perl "minor update" "gitlab.com" + +git_user_id=$1 +git_repo_id=$2 +release_note=$3 +git_host=$4 + +if [ "$git_host" = "" ]; then + git_host="github.com" + echo "[INFO] No command line input provided. Set \$git_host to $git_host" +fi + +if [ "$git_user_id" = "" ]; then + git_user_id="GIT_USER_ID" + echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" +fi + +if [ "$git_repo_id" = "" ]; then + git_repo_id="GIT_REPO_ID" + echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" +fi + +if [ "$release_note" = "" ]; then + release_note="Minor update" + echo "[INFO] No command line input provided. Set \$release_note to $release_note" +fi + +# Initialize the local directory as a Git repository +git init + +# Adds the files in the local repository and stages them for commit. +git add . + +# Commits the tracked changes and prepares them to be pushed to a remote repository. +git commit -m "$release_note" + +# Sets the new remote +git_remote=$(git remote) +if [ "$git_remote" = "" ]; then # git remote not defined + + if [ "$GIT_TOKEN" = "" ]; then + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." + git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git + else + git remote add origin https://${git_user_id}:"${GIT_TOKEN}"@${git_host}/${git_user_id}/${git_repo_id}.git + fi + +fi + +git pull origin master + +# Pushes (Forces) the changes in the local repository up to the remote repository +echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git" +git push origin master 2>&1 | grep -v 'To https' diff --git a/client/src/api/genai/generated/index.ts b/client/src/api/genai/generated/index.ts new file mode 100644 index 00000000..94e65051 --- /dev/null +++ b/client/src/api/genai/generated/index.ts @@ -0,0 +1,16 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * LLM Service + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +export * from "./api"; +export * from "./configuration"; diff --git a/client/src/api/index.ts b/client/src/api/index.ts new file mode 100644 index 00000000..2c3958ef --- /dev/null +++ b/client/src/api/index.ts @@ -0,0 +1,59 @@ +import { + AccountApiFactory, + Configuration, + EdgeControllerApiFactory, + NodeControllerApiFactory, + RootApiFactory, + ViewportControllerApiFactory, + WhiteboardApiFactory, +} from "@/api/main/generated"; +import globalAxios from "axios"; +import { getSession } from "next-auth/react"; +import { DefaultApiFactory } from "./genai/generated"; + +globalAxios.interceptors.request.use(async (request) => { + const session = await getSession(); + + if (session) { + // @ts-ignore + request.headers["Authorization"] = `Bearer ${session.accessToken}`; + } + return request; +}); + +const configuration: Configuration = { + isJsonMime(mime: string): boolean { + const jsonMime = new RegExp( + "^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$", + "i", + ); + return ( + mime !== null && + (jsonMime.test(mime) || + mime.toLowerCase() === "application/json-patch+json") + ); + }, + basePath: process.env.NEXT_PUBLIC_API_URL, +}; + +const configurationAI: Configuration = { + isJsonMime(mime: string): boolean { + const jsonMime = new RegExp( + "^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$", + "i", + ); + return ( + mime !== null && + (jsonMime.test(mime) || + mime.toLowerCase() === "application/json-patch+json") + ); + }, + basePath: process.env.NEXT_PUBLIC_GENAI_URL, +}; +export const rootApiFactory = RootApiFactory(configuration); +export const accountApiFactory = AccountApiFactory(configuration); +export const llmApiFactory = DefaultApiFactory(configurationAI); +export const whiteboardApiFactory = WhiteboardApiFactory(configuration); +export const nodeApiFactory = NodeControllerApiFactory(configuration); +export const edgeApiFactory = EdgeControllerApiFactory(configuration); +export const viewportFactory = ViewportControllerApiFactory(configuration); diff --git a/client/src/api/main/generated/.gitignore b/client/src/api/main/generated/.gitignore new file mode 100644 index 00000000..149b5765 --- /dev/null +++ b/client/src/api/main/generated/.gitignore @@ -0,0 +1,4 @@ +wwwroot/*.js +node_modules +typings +dist diff --git a/client/src/api/main/generated/.npmignore b/client/src/api/main/generated/.npmignore new file mode 100644 index 00000000..999d88df --- /dev/null +++ b/client/src/api/main/generated/.npmignore @@ -0,0 +1 @@ +# empty npmignore to ensure all required files (e.g., in the dist folder) are published by npm \ No newline at end of file diff --git a/client/src/api/main/generated/.openapi-generator-ignore b/client/src/api/main/generated/.openapi-generator-ignore new file mode 100644 index 00000000..7484ee59 --- /dev/null +++ b/client/src/api/main/generated/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/client/src/api/main/generated/.openapi-generator/FILES b/client/src/api/main/generated/.openapi-generator/FILES new file mode 100644 index 00000000..1f3f7ef4 --- /dev/null +++ b/client/src/api/main/generated/.openapi-generator/FILES @@ -0,0 +1,25 @@ +.gitignore +.npmignore +api.ts +base.ts +common.ts +configuration.ts +docs/AccountApi.md +docs/Edge.md +docs/EdgeControllerApi.md +docs/InviteCollaboratorsRequest.md +docs/Node.md +docs/NodeControllerApi.md +docs/RemoveCollaboratorsRequest.md +docs/RootApi.md +docs/SaveWhiteboardStateRequest.md +docs/UpdateNodeRequest.md +docs/User.md +docs/UserResponse.md +docs/Viewport.md +docs/ViewportControllerApi.md +docs/ViewportResponse.md +docs/WhiteboardApi.md +docs/WhiteboardResponse.md +git_push.sh +index.ts diff --git a/client/src/api/main/generated/.openapi-generator/VERSION b/client/src/api/main/generated/.openapi-generator/VERSION new file mode 100644 index 00000000..eb1dc6a5 --- /dev/null +++ b/client/src/api/main/generated/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.13.0 diff --git a/client/src/api/main/generated/api.ts b/client/src/api/main/generated/api.ts new file mode 100644 index 00000000..c076a416 --- /dev/null +++ b/client/src/api/main/generated/api.ts @@ -0,0 +1,3244 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Team Server Down + * DevOps Application + * + * The version of the OpenAPI document: v0.0.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import type { Configuration } from "./configuration"; +import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from "axios"; +import globalAxios from "axios"; +// Some imports not used depending on template conditions +// @ts-ignore +import { + DUMMY_BASE_URL, + assertParamExists, + setApiKeyToObject, + setBasicAuthToObject, + setBearerAuthToObject, + setOAuthToObject, + setSearchParams, + serializeDataIfNeeded, + toPathString, + createRequestFunction, +} from "./common"; +import type { RequestArgs } from "./base"; +// @ts-ignore +import { + BASE_PATH, + COLLECTION_FORMATS, + BaseAPI, + RequiredError, + operationServerMap, +} from "./base"; + +/** + * + * @export + * @interface Edge + */ +export interface Edge { + /** + * + * @type {string} + * @memberof Edge + */ + id?: string; + /** + * + * @type {number} + * @memberof Edge + */ + whiteboardId?: number; + /** + * + * @type {string} + * @memberof Edge + */ + source?: string; + /** + * + * @type {string} + * @memberof Edge + */ + sourceHandle?: string; + /** + * + * @type {string} + * @memberof Edge + */ + target?: string; + /** + * + * @type {string} + * @memberof Edge + */ + targetHandle?: string; +} +/** + * + * @export + * @interface InviteCollaboratorsRequest + */ +export interface InviteCollaboratorsRequest { + /** + * + * @type {Array} + * @memberof InviteCollaboratorsRequest + */ + emails?: Array; +} +/** + * + * @export + * @interface Node + */ +export interface Node { + /** + * + * @type {string} + * @memberof Node + */ + id?: string; + /** + * + * @type {number} + * @memberof Node + */ + whiteboardId?: number; + /** + * + * @type {string} + * @memberof Node + */ + type?: string; + /** + * + * @type {number} + * @memberof Node + */ + positionX?: number; + /** + * + * @type {number} + * @memberof Node + */ + positionY?: number; + /** + * + * @type {string} + * @memberof Node + */ + label?: string; + /** + * + * @type {number} + * @memberof Node + */ + width?: number; + /** + * + * @type {number} + * @memberof Node + */ + height?: number; + /** + * + * @type {string} + * @memberof Node + */ + color?: string; + /** + * + * @type {string} + * @memberof Node + */ + borderColor?: string; + /** + * + * @type {number} + * @memberof Node + */ + borderWidth?: number; + /** + * + * @type {number} + * @memberof Node + */ + borderOpacity?: number; + /** + * + * @type {number} + * @memberof Node + */ + opacity?: number; + /** + * + * @type {string} + * @memberof Node + */ + textColor?: string; + /** + * + * @type {number} + * @memberof Node + */ + fontSize?: number; + /** + * + * @type {string} + * @memberof Node + */ + fontFamily?: string; + /** + * + * @type {boolean} + * @memberof Node + */ + bold?: boolean; + /** + * + * @type {boolean} + * @memberof Node + */ + italic?: boolean; + /** + * + * @type {boolean} + * @memberof Node + */ + strikethrough?: boolean; + /** + * + * @type {boolean} + * @memberof Node + */ + underline?: boolean; +} +/** + * + * @export + * @interface RemoveCollaboratorsRequest + */ +export interface RemoveCollaboratorsRequest { + /** + * + * @type {Array} + * @memberof RemoveCollaboratorsRequest + */ + userIds?: Array; +} +/** + * + * @export + * @interface SaveWhiteboardStateRequest + */ +export interface SaveWhiteboardStateRequest { + /** + * + * @type {Array} + * @memberof SaveWhiteboardStateRequest + */ + nodes?: Array; + /** + * + * @type {Array} + * @memberof SaveWhiteboardStateRequest + */ + edges?: Array; + /** + * + * @type {ViewportResponse} + * @memberof SaveWhiteboardStateRequest + */ + viewportResponse?: ViewportResponse; +} +/** + * + * @export + * @interface UpdateNodeRequest + */ +export interface UpdateNodeRequest { + /** + * + * @type {string} + * @memberof UpdateNodeRequest + */ + type?: string; + /** + * + * @type {number} + * @memberof UpdateNodeRequest + */ + positionX?: number; + /** + * + * @type {number} + * @memberof UpdateNodeRequest + */ + positionY?: number; + /** + * + * @type {string} + * @memberof UpdateNodeRequest + */ + label?: string; + /** + * + * @type {number} + * @memberof UpdateNodeRequest + */ + width?: number; + /** + * + * @type {number} + * @memberof UpdateNodeRequest + */ + height?: number; + /** + * + * @type {string} + * @memberof UpdateNodeRequest + */ + color?: string; + /** + * + * @type {string} + * @memberof UpdateNodeRequest + */ + borderColor?: string; + /** + * + * @type {number} + * @memberof UpdateNodeRequest + */ + borderWidth?: number; + /** + * + * @type {number} + * @memberof UpdateNodeRequest + */ + borderOpacity?: number; + /** + * + * @type {number} + * @memberof UpdateNodeRequest + */ + opacity?: number; + /** + * + * @type {string} + * @memberof UpdateNodeRequest + */ + textColor?: string; + /** + * + * @type {number} + * @memberof UpdateNodeRequest + */ + fontSize?: number; + /** + * + * @type {string} + * @memberof UpdateNodeRequest + */ + fontFamily?: string; + /** + * + * @type {boolean} + * @memberof UpdateNodeRequest + */ + isBold?: boolean; + /** + * + * @type {boolean} + * @memberof UpdateNodeRequest + */ + isItalic?: boolean; + /** + * + * @type {boolean} + * @memberof UpdateNodeRequest + */ + isStrikethrough?: boolean; + /** + * + * @type {boolean} + * @memberof UpdateNodeRequest + */ + isUnderline?: boolean; +} +/** + * + * @export + * @interface User + */ +export interface User { + /** + * + * @type {number} + * @memberof User + */ + id?: number; + /** + * + * @type {string} + * @memberof User + */ + firstName?: string; + /** + * + * @type {string} + * @memberof User + */ + lastName?: string; + /** + * + * @type {string} + * @memberof User + */ + username?: string; + /** + * + * @type {string} + * @memberof User + */ + email?: string; +} +/** + * + * @export + * @interface UserResponse + */ +export interface UserResponse { + /** + * + * @type {number} + * @memberof UserResponse + */ + id?: number; + /** + * + * @type {string} + * @memberof UserResponse + */ + firstName?: string; + /** + * + * @type {string} + * @memberof UserResponse + */ + lastName?: string; + /** + * + * @type {string} + * @memberof UserResponse + */ + username?: string; + /** + * + * @type {string} + * @memberof UserResponse + */ + email?: string; +} +/** + * + * @export + * @interface Viewport + */ +export interface Viewport { + /** + * + * @type {number} + * @memberof Viewport + */ + id?: number; + /** + * + * @type {number} + * @memberof Viewport + */ + x?: number; + /** + * + * @type {number} + * @memberof Viewport + */ + y?: number; + /** + * + * @type {number} + * @memberof Viewport + */ + zoom?: number; + /** + * + * @type {number} + * @memberof Viewport + */ + whiteboardId?: number; +} +/** + * + * @export + * @interface ViewportResponse + */ +export interface ViewportResponse { + /** + * + * @type {number} + * @memberof ViewportResponse + */ + x?: number; + /** + * + * @type {number} + * @memberof ViewportResponse + */ + y?: number; + /** + * + * @type {number} + * @memberof ViewportResponse + */ + zoom?: number; +} +/** + * + * @export + * @interface WhiteboardResponse + */ +export interface WhiteboardResponse { + /** + * + * @type {number} + * @memberof WhiteboardResponse + */ + id?: number; + /** + * + * @type {string} + * @memberof WhiteboardResponse + */ + title?: string; + /** + * + * @type {User} + * @memberof WhiteboardResponse + */ + user?: User; + /** + * + * @type {string} + * @memberof WhiteboardResponse + */ + createdAt?: string; + /** + * + * @type {string} + * @memberof WhiteboardResponse + */ + lastUpdatedAt?: string; +} + +/** + * AccountApi - axios parameter creator + * @export + */ +export const AccountApiAxiosParamCreator = function ( + configuration?: Configuration, +) { + return { + /** + * + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getCurrentUser: async ( + options: RawAxiosRequestConfig = {}, + ): Promise => { + const localVarPath = `/me`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "GET", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication keycloak required + // oauth required + await setOAuthToObject( + localVarHeaderParameter, + "keycloak", + [], + configuration, + ); + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + }; +}; + +/** + * AccountApi - functional programming interface + * @export + */ +export const AccountApiFp = function (configuration?: Configuration) { + const localVarAxiosParamCreator = AccountApiAxiosParamCreator(configuration); + return { + /** + * + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getCurrentUser( + options?: RawAxiosRequestConfig, + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.getCurrentUser(options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = + operationServerMap["AccountApi.getCurrentUser"]?.[ + localVarOperationServerIndex + ]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, localVarOperationServerBasePath || basePath); + }, + }; +}; + +/** + * AccountApi - factory interface + * @export + */ +export const AccountApiFactory = function ( + configuration?: Configuration, + basePath?: string, + axios?: AxiosInstance, +) { + const localVarFp = AccountApiFp(configuration); + return { + /** + * + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getCurrentUser( + options?: RawAxiosRequestConfig, + ): AxiosPromise { + return localVarFp + .getCurrentUser(options) + .then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * AccountApi - object-oriented interface + * @export + * @class AccountApi + * @extends {BaseAPI} + */ +export class AccountApi extends BaseAPI { + /** + * + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof AccountApi + */ + public getCurrentUser(options?: RawAxiosRequestConfig) { + return AccountApiFp(this.configuration) + .getCurrentUser(options) + .then((request) => request(this.axios, this.basePath)); + } +} + +/** + * EdgeControllerApi - axios parameter creator + * @export + */ +export const EdgeControllerApiAxiosParamCreator = function ( + configuration?: Configuration, +) { + return { + /** + * + * @param {Edge} edge + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + addEdge: async ( + edge: Edge, + options: RawAxiosRequestConfig = {}, + ): Promise => { + // verify required parameter 'edge' is not null or undefined + assertParamExists("addEdge", "edge", edge); + const localVarPath = `/edge`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "POST", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication keycloak required + // oauth required + await setOAuthToObject( + localVarHeaderParameter, + "keycloak", + [], + configuration, + ); + + localVarHeaderParameter["Content-Type"] = "application/json"; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + localVarRequestOptions.data = serializeDataIfNeeded( + edge, + localVarRequestOptions, + configuration, + ); + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @param {string} id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deleteEdge: async ( + id: string, + options: RawAxiosRequestConfig = {}, + ): Promise => { + // verify required parameter 'id' is not null or undefined + assertParamExists("deleteEdge", "id", id); + const localVarPath = `/edge/{id}`.replace( + `{${"id"}}`, + encodeURIComponent(String(id)), + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "DELETE", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication keycloak required + // oauth required + await setOAuthToObject( + localVarHeaderParameter, + "keycloak", + [], + configuration, + ); + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @param {number} whiteboardId + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getEdgesByWhiteboard: async ( + whiteboardId: number, + options: RawAxiosRequestConfig = {}, + ): Promise => { + // verify required parameter 'whiteboardId' is not null or undefined + assertParamExists("getEdgesByWhiteboard", "whiteboardId", whiteboardId); + const localVarPath = `/edge/whiteboard/{whiteboardId}`.replace( + `{${"whiteboardId"}}`, + encodeURIComponent(String(whiteboardId)), + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "GET", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication keycloak required + // oauth required + await setOAuthToObject( + localVarHeaderParameter, + "keycloak", + [], + configuration, + ); + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + }; +}; + +/** + * EdgeControllerApi - functional programming interface + * @export + */ +export const EdgeControllerApiFp = function (configuration?: Configuration) { + const localVarAxiosParamCreator = + EdgeControllerApiAxiosParamCreator(configuration); + return { + /** + * + * @param {Edge} edge + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async addEdge( + edge: Edge, + options?: RawAxiosRequestConfig, + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = await localVarAxiosParamCreator.addEdge( + edge, + options, + ); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = + operationServerMap["EdgeControllerApi.addEdge"]?.[ + localVarOperationServerIndex + ]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @param {string} id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async deleteEdge( + id: string, + options?: RawAxiosRequestConfig, + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = await localVarAxiosParamCreator.deleteEdge( + id, + options, + ); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = + operationServerMap["EdgeControllerApi.deleteEdge"]?.[ + localVarOperationServerIndex + ]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @param {number} whiteboardId + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getEdgesByWhiteboard( + whiteboardId: number, + options?: RawAxiosRequestConfig, + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise> + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.getEdgesByWhiteboard( + whiteboardId, + options, + ); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = + operationServerMap["EdgeControllerApi.getEdgesByWhiteboard"]?.[ + localVarOperationServerIndex + ]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, localVarOperationServerBasePath || basePath); + }, + }; +}; + +/** + * EdgeControllerApi - factory interface + * @export + */ +export const EdgeControllerApiFactory = function ( + configuration?: Configuration, + basePath?: string, + axios?: AxiosInstance, +) { + const localVarFp = EdgeControllerApiFp(configuration); + return { + /** + * + * @param {Edge} edge + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + addEdge(edge: Edge, options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp + .addEdge(edge, options) + .then((request) => request(axios, basePath)); + }, + /** + * + * @param {string} id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deleteEdge( + id: string, + options?: RawAxiosRequestConfig, + ): AxiosPromise { + return localVarFp + .deleteEdge(id, options) + .then((request) => request(axios, basePath)); + }, + /** + * + * @param {number} whiteboardId + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getEdgesByWhiteboard( + whiteboardId: number, + options?: RawAxiosRequestConfig, + ): AxiosPromise> { + return localVarFp + .getEdgesByWhiteboard(whiteboardId, options) + .then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * EdgeControllerApi - object-oriented interface + * @export + * @class EdgeControllerApi + * @extends {BaseAPI} + */ +export class EdgeControllerApi extends BaseAPI { + /** + * + * @param {Edge} edge + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof EdgeControllerApi + */ + public addEdge(edge: Edge, options?: RawAxiosRequestConfig) { + return EdgeControllerApiFp(this.configuration) + .addEdge(edge, options) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @param {string} id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof EdgeControllerApi + */ + public deleteEdge(id: string, options?: RawAxiosRequestConfig) { + return EdgeControllerApiFp(this.configuration) + .deleteEdge(id, options) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @param {number} whiteboardId + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof EdgeControllerApi + */ + public getEdgesByWhiteboard( + whiteboardId: number, + options?: RawAxiosRequestConfig, + ) { + return EdgeControllerApiFp(this.configuration) + .getEdgesByWhiteboard(whiteboardId, options) + .then((request) => request(this.axios, this.basePath)); + } +} + +/** + * NodeControllerApi - axios parameter creator + * @export + */ +export const NodeControllerApiAxiosParamCreator = function ( + configuration?: Configuration, +) { + return { + /** + * + * @param {Node} node + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + createNode: async ( + node: Node, + options: RawAxiosRequestConfig = {}, + ): Promise => { + // verify required parameter 'node' is not null or undefined + assertParamExists("createNode", "node", node); + const localVarPath = `/nodes`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "POST", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication keycloak required + // oauth required + await setOAuthToObject( + localVarHeaderParameter, + "keycloak", + [], + configuration, + ); + + localVarHeaderParameter["Content-Type"] = "application/json"; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + localVarRequestOptions.data = serializeDataIfNeeded( + node, + localVarRequestOptions, + configuration, + ); + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @param {string} id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deleteNode: async ( + id: string, + options: RawAxiosRequestConfig = {}, + ): Promise => { + // verify required parameter 'id' is not null or undefined + assertParamExists("deleteNode", "id", id); + const localVarPath = `/nodes/{id}`.replace( + `{${"id"}}`, + encodeURIComponent(String(id)), + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "DELETE", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication keycloak required + // oauth required + await setOAuthToObject( + localVarHeaderParameter, + "keycloak", + [], + configuration, + ); + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @param {number} whiteboardId + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getAllByWhiteboardId: async ( + whiteboardId: number, + options: RawAxiosRequestConfig = {}, + ): Promise => { + // verify required parameter 'whiteboardId' is not null or undefined + assertParamExists("getAllByWhiteboardId", "whiteboardId", whiteboardId); + const localVarPath = `/nodes/whiteboard/{whiteboardId}`.replace( + `{${"whiteboardId"}}`, + encodeURIComponent(String(whiteboardId)), + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "GET", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication keycloak required + // oauth required + await setOAuthToObject( + localVarHeaderParameter, + "keycloak", + [], + configuration, + ); + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @param {string} id + * @param {UpdateNodeRequest} updateNodeRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + patchNode: async ( + id: string, + updateNodeRequest: UpdateNodeRequest, + options: RawAxiosRequestConfig = {}, + ): Promise => { + // verify required parameter 'id' is not null or undefined + assertParamExists("patchNode", "id", id); + // verify required parameter 'updateNodeRequest' is not null or undefined + assertParamExists("patchNode", "updateNodeRequest", updateNodeRequest); + const localVarPath = `/nodes/nodes/{id}`.replace( + `{${"id"}}`, + encodeURIComponent(String(id)), + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "PATCH", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication keycloak required + // oauth required + await setOAuthToObject( + localVarHeaderParameter, + "keycloak", + [], + configuration, + ); + + localVarHeaderParameter["Content-Type"] = "application/json"; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + localVarRequestOptions.data = serializeDataIfNeeded( + updateNodeRequest, + localVarRequestOptions, + configuration, + ); + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + }; +}; + +/** + * NodeControllerApi - functional programming interface + * @export + */ +export const NodeControllerApiFp = function (configuration?: Configuration) { + const localVarAxiosParamCreator = + NodeControllerApiAxiosParamCreator(configuration); + return { + /** + * + * @param {Node} node + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async createNode( + node: Node, + options?: RawAxiosRequestConfig, + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = await localVarAxiosParamCreator.createNode( + node, + options, + ); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = + operationServerMap["NodeControllerApi.createNode"]?.[ + localVarOperationServerIndex + ]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @param {string} id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async deleteNode( + id: string, + options?: RawAxiosRequestConfig, + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = await localVarAxiosParamCreator.deleteNode( + id, + options, + ); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = + operationServerMap["NodeControllerApi.deleteNode"]?.[ + localVarOperationServerIndex + ]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @param {number} whiteboardId + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getAllByWhiteboardId( + whiteboardId: number, + options?: RawAxiosRequestConfig, + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise> + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.getAllByWhiteboardId( + whiteboardId, + options, + ); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = + operationServerMap["NodeControllerApi.getAllByWhiteboardId"]?.[ + localVarOperationServerIndex + ]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @param {string} id + * @param {UpdateNodeRequest} updateNodeRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async patchNode( + id: string, + updateNodeRequest: UpdateNodeRequest, + options?: RawAxiosRequestConfig, + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = await localVarAxiosParamCreator.patchNode( + id, + updateNodeRequest, + options, + ); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = + operationServerMap["NodeControllerApi.patchNode"]?.[ + localVarOperationServerIndex + ]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, localVarOperationServerBasePath || basePath); + }, + }; +}; + +/** + * NodeControllerApi - factory interface + * @export + */ +export const NodeControllerApiFactory = function ( + configuration?: Configuration, + basePath?: string, + axios?: AxiosInstance, +) { + const localVarFp = NodeControllerApiFp(configuration); + return { + /** + * + * @param {Node} node + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + createNode( + node: Node, + options?: RawAxiosRequestConfig, + ): AxiosPromise { + return localVarFp + .createNode(node, options) + .then((request) => request(axios, basePath)); + }, + /** + * + * @param {string} id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deleteNode( + id: string, + options?: RawAxiosRequestConfig, + ): AxiosPromise { + return localVarFp + .deleteNode(id, options) + .then((request) => request(axios, basePath)); + }, + /** + * + * @param {number} whiteboardId + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getAllByWhiteboardId( + whiteboardId: number, + options?: RawAxiosRequestConfig, + ): AxiosPromise> { + return localVarFp + .getAllByWhiteboardId(whiteboardId, options) + .then((request) => request(axios, basePath)); + }, + /** + * + * @param {string} id + * @param {UpdateNodeRequest} updateNodeRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + patchNode( + id: string, + updateNodeRequest: UpdateNodeRequest, + options?: RawAxiosRequestConfig, + ): AxiosPromise { + return localVarFp + .patchNode(id, updateNodeRequest, options) + .then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * NodeControllerApi - object-oriented interface + * @export + * @class NodeControllerApi + * @extends {BaseAPI} + */ +export class NodeControllerApi extends BaseAPI { + /** + * + * @param {Node} node + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NodeControllerApi + */ + public createNode(node: Node, options?: RawAxiosRequestConfig) { + return NodeControllerApiFp(this.configuration) + .createNode(node, options) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @param {string} id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NodeControllerApi + */ + public deleteNode(id: string, options?: RawAxiosRequestConfig) { + return NodeControllerApiFp(this.configuration) + .deleteNode(id, options) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @param {number} whiteboardId + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NodeControllerApi + */ + public getAllByWhiteboardId( + whiteboardId: number, + options?: RawAxiosRequestConfig, + ) { + return NodeControllerApiFp(this.configuration) + .getAllByWhiteboardId(whiteboardId, options) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @param {string} id + * @param {UpdateNodeRequest} updateNodeRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NodeControllerApi + */ + public patchNode( + id: string, + updateNodeRequest: UpdateNodeRequest, + options?: RawAxiosRequestConfig, + ) { + return NodeControllerApiFp(this.configuration) + .patchNode(id, updateNodeRequest, options) + .then((request) => request(this.axios, this.basePath)); + } +} + +/** + * RootApi - axios parameter creator + * @export + */ +export const RootApiAxiosParamCreator = function ( + configuration?: Configuration, +) { + return { + /** + * Returns a simple Hello World message. + * @summary Root endpoint + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + root: async (options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "GET", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication keycloak required + // oauth required + await setOAuthToObject( + localVarHeaderParameter, + "keycloak", + [], + configuration, + ); + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + }; +}; + +/** + * RootApi - functional programming interface + * @export + */ +export const RootApiFp = function (configuration?: Configuration) { + const localVarAxiosParamCreator = RootApiAxiosParamCreator(configuration); + return { + /** + * Returns a simple Hello World message. + * @summary Root endpoint + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async root( + options?: RawAxiosRequestConfig, + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = await localVarAxiosParamCreator.root(options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = + operationServerMap["RootApi.root"]?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, localVarOperationServerBasePath || basePath); + }, + }; +}; + +/** + * RootApi - factory interface + * @export + */ +export const RootApiFactory = function ( + configuration?: Configuration, + basePath?: string, + axios?: AxiosInstance, +) { + const localVarFp = RootApiFp(configuration); + return { + /** + * Returns a simple Hello World message. + * @summary Root endpoint + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + root(options?: RawAxiosRequestConfig): AxiosPromise { + return localVarFp + .root(options) + .then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * RootApi - object-oriented interface + * @export + * @class RootApi + * @extends {BaseAPI} + */ +export class RootApi extends BaseAPI { + /** + * Returns a simple Hello World message. + * @summary Root endpoint + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RootApi + */ + public root(options?: RawAxiosRequestConfig) { + return RootApiFp(this.configuration) + .root(options) + .then((request) => request(this.axios, this.basePath)); + } +} + +/** + * ViewportControllerApi - axios parameter creator + * @export + */ +export const ViewportControllerApiAxiosParamCreator = function ( + configuration?: Configuration, +) { + return { + /** + * + * @param {number} id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deleteViewport: async ( + id: number, + options: RawAxiosRequestConfig = {}, + ): Promise => { + // verify required parameter 'id' is not null or undefined + assertParamExists("deleteViewport", "id", id); + const localVarPath = `/api/viewports/{id}`.replace( + `{${"id"}}`, + encodeURIComponent(String(id)), + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "DELETE", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication keycloak required + // oauth required + await setOAuthToObject( + localVarHeaderParameter, + "keycloak", + [], + configuration, + ); + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @param {number} whiteboardId + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getViewportByWhiteboardId: async ( + whiteboardId: number, + options: RawAxiosRequestConfig = {}, + ): Promise => { + // verify required parameter 'whiteboardId' is not null or undefined + assertParamExists( + "getViewportByWhiteboardId", + "whiteboardId", + whiteboardId, + ); + const localVarPath = `/api/viewports/whiteboard/{whiteboardId}`.replace( + `{${"whiteboardId"}}`, + encodeURIComponent(String(whiteboardId)), + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "GET", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication keycloak required + // oauth required + await setOAuthToObject( + localVarHeaderParameter, + "keycloak", + [], + configuration, + ); + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + }; +}; + +/** + * ViewportControllerApi - functional programming interface + * @export + */ +export const ViewportControllerApiFp = function ( + configuration?: Configuration, +) { + const localVarAxiosParamCreator = + ViewportControllerApiAxiosParamCreator(configuration); + return { + /** + * + * @param {number} id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async deleteViewport( + id: number, + options?: RawAxiosRequestConfig, + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = await localVarAxiosParamCreator.deleteViewport( + id, + options, + ); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = + operationServerMap["ViewportControllerApi.deleteViewport"]?.[ + localVarOperationServerIndex + ]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @param {number} whiteboardId + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getViewportByWhiteboardId( + whiteboardId: number, + options?: RawAxiosRequestConfig, + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.getViewportByWhiteboardId( + whiteboardId, + options, + ); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = + operationServerMap["ViewportControllerApi.getViewportByWhiteboardId"]?.[ + localVarOperationServerIndex + ]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, localVarOperationServerBasePath || basePath); + }, + }; +}; + +/** + * ViewportControllerApi - factory interface + * @export + */ +export const ViewportControllerApiFactory = function ( + configuration?: Configuration, + basePath?: string, + axios?: AxiosInstance, +) { + const localVarFp = ViewportControllerApiFp(configuration); + return { + /** + * + * @param {number} id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deleteViewport( + id: number, + options?: RawAxiosRequestConfig, + ): AxiosPromise { + return localVarFp + .deleteViewport(id, options) + .then((request) => request(axios, basePath)); + }, + /** + * + * @param {number} whiteboardId + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getViewportByWhiteboardId( + whiteboardId: number, + options?: RawAxiosRequestConfig, + ): AxiosPromise { + return localVarFp + .getViewportByWhiteboardId(whiteboardId, options) + .then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * ViewportControllerApi - object-oriented interface + * @export + * @class ViewportControllerApi + * @extends {BaseAPI} + */ +export class ViewportControllerApi extends BaseAPI { + /** + * + * @param {number} id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof ViewportControllerApi + */ + public deleteViewport(id: number, options?: RawAxiosRequestConfig) { + return ViewportControllerApiFp(this.configuration) + .deleteViewport(id, options) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @param {number} whiteboardId + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof ViewportControllerApi + */ + public getViewportByWhiteboardId( + whiteboardId: number, + options?: RawAxiosRequestConfig, + ) { + return ViewportControllerApiFp(this.configuration) + .getViewportByWhiteboardId(whiteboardId, options) + .then((request) => request(this.axios, this.basePath)); + } +} + +/** + * WhiteboardApi - axios parameter creator + * @export + */ +export const WhiteboardApiAxiosParamCreator = function ( + configuration?: Configuration, +) { + return { + /** + * Creates a new whiteboard for a user. + * @summary Create whiteboard + * @param {string} title + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + createWhiteboard: async ( + title: string, + options: RawAxiosRequestConfig = {}, + ): Promise => { + // verify required parameter 'title' is not null or undefined + assertParamExists("createWhiteboard", "title", title); + const localVarPath = `/whiteboards`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "POST", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication keycloak required + // oauth required + await setOAuthToObject( + localVarHeaderParameter, + "keycloak", + [], + configuration, + ); + + if (title !== undefined) { + localVarQueryParameter["title"] = title; + } + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @param {number} id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deleteWhiteboard: async ( + id: number, + options: RawAxiosRequestConfig = {}, + ): Promise => { + // verify required parameter 'id' is not null or undefined + assertParamExists("deleteWhiteboard", "id", id); + const localVarPath = `/whiteboards/{id}`.replace( + `{${"id"}}`, + encodeURIComponent(String(id)), + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "DELETE", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication keycloak required + // oauth required + await setOAuthToObject( + localVarHeaderParameter, + "keycloak", + [], + configuration, + ); + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @param {number} id ID of the whiteboard + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getCollaborators: async ( + id: number, + options: RawAxiosRequestConfig = {}, + ): Promise => { + // verify required parameter 'id' is not null or undefined + assertParamExists("getCollaborators", "id", id); + const localVarPath = `/whiteboards/{id}/collaborators`.replace( + `{${"id"}}`, + encodeURIComponent(String(id)), + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "GET", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication keycloak required + // oauth required + await setOAuthToObject( + localVarHeaderParameter, + "keycloak", + [], + configuration, + ); + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Returns a list of whiteboards for the current user. + * @summary Get whiteboards by user ID + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getUserWhiteboards: async ( + options: RawAxiosRequestConfig = {}, + ): Promise => { + const localVarPath = `/whiteboards`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "GET", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication keycloak required + // oauth required + await setOAuthToObject( + localVarHeaderParameter, + "keycloak", + [], + configuration, + ); + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @param {number} id ID of the whiteboard + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getWhiteboardById: async ( + id: number, + options: RawAxiosRequestConfig = {}, + ): Promise => { + // verify required parameter 'id' is not null or undefined + assertParamExists("getWhiteboardById", "id", id); + const localVarPath = `/whiteboards/{id}`.replace( + `{${"id"}}`, + encodeURIComponent(String(id)), + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "GET", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication keycloak required + // oauth required + await setOAuthToObject( + localVarHeaderParameter, + "keycloak", + [], + configuration, + ); + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Returns the title of a whiteboard by its ID + * @summary Get whiteboard title + * @param {number} id ID of the whiteboard + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getWhiteboardTitle: async ( + id: number, + options: RawAxiosRequestConfig = {}, + ): Promise => { + // verify required parameter 'id' is not null or undefined + assertParamExists("getWhiteboardTitle", "id", id); + const localVarPath = `/whiteboards/{id}/title`.replace( + `{${"id"}}`, + encodeURIComponent(String(id)), + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "GET", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication keycloak required + // oauth required + await setOAuthToObject( + localVarHeaderParameter, + "keycloak", + [], + configuration, + ); + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary Invite users to collaborate on the whiteboard + * @param {number} id ID of the whiteboard + * @param {InviteCollaboratorsRequest} inviteCollaboratorsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + inviteCollaborators: async ( + id: number, + inviteCollaboratorsRequest: InviteCollaboratorsRequest, + options: RawAxiosRequestConfig = {}, + ): Promise => { + // verify required parameter 'id' is not null or undefined + assertParamExists("inviteCollaborators", "id", id); + // verify required parameter 'inviteCollaboratorsRequest' is not null or undefined + assertParamExists( + "inviteCollaborators", + "inviteCollaboratorsRequest", + inviteCollaboratorsRequest, + ); + const localVarPath = `/whiteboards/{id}/invitations`.replace( + `{${"id"}}`, + encodeURIComponent(String(id)), + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "POST", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication keycloak required + // oauth required + await setOAuthToObject( + localVarHeaderParameter, + "keycloak", + [], + configuration, + ); + + localVarHeaderParameter["Content-Type"] = "application/json"; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + localVarRequestOptions.data = serializeDataIfNeeded( + inviteCollaboratorsRequest, + localVarRequestOptions, + configuration, + ); + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary Remove collaborators from the whiteboard + * @param {number} id ID of the whiteboard + * @param {RemoveCollaboratorsRequest} removeCollaboratorsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + removeCollaborators: async ( + id: number, + removeCollaboratorsRequest: RemoveCollaboratorsRequest, + options: RawAxiosRequestConfig = {}, + ): Promise => { + // verify required parameter 'id' is not null or undefined + assertParamExists("removeCollaborators", "id", id); + // verify required parameter 'removeCollaboratorsRequest' is not null or undefined + assertParamExists( + "removeCollaborators", + "removeCollaboratorsRequest", + removeCollaboratorsRequest, + ); + const localVarPath = `/whiteboards/{id}/invitations`.replace( + `{${"id"}}`, + encodeURIComponent(String(id)), + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "DELETE", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication keycloak required + // oauth required + await setOAuthToObject( + localVarHeaderParameter, + "keycloak", + [], + configuration, + ); + + localVarHeaderParameter["Content-Type"] = "application/json"; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + localVarRequestOptions.data = serializeDataIfNeeded( + removeCollaboratorsRequest, + localVarRequestOptions, + configuration, + ); + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @param {number} whiteboardId + * @param {SaveWhiteboardStateRequest} saveWhiteboardStateRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + saveWhiteboardState: async ( + whiteboardId: number, + saveWhiteboardStateRequest: SaveWhiteboardStateRequest, + options: RawAxiosRequestConfig = {}, + ): Promise => { + // verify required parameter 'whiteboardId' is not null or undefined + assertParamExists("saveWhiteboardState", "whiteboardId", whiteboardId); + // verify required parameter 'saveWhiteboardStateRequest' is not null or undefined + assertParamExists( + "saveWhiteboardState", + "saveWhiteboardStateRequest", + saveWhiteboardStateRequest, + ); + const localVarPath = `/whiteboards/{whiteboardId}/save`.replace( + `{${"whiteboardId"}}`, + encodeURIComponent(String(whiteboardId)), + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "POST", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication keycloak required + // oauth required + await setOAuthToObject( + localVarHeaderParameter, + "keycloak", + [], + configuration, + ); + + localVarHeaderParameter["Content-Type"] = "application/json"; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + localVarRequestOptions.data = serializeDataIfNeeded( + saveWhiteboardStateRequest, + localVarRequestOptions, + configuration, + ); + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Updates the title of an existing whiteboard. + * @summary Update title + * @param {number} id ID of the whiteboard + * @param {string} title + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + updateTitle: async ( + id: number, + title: string, + options: RawAxiosRequestConfig = {}, + ): Promise => { + // verify required parameter 'id' is not null or undefined + assertParamExists("updateTitle", "id", id); + // verify required parameter 'title' is not null or undefined + assertParamExists("updateTitle", "title", title); + const localVarPath = `/whiteboards/{id}/title`.replace( + `{${"id"}}`, + encodeURIComponent(String(id)), + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "PUT", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication keycloak required + // oauth required + await setOAuthToObject( + localVarHeaderParameter, + "keycloak", + [], + configuration, + ); + + if (title !== undefined) { + localVarQueryParameter["title"] = title; + } + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + }; +}; + +/** + * WhiteboardApi - functional programming interface + * @export + */ +export const WhiteboardApiFp = function (configuration?: Configuration) { + const localVarAxiosParamCreator = + WhiteboardApiAxiosParamCreator(configuration); + return { + /** + * Creates a new whiteboard for a user. + * @summary Create whiteboard + * @param {string} title + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async createWhiteboard( + title: string, + options?: RawAxiosRequestConfig, + ): Promise< + ( + axios?: AxiosInstance, + basePath?: string, + ) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.createWhiteboard(title, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = + operationServerMap["WhiteboardApi.createWhiteboard"]?.[ + localVarOperationServerIndex + ]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @param {number} id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async deleteWhiteboard( + id: number, + options?: RawAxiosRequestConfig, + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.deleteWhiteboard(id, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = + operationServerMap["WhiteboardApi.deleteWhiteboard"]?.[ + localVarOperationServerIndex + ]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @param {number} id ID of the whiteboard + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getCollaborators( + id: number, + options?: RawAxiosRequestConfig, + ): Promise< + ( + axios?: AxiosInstance, + basePath?: string, + ) => AxiosPromise> + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.getCollaborators(id, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = + operationServerMap["WhiteboardApi.getCollaborators"]?.[ + localVarOperationServerIndex + ]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Returns a list of whiteboards for the current user. + * @summary Get whiteboards by user ID + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getUserWhiteboards( + options?: RawAxiosRequestConfig, + ): Promise< + ( + axios?: AxiosInstance, + basePath?: string, + ) => AxiosPromise> + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.getUserWhiteboards(options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = + operationServerMap["WhiteboardApi.getUserWhiteboards"]?.[ + localVarOperationServerIndex + ]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @param {number} id ID of the whiteboard + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getWhiteboardById( + id: number, + options?: RawAxiosRequestConfig, + ): Promise< + ( + axios?: AxiosInstance, + basePath?: string, + ) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.getWhiteboardById(id, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = + operationServerMap["WhiteboardApi.getWhiteboardById"]?.[ + localVarOperationServerIndex + ]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Returns the title of a whiteboard by its ID + * @summary Get whiteboard title + * @param {number} id ID of the whiteboard + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getWhiteboardTitle( + id: number, + options?: RawAxiosRequestConfig, + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.getWhiteboardTitle(id, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = + operationServerMap["WhiteboardApi.getWhiteboardTitle"]?.[ + localVarOperationServerIndex + ]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary Invite users to collaborate on the whiteboard + * @param {number} id ID of the whiteboard + * @param {InviteCollaboratorsRequest} inviteCollaboratorsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async inviteCollaborators( + id: number, + inviteCollaboratorsRequest: InviteCollaboratorsRequest, + options?: RawAxiosRequestConfig, + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.inviteCollaborators( + id, + inviteCollaboratorsRequest, + options, + ); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = + operationServerMap["WhiteboardApi.inviteCollaborators"]?.[ + localVarOperationServerIndex + ]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary Remove collaborators from the whiteboard + * @param {number} id ID of the whiteboard + * @param {RemoveCollaboratorsRequest} removeCollaboratorsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async removeCollaborators( + id: number, + removeCollaboratorsRequest: RemoveCollaboratorsRequest, + options?: RawAxiosRequestConfig, + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.removeCollaborators( + id, + removeCollaboratorsRequest, + options, + ); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = + operationServerMap["WhiteboardApi.removeCollaborators"]?.[ + localVarOperationServerIndex + ]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @param {number} whiteboardId + * @param {SaveWhiteboardStateRequest} saveWhiteboardStateRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async saveWhiteboardState( + whiteboardId: number, + saveWhiteboardStateRequest: SaveWhiteboardStateRequest, + options?: RawAxiosRequestConfig, + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.saveWhiteboardState( + whiteboardId, + saveWhiteboardStateRequest, + options, + ); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = + operationServerMap["WhiteboardApi.saveWhiteboardState"]?.[ + localVarOperationServerIndex + ]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, localVarOperationServerBasePath || basePath); + }, + /** + * Updates the title of an existing whiteboard. + * @summary Update title + * @param {number} id ID of the whiteboard + * @param {string} title + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async updateTitle( + id: number, + title: string, + options?: RawAxiosRequestConfig, + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = await localVarAxiosParamCreator.updateTitle( + id, + title, + options, + ); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = + operationServerMap["WhiteboardApi.updateTitle"]?.[ + localVarOperationServerIndex + ]?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, localVarOperationServerBasePath || basePath); + }, + }; +}; + +/** + * WhiteboardApi - factory interface + * @export + */ +export const WhiteboardApiFactory = function ( + configuration?: Configuration, + basePath?: string, + axios?: AxiosInstance, +) { + const localVarFp = WhiteboardApiFp(configuration); + return { + /** + * Creates a new whiteboard for a user. + * @summary Create whiteboard + * @param {string} title + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + createWhiteboard( + title: string, + options?: RawAxiosRequestConfig, + ): AxiosPromise { + return localVarFp + .createWhiteboard(title, options) + .then((request) => request(axios, basePath)); + }, + /** + * + * @param {number} id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deleteWhiteboard( + id: number, + options?: RawAxiosRequestConfig, + ): AxiosPromise { + return localVarFp + .deleteWhiteboard(id, options) + .then((request) => request(axios, basePath)); + }, + /** + * + * @param {number} id ID of the whiteboard + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getCollaborators( + id: number, + options?: RawAxiosRequestConfig, + ): AxiosPromise> { + return localVarFp + .getCollaborators(id, options) + .then((request) => request(axios, basePath)); + }, + /** + * Returns a list of whiteboards for the current user. + * @summary Get whiteboards by user ID + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getUserWhiteboards( + options?: RawAxiosRequestConfig, + ): AxiosPromise> { + return localVarFp + .getUserWhiteboards(options) + .then((request) => request(axios, basePath)); + }, + /** + * + * @param {number} id ID of the whiteboard + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getWhiteboardById( + id: number, + options?: RawAxiosRequestConfig, + ): AxiosPromise { + return localVarFp + .getWhiteboardById(id, options) + .then((request) => request(axios, basePath)); + }, + /** + * Returns the title of a whiteboard by its ID + * @summary Get whiteboard title + * @param {number} id ID of the whiteboard + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getWhiteboardTitle( + id: number, + options?: RawAxiosRequestConfig, + ): AxiosPromise { + return localVarFp + .getWhiteboardTitle(id, options) + .then((request) => request(axios, basePath)); + }, + /** + * + * @summary Invite users to collaborate on the whiteboard + * @param {number} id ID of the whiteboard + * @param {InviteCollaboratorsRequest} inviteCollaboratorsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + inviteCollaborators( + id: number, + inviteCollaboratorsRequest: InviteCollaboratorsRequest, + options?: RawAxiosRequestConfig, + ): AxiosPromise { + return localVarFp + .inviteCollaborators(id, inviteCollaboratorsRequest, options) + .then((request) => request(axios, basePath)); + }, + /** + * + * @summary Remove collaborators from the whiteboard + * @param {number} id ID of the whiteboard + * @param {RemoveCollaboratorsRequest} removeCollaboratorsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + removeCollaborators( + id: number, + removeCollaboratorsRequest: RemoveCollaboratorsRequest, + options?: RawAxiosRequestConfig, + ): AxiosPromise { + return localVarFp + .removeCollaborators(id, removeCollaboratorsRequest, options) + .then((request) => request(axios, basePath)); + }, + /** + * + * @param {number} whiteboardId + * @param {SaveWhiteboardStateRequest} saveWhiteboardStateRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + saveWhiteboardState( + whiteboardId: number, + saveWhiteboardStateRequest: SaveWhiteboardStateRequest, + options?: RawAxiosRequestConfig, + ): AxiosPromise { + return localVarFp + .saveWhiteboardState(whiteboardId, saveWhiteboardStateRequest, options) + .then((request) => request(axios, basePath)); + }, + /** + * Updates the title of an existing whiteboard. + * @summary Update title + * @param {number} id ID of the whiteboard + * @param {string} title + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + updateTitle( + id: number, + title: string, + options?: RawAxiosRequestConfig, + ): AxiosPromise { + return localVarFp + .updateTitle(id, title, options) + .then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * WhiteboardApi - object-oriented interface + * @export + * @class WhiteboardApi + * @extends {BaseAPI} + */ +export class WhiteboardApi extends BaseAPI { + /** + * Creates a new whiteboard for a user. + * @summary Create whiteboard + * @param {string} title + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof WhiteboardApi + */ + public createWhiteboard(title: string, options?: RawAxiosRequestConfig) { + return WhiteboardApiFp(this.configuration) + .createWhiteboard(title, options) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @param {number} id + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof WhiteboardApi + */ + public deleteWhiteboard(id: number, options?: RawAxiosRequestConfig) { + return WhiteboardApiFp(this.configuration) + .deleteWhiteboard(id, options) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @param {number} id ID of the whiteboard + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof WhiteboardApi + */ + public getCollaborators(id: number, options?: RawAxiosRequestConfig) { + return WhiteboardApiFp(this.configuration) + .getCollaborators(id, options) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * Returns a list of whiteboards for the current user. + * @summary Get whiteboards by user ID + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof WhiteboardApi + */ + public getUserWhiteboards(options?: RawAxiosRequestConfig) { + return WhiteboardApiFp(this.configuration) + .getUserWhiteboards(options) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @param {number} id ID of the whiteboard + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof WhiteboardApi + */ + public getWhiteboardById(id: number, options?: RawAxiosRequestConfig) { + return WhiteboardApiFp(this.configuration) + .getWhiteboardById(id, options) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * Returns the title of a whiteboard by its ID + * @summary Get whiteboard title + * @param {number} id ID of the whiteboard + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof WhiteboardApi + */ + public getWhiteboardTitle(id: number, options?: RawAxiosRequestConfig) { + return WhiteboardApiFp(this.configuration) + .getWhiteboardTitle(id, options) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary Invite users to collaborate on the whiteboard + * @param {number} id ID of the whiteboard + * @param {InviteCollaboratorsRequest} inviteCollaboratorsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof WhiteboardApi + */ + public inviteCollaborators( + id: number, + inviteCollaboratorsRequest: InviteCollaboratorsRequest, + options?: RawAxiosRequestConfig, + ) { + return WhiteboardApiFp(this.configuration) + .inviteCollaborators(id, inviteCollaboratorsRequest, options) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary Remove collaborators from the whiteboard + * @param {number} id ID of the whiteboard + * @param {RemoveCollaboratorsRequest} removeCollaboratorsRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof WhiteboardApi + */ + public removeCollaborators( + id: number, + removeCollaboratorsRequest: RemoveCollaboratorsRequest, + options?: RawAxiosRequestConfig, + ) { + return WhiteboardApiFp(this.configuration) + .removeCollaborators(id, removeCollaboratorsRequest, options) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @param {number} whiteboardId + * @param {SaveWhiteboardStateRequest} saveWhiteboardStateRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof WhiteboardApi + */ + public saveWhiteboardState( + whiteboardId: number, + saveWhiteboardStateRequest: SaveWhiteboardStateRequest, + options?: RawAxiosRequestConfig, + ) { + return WhiteboardApiFp(this.configuration) + .saveWhiteboardState(whiteboardId, saveWhiteboardStateRequest, options) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * Updates the title of an existing whiteboard. + * @summary Update title + * @param {number} id ID of the whiteboard + * @param {string} title + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof WhiteboardApi + */ + public updateTitle( + id: number, + title: string, + options?: RawAxiosRequestConfig, + ) { + return WhiteboardApiFp(this.configuration) + .updateTitle(id, title, options) + .then((request) => request(this.axios, this.basePath)); + } +} diff --git a/client/src/api/main/generated/base.ts b/client/src/api/main/generated/base.ts new file mode 100644 index 00000000..0597dc41 --- /dev/null +++ b/client/src/api/main/generated/base.ts @@ -0,0 +1,91 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Team Server Down + * DevOps Application + * + * The version of the OpenAPI document: v0.0.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import type { Configuration } from "./configuration"; +// Some imports not used depending on template conditions +// @ts-ignore +import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from "axios"; +import globalAxios from "axios"; + +export const BASE_PATH = "http://localhost:9091".replace(/\/+$/, ""); + +/** + * + * @export + */ +export const COLLECTION_FORMATS = { + csv: ",", + ssv: " ", + tsv: "\t", + pipes: "|", +}; + +/** + * + * @export + * @interface RequestArgs + */ +export interface RequestArgs { + url: string; + options: RawAxiosRequestConfig; +} + +/** + * + * @export + * @class BaseAPI + */ +export class BaseAPI { + protected configuration: Configuration | undefined; + + constructor( + configuration?: Configuration, + protected basePath: string = BASE_PATH, + protected axios: AxiosInstance = globalAxios, + ) { + if (configuration) { + this.configuration = configuration; + this.basePath = configuration.basePath ?? basePath; + } + } +} + +/** + * + * @export + * @class RequiredError + * @extends {Error} + */ +export class RequiredError extends Error { + constructor( + public field: string, + msg?: string, + ) { + super(msg); + this.name = "RequiredError"; + } +} + +interface ServerMap { + [key: string]: { + url: string; + description: string; + }[]; +} + +/** + * + * @export + */ +export const operationServerMap: ServerMap = {}; diff --git a/client/src/api/main/generated/common.ts b/client/src/api/main/generated/common.ts new file mode 100644 index 00000000..6369966c --- /dev/null +++ b/client/src/api/main/generated/common.ts @@ -0,0 +1,202 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Team Server Down + * DevOps Application + * + * The version of the OpenAPI document: v0.0.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import type { Configuration } from "./configuration"; +import type { RequestArgs } from "./base"; +import type { AxiosInstance, AxiosResponse } from "axios"; +import { RequiredError } from "./base"; + +/** + * + * @export + */ +export const DUMMY_BASE_URL = "https://example.com"; + +/** + * + * @throws {RequiredError} + * @export + */ +export const assertParamExists = function ( + functionName: string, + paramName: string, + paramValue: unknown, +) { + if (paramValue === null || paramValue === undefined) { + throw new RequiredError( + paramName, + `Required parameter ${paramName} was null or undefined when calling ${functionName}.`, + ); + } +}; + +/** + * + * @export + */ +export const setApiKeyToObject = async function ( + object: any, + keyParamName: string, + configuration?: Configuration, +) { + if (configuration && configuration.apiKey) { + const localVarApiKeyValue = + typeof configuration.apiKey === "function" + ? await configuration.apiKey(keyParamName) + : await configuration.apiKey; + object[keyParamName] = localVarApiKeyValue; + } +}; + +/** + * + * @export + */ +export const setBasicAuthToObject = function ( + object: any, + configuration?: Configuration, +) { + if (configuration && (configuration.username || configuration.password)) { + object["auth"] = { + username: configuration.username, + password: configuration.password, + }; + } +}; + +/** + * + * @export + */ +export const setBearerAuthToObject = async function ( + object: any, + configuration?: Configuration, +) { + if (configuration && configuration.accessToken) { + const accessToken = + typeof configuration.accessToken === "function" + ? await configuration.accessToken() + : await configuration.accessToken; + object["Authorization"] = "Bearer " + accessToken; + } +}; + +/** + * + * @export + */ +export const setOAuthToObject = async function ( + object: any, + name: string, + scopes: string[], + configuration?: Configuration, +) { + if (configuration && configuration.accessToken) { + const localVarAccessTokenValue = + typeof configuration.accessToken === "function" + ? await configuration.accessToken(name, scopes) + : await configuration.accessToken; + object["Authorization"] = "Bearer " + localVarAccessTokenValue; + } +}; + +function setFlattenedQueryParams( + urlSearchParams: URLSearchParams, + parameter: any, + key: string = "", +): void { + if (parameter == null) return; + if (typeof parameter === "object") { + if (Array.isArray(parameter)) { + (parameter as any[]).forEach((item) => + setFlattenedQueryParams(urlSearchParams, item, key), + ); + } else { + Object.keys(parameter).forEach((currentKey) => + setFlattenedQueryParams( + urlSearchParams, + parameter[currentKey], + `${key}${key !== "" ? "." : ""}${currentKey}`, + ), + ); + } + } else { + if (urlSearchParams.has(key)) { + urlSearchParams.append(key, parameter); + } else { + urlSearchParams.set(key, parameter); + } + } +} + +/** + * + * @export + */ +export const setSearchParams = function (url: URL, ...objects: any[]) { + const searchParams = new URLSearchParams(url.search); + setFlattenedQueryParams(searchParams, objects); + url.search = searchParams.toString(); +}; + +/** + * + * @export + */ +export const serializeDataIfNeeded = function ( + value: any, + requestOptions: any, + configuration?: Configuration, +) { + const nonString = typeof value !== "string"; + const needsSerialization = + nonString && configuration && configuration.isJsonMime + ? configuration.isJsonMime(requestOptions.headers["Content-Type"]) + : nonString; + return needsSerialization + ? JSON.stringify(value !== undefined ? value : {}) + : value || ""; +}; + +/** + * + * @export + */ +export const toPathString = function (url: URL) { + return url.pathname + url.search + url.hash; +}; + +/** + * + * @export + */ +export const createRequestFunction = function ( + axiosArgs: RequestArgs, + globalAxios: AxiosInstance, + BASE_PATH: string, + configuration?: Configuration, +) { + return >( + axios: AxiosInstance = globalAxios, + basePath: string = BASE_PATH, + ) => { + const axiosRequestArgs = { + ...axiosArgs.options, + url: + (axios.defaults.baseURL ? "" : (configuration?.basePath ?? basePath)) + + axiosArgs.url, + }; + return axios.request(axiosRequestArgs); + }; +}; diff --git a/client/src/api/main/generated/configuration.ts b/client/src/api/main/generated/configuration.ts new file mode 100644 index 00000000..e262744c --- /dev/null +++ b/client/src/api/main/generated/configuration.ts @@ -0,0 +1,137 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Team Server Down + * DevOps Application + * + * The version of the OpenAPI document: v0.0.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +export interface ConfigurationParameters { + apiKey?: + | string + | Promise + | ((name: string) => string) + | ((name: string) => Promise); + username?: string; + password?: string; + accessToken?: + | string + | Promise + | ((name?: string, scopes?: string[]) => string) + | ((name?: string, scopes?: string[]) => Promise); + basePath?: string; + serverIndex?: number; + baseOptions?: any; + formDataCtor?: new () => any; +} + +export class Configuration { + /** + * parameter for apiKey security + * @param name security name + * @memberof Configuration + */ + apiKey?: + | string + | Promise + | ((name: string) => string) + | ((name: string) => Promise); + /** + * parameter for basic security + * + * @type {string} + * @memberof Configuration + */ + username?: string; + /** + * parameter for basic security + * + * @type {string} + * @memberof Configuration + */ + password?: string; + /** + * parameter for oauth2 security + * @param name security name + * @param scopes oauth2 scope + * @memberof Configuration + */ + accessToken?: + | string + | Promise + | ((name?: string, scopes?: string[]) => string) + | ((name?: string, scopes?: string[]) => Promise); + /** + * override base path + * + * @type {string} + * @memberof Configuration + */ + basePath?: string; + /** + * override server index + * + * @type {number} + * @memberof Configuration + */ + serverIndex?: number; + /** + * base options for axios calls + * + * @type {any} + * @memberof Configuration + */ + baseOptions?: any; + /** + * The FormData constructor that will be used to create multipart form data + * requests. You can inject this here so that execution environments that + * do not support the FormData class can still run the generated client. + * + * @type {new () => FormData} + */ + formDataCtor?: new () => any; + + constructor(param: ConfigurationParameters = {}) { + this.apiKey = param.apiKey; + this.username = param.username; + this.password = param.password; + this.accessToken = param.accessToken; + this.basePath = param.basePath; + this.serverIndex = param.serverIndex; + this.baseOptions = { + ...param.baseOptions, + headers: { + ...param.baseOptions?.headers, + }, + }; + this.formDataCtor = param.formDataCtor; + } + + /** + * Check if the given MIME is a JSON MIME. + * JSON MIME examples: + * application/json + * application/json; charset=UTF8 + * APPLICATION/JSON + * application/vnd.company+json + * @param mime - MIME (Multipurpose Internet Mail Extensions) + * @return True if the given MIME is JSON, false otherwise. + */ + public isJsonMime(mime: string): boolean { + const jsonMime: RegExp = new RegExp( + "^(application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(;.*)?$", + "i", + ); + return ( + mime !== null && + (jsonMime.test(mime) || + mime.toLowerCase() === "application/json-patch+json") + ); + } +} diff --git a/client/src/api/main/generated/docs/AccountApi.md b/client/src/api/main/generated/docs/AccountApi.md new file mode 100644 index 00000000..01123a4f --- /dev/null +++ b/client/src/api/main/generated/docs/AccountApi.md @@ -0,0 +1,51 @@ +# AccountApi + +All URIs are relative to *http://localhost:9091* + +|Method | HTTP request | Description| +|------------- | ------------- | -------------| +|[**getCurrentUser**](#getcurrentuser) | **GET** /me | | + +# **getCurrentUser** +> UserResponse getCurrentUser() + + +### Example + +```typescript +import { + AccountApi, + Configuration +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new AccountApi(configuration); + +const { status, data } = await apiInstance.getCurrentUser(); +``` + +### Parameters +This endpoint does not have any parameters. + + +### Return type + +**UserResponse** + +### Authorization + +[keycloak](../README.md#keycloak) + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: */* + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | OK | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/client/src/api/main/generated/docs/AuthenticatedUser.md b/client/src/api/main/generated/docs/AuthenticatedUser.md new file mode 100644 index 00000000..339fac94 --- /dev/null +++ b/client/src/api/main/generated/docs/AuthenticatedUser.md @@ -0,0 +1,20 @@ +# AuthenticatedUser + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**userId** | **string** | | [optional] [default to undefined] + +## Example + +```typescript +import { AuthenticatedUser } from './api'; + +const instance: AuthenticatedUser = { + userId, +}; +``` + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/client/src/api/main/generated/docs/CreateWhiteboardRequest.md b/client/src/api/main/generated/docs/CreateWhiteboardRequest.md new file mode 100644 index 00000000..10692ebb --- /dev/null +++ b/client/src/api/main/generated/docs/CreateWhiteboardRequest.md @@ -0,0 +1,22 @@ +# CreateWhiteboardRequest + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**userId** | **number** | | [optional] [default to undefined] +**title** | **string** | | [optional] [default to undefined] + +## Example + +```typescript +import { CreateWhiteboardRequest } from './api'; + +const instance: CreateWhiteboardRequest = { + userId, + title, +}; +``` + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/client/src/api/main/generated/docs/DefaultApi.md b/client/src/api/main/generated/docs/DefaultApi.md new file mode 100644 index 00000000..b33e7713 --- /dev/null +++ b/client/src/api/main/generated/docs/DefaultApi.md @@ -0,0 +1,210 @@ +# DefaultApi + +All URIs are relative to *http://localhost* + +|Method | HTTP request | Description| +|------------- | ------------- | -------------| +|[**completeTextCompletionPost**](#completetextcompletionpost) | **POST** /completion | Complete Text| +|[**healthCheckHealthGet**](#healthcheckhealthget) | **GET** /health | Health Check| +|[**rephraseTextRephrasePost**](#rephrasetextrephrasepost) | **POST** /rephrase | Rephrase Text| +|[**summarizeTextSummarizationPost**](#summarizetextsummarizationpost) | **POST** /summarization | Summarize Text| + +# **completeTextCompletionPost** +> TextResponse completeTextCompletionPost(textRequest) + + +### Example + +```typescript +import { + DefaultApi, + Configuration, + TextRequest +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new DefaultApi(configuration); + +let textRequest: TextRequest; // + +const { status, data } = await apiInstance.completeTextCompletionPost( + textRequest +); +``` + +### Parameters + +|Name | Type | Description | Notes| +|------------- | ------------- | ------------- | -------------| +| **textRequest** | **TextRequest**| | | + + +### Return type + +**TextResponse** + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | Successful Response | - | +|**422** | Validation Error | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **healthCheckHealthGet** +> any healthCheckHealthGet() + + +### Example + +```typescript +import { + DefaultApi, + Configuration +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new DefaultApi(configuration); + +const { status, data } = await apiInstance.healthCheckHealthGet(); +``` + +### Parameters +This endpoint does not have any parameters. + + +### Return type + +**any** + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: application/json + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | Successful Response | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **rephraseTextRephrasePost** +> TextResponse rephraseTextRephrasePost(textRequest) + + +### Example + +```typescript +import { + DefaultApi, + Configuration, + TextRequest +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new DefaultApi(configuration); + +let textRequest: TextRequest; // + +const { status, data } = await apiInstance.rephraseTextRephrasePost( + textRequest +); +``` + +### Parameters + +|Name | Type | Description | Notes| +|------------- | ------------- | ------------- | -------------| +| **textRequest** | **TextRequest**| | | + + +### Return type + +**TextResponse** + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | Successful Response | - | +|**422** | Validation Error | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **summarizeTextSummarizationPost** +> TextResponse summarizeTextSummarizationPost(textRequest) + + +### Example + +```typescript +import { + DefaultApi, + Configuration, + TextRequest +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new DefaultApi(configuration); + +let textRequest: TextRequest; // + +const { status, data } = await apiInstance.summarizeTextSummarizationPost( + textRequest +); +``` + +### Parameters + +|Name | Type | Description | Notes| +|------------- | ------------- | ------------- | -------------| +| **textRequest** | **TextRequest**| | | + + +### Return type + +**TextResponse** + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: application/json + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | Successful Response | - | +|**422** | Validation Error | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/client/src/api/main/generated/docs/Edge.md b/client/src/api/main/generated/docs/Edge.md new file mode 100644 index 00000000..ff4ceb59 --- /dev/null +++ b/client/src/api/main/generated/docs/Edge.md @@ -0,0 +1,30 @@ +# Edge + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | **string** | | [optional] [default to undefined] +**whiteboardId** | **number** | | [optional] [default to undefined] +**source** | **string** | | [optional] [default to undefined] +**sourceHandle** | **string** | | [optional] [default to undefined] +**target** | **string** | | [optional] [default to undefined] +**targetHandle** | **string** | | [optional] [default to undefined] + +## Example + +```typescript +import { Edge } from './api'; + +const instance: Edge = { + id, + whiteboardId, + source, + sourceHandle, + target, + targetHandle, +}; +``` + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/client/src/api/main/generated/docs/EdgeControllerApi.md b/client/src/api/main/generated/docs/EdgeControllerApi.md new file mode 100644 index 00000000..15011518 --- /dev/null +++ b/client/src/api/main/generated/docs/EdgeControllerApi.md @@ -0,0 +1,161 @@ +# EdgeControllerApi + +All URIs are relative to *http://localhost:9091* + +|Method | HTTP request | Description| +|------------- | ------------- | -------------| +|[**addEdge**](#addedge) | **POST** /edge | | +|[**deleteEdge**](#deleteedge) | **DELETE** /edge/{id} | | +|[**getEdgesByWhiteboard**](#getedgesbywhiteboard) | **GET** /edge/whiteboard/{whiteboardId} | | + +# **addEdge** +> Edge addEdge(edge) + + +### Example + +```typescript +import { + EdgeControllerApi, + Configuration, + Edge +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new EdgeControllerApi(configuration); + +let edge: Edge; // + +const { status, data } = await apiInstance.addEdge( + edge +); +``` + +### Parameters + +|Name | Type | Description | Notes| +|------------- | ------------- | ------------- | -------------| +| **edge** | **Edge**| | | + + +### Return type + +**Edge** + +### Authorization + +[keycloak](../README.md#keycloak) + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: */* + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | OK | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **deleteEdge** +> deleteEdge() + + +### Example + +```typescript +import { + EdgeControllerApi, + Configuration +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new EdgeControllerApi(configuration); + +let id: string; // (default to undefined) + +const { status, data } = await apiInstance.deleteEdge( + id +); +``` + +### Parameters + +|Name | Type | Description | Notes| +|------------- | ------------- | ------------- | -------------| +| **id** | [**string**] | | defaults to undefined| + + +### Return type + +void (empty response body) + +### Authorization + +[keycloak](../README.md#keycloak) + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | OK | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **getEdgesByWhiteboard** +> Array getEdgesByWhiteboard() + + +### Example + +```typescript +import { + EdgeControllerApi, + Configuration +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new EdgeControllerApi(configuration); + +let whiteboardId: number; // (default to undefined) + +const { status, data } = await apiInstance.getEdgesByWhiteboard( + whiteboardId +); +``` + +### Parameters + +|Name | Type | Description | Notes| +|------------- | ------------- | ------------- | -------------| +| **whiteboardId** | [**number**] | | defaults to undefined| + + +### Return type + +**Array** + +### Authorization + +[keycloak](../README.md#keycloak) + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: */* + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | OK | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/client/src/api/main/generated/docs/HTTPValidationError.md b/client/src/api/main/generated/docs/HTTPValidationError.md new file mode 100644 index 00000000..7fe160d8 --- /dev/null +++ b/client/src/api/main/generated/docs/HTTPValidationError.md @@ -0,0 +1,20 @@ +# HTTPValidationError + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**detail** | [**Array<ValidationError>**](ValidationError.md) | | [optional] [default to undefined] + +## Example + +```typescript +import { HTTPValidationError } from './api'; + +const instance: HTTPValidationError = { + detail, +}; +``` + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/client/src/api/main/generated/docs/InviteCollaboratorsRequest.md b/client/src/api/main/generated/docs/InviteCollaboratorsRequest.md new file mode 100644 index 00000000..4ae4a5f2 --- /dev/null +++ b/client/src/api/main/generated/docs/InviteCollaboratorsRequest.md @@ -0,0 +1,20 @@ +# InviteCollaboratorsRequest + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**emails** | **Array<string>** | | [optional] [default to undefined] + +## Example + +```typescript +import { InviteCollaboratorsRequest } from './api'; + +const instance: InviteCollaboratorsRequest = { + emails, +}; +``` + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/client/src/api/main/generated/docs/InviteUsersRequest.md b/client/src/api/main/generated/docs/InviteUsersRequest.md new file mode 100644 index 00000000..f98fa3c3 --- /dev/null +++ b/client/src/api/main/generated/docs/InviteUsersRequest.md @@ -0,0 +1,20 @@ +# InviteUsersRequest + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**emails** | **Array<string>** | | [optional] [default to undefined] + +## Example + +```typescript +import { InviteUsersRequest } from './api'; + +const instance: InviteUsersRequest = { + emails, +}; +``` + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/client/src/api/main/generated/docs/LlmServiceControllerApi.md b/client/src/api/main/generated/docs/LlmServiceControllerApi.md new file mode 100644 index 00000000..a2cfb529 --- /dev/null +++ b/client/src/api/main/generated/docs/LlmServiceControllerApi.md @@ -0,0 +1,204 @@ +# LlmServiceControllerApi + +All URIs are relative to *http://localhost:9091* + +|Method | HTTP request | Description| +|------------- | ------------- | -------------| +|[**completeText**](#completetext) | **POST** /api/llm/completion | | +|[**healthCheck**](#healthcheck) | **GET** /api/llm/health | | +|[**rephraseText**](#rephrasetext) | **POST** /api/llm/rephrase | | +|[**summarizeText**](#summarizetext) | **POST** /api/llm/summarization | | + +# **completeText** +> { [key: string]: string; } completeText(requestBody) + + +### Example + +```typescript +import { + LlmServiceControllerApi, + Configuration +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new LlmServiceControllerApi(configuration); + +let requestBody: { [key: string]: Array; }; // + +const { status, data } = await apiInstance.completeText( + requestBody +); +``` + +### Parameters + +|Name | Type | Description | Notes| +|------------- | ------------- | ------------- | -------------| +| **requestBody** | **{ [key: string]: Array; }**| | | + + +### Return type + +**{ [key: string]: string; }** + +### Authorization + +[keycloak](../README.md#keycloak) + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: */* + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | OK | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **healthCheck** +> { [key: string]: string; } healthCheck() + + +### Example + +```typescript +import { + LlmServiceControllerApi, + Configuration +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new LlmServiceControllerApi(configuration); + +const { status, data } = await apiInstance.healthCheck(); +``` + +### Parameters +This endpoint does not have any parameters. + + +### Return type + +**{ [key: string]: string; }** + +### Authorization + +[keycloak](../README.md#keycloak) + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: */* + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | OK | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **rephraseText** +> { [key: string]: string; } rephraseText(requestBody) + + +### Example + +```typescript +import { + LlmServiceControllerApi, + Configuration +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new LlmServiceControllerApi(configuration); + +let requestBody: { [key: string]: Array; }; // + +const { status, data } = await apiInstance.rephraseText( + requestBody +); +``` + +### Parameters + +|Name | Type | Description | Notes| +|------------- | ------------- | ------------- | -------------| +| **requestBody** | **{ [key: string]: Array; }**| | | + + +### Return type + +**{ [key: string]: string; }** + +### Authorization + +[keycloak](../README.md#keycloak) + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: */* + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | OK | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **summarizeText** +> { [key: string]: string; } summarizeText(requestBody) + + +### Example + +```typescript +import { + LlmServiceControllerApi, + Configuration +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new LlmServiceControllerApi(configuration); + +let requestBody: { [key: string]: Array; }; // + +const { status, data } = await apiInstance.summarizeText( + requestBody +); +``` + +### Parameters + +|Name | Type | Description | Notes| +|------------- | ------------- | ------------- | -------------| +| **requestBody** | **{ [key: string]: Array; }**| | | + + +### Return type + +**{ [key: string]: string; }** + +### Authorization + +[keycloak](../README.md#keycloak) + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: */* + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | OK | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/client/src/api/main/generated/docs/Node.md b/client/src/api/main/generated/docs/Node.md new file mode 100644 index 00000000..7eb6254d --- /dev/null +++ b/client/src/api/main/generated/docs/Node.md @@ -0,0 +1,58 @@ +# Node + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | **string** | | [optional] [default to undefined] +**whiteboardId** | **number** | | [optional] [default to undefined] +**type** | **string** | | [optional] [default to undefined] +**positionX** | **number** | | [optional] [default to undefined] +**positionY** | **number** | | [optional] [default to undefined] +**label** | **string** | | [optional] [default to undefined] +**width** | **number** | | [optional] [default to undefined] +**height** | **number** | | [optional] [default to undefined] +**color** | **string** | | [optional] [default to undefined] +**borderColor** | **string** | | [optional] [default to undefined] +**borderWidth** | **number** | | [optional] [default to undefined] +**borderOpacity** | **number** | | [optional] [default to undefined] +**opacity** | **number** | | [optional] [default to undefined] +**textColor** | **string** | | [optional] [default to undefined] +**fontSize** | **number** | | [optional] [default to undefined] +**fontFamily** | **string** | | [optional] [default to undefined] +**bold** | **boolean** | | [optional] [default to undefined] +**italic** | **boolean** | | [optional] [default to undefined] +**strikethrough** | **boolean** | | [optional] [default to undefined] +**underline** | **boolean** | | [optional] [default to undefined] + +## Example + +```typescript +import { Node } from './api'; + +const instance: Node = { + id, + whiteboardId, + type, + positionX, + positionY, + label, + width, + height, + color, + borderColor, + borderWidth, + borderOpacity, + opacity, + textColor, + fontSize, + fontFamily, + bold, + italic, + strikethrough, + underline, +}; +``` + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/client/src/api/main/generated/docs/NodeControllerApi.md b/client/src/api/main/generated/docs/NodeControllerApi.md new file mode 100644 index 00000000..2d91d6eb --- /dev/null +++ b/client/src/api/main/generated/docs/NodeControllerApi.md @@ -0,0 +1,216 @@ +# NodeControllerApi + +All URIs are relative to *http://localhost:9091* + +|Method | HTTP request | Description| +|------------- | ------------- | -------------| +|[**createNode**](#createnode) | **POST** /nodes | | +|[**deleteNode**](#deletenode) | **DELETE** /nodes/{id} | | +|[**getAllByWhiteboardId**](#getallbywhiteboardid) | **GET** /nodes/whiteboard/{whiteboardId} | | +|[**patchNode**](#patchnode) | **PATCH** /nodes/nodes/{id} | | + +# **createNode** +> Node createNode(node) + + +### Example + +```typescript +import { + NodeControllerApi, + Configuration, + Node +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new NodeControllerApi(configuration); + +let node: Node; // + +const { status, data } = await apiInstance.createNode( + node +); +``` + +### Parameters + +|Name | Type | Description | Notes| +|------------- | ------------- | ------------- | -------------| +| **node** | **Node**| | | + + +### Return type + +**Node** + +### Authorization + +[keycloak](../README.md#keycloak) + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: */* + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | OK | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **deleteNode** +> deleteNode() + + +### Example + +```typescript +import { + NodeControllerApi, + Configuration +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new NodeControllerApi(configuration); + +let id: string; // (default to undefined) + +const { status, data } = await apiInstance.deleteNode( + id +); +``` + +### Parameters + +|Name | Type | Description | Notes| +|------------- | ------------- | ------------- | -------------| +| **id** | [**string**] | | defaults to undefined| + + +### Return type + +void (empty response body) + +### Authorization + +[keycloak](../README.md#keycloak) + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | OK | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **getAllByWhiteboardId** +> Array getAllByWhiteboardId() + + +### Example + +```typescript +import { + NodeControllerApi, + Configuration +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new NodeControllerApi(configuration); + +let whiteboardId: number; // (default to undefined) + +const { status, data } = await apiInstance.getAllByWhiteboardId( + whiteboardId +); +``` + +### Parameters + +|Name | Type | Description | Notes| +|------------- | ------------- | ------------- | -------------| +| **whiteboardId** | [**number**] | | defaults to undefined| + + +### Return type + +**Array** + +### Authorization + +[keycloak](../README.md#keycloak) + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: */* + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | OK | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **patchNode** +> Node patchNode(updateNodeRequest) + + +### Example + +```typescript +import { + NodeControllerApi, + Configuration, + UpdateNodeRequest +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new NodeControllerApi(configuration); + +let id: string; // (default to undefined) +let updateNodeRequest: UpdateNodeRequest; // + +const { status, data } = await apiInstance.patchNode( + id, + updateNodeRequest +); +``` + +### Parameters + +|Name | Type | Description | Notes| +|------------- | ------------- | ------------- | -------------| +| **updateNodeRequest** | **UpdateNodeRequest**| | | +| **id** | [**string**] | | defaults to undefined| + + +### Return type + +**Node** + +### Authorization + +[keycloak](../README.md#keycloak) + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: */* + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | OK | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/client/src/api/main/generated/docs/NodeUpdateDTO.md b/client/src/api/main/generated/docs/NodeUpdateDTO.md new file mode 100644 index 00000000..1318a3e4 --- /dev/null +++ b/client/src/api/main/generated/docs/NodeUpdateDTO.md @@ -0,0 +1,54 @@ +# NodeUpdateDTO + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**type** | **string** | | [optional] [default to undefined] +**positionX** | **number** | | [optional] [default to undefined] +**positionY** | **number** | | [optional] [default to undefined] +**label** | **string** | | [optional] [default to undefined] +**width** | **number** | | [optional] [default to undefined] +**height** | **number** | | [optional] [default to undefined] +**color** | **string** | | [optional] [default to undefined] +**borderColor** | **string** | | [optional] [default to undefined] +**borderWidth** | **number** | | [optional] [default to undefined] +**borderOpacity** | **number** | | [optional] [default to undefined] +**opacity** | **number** | | [optional] [default to undefined] +**textColor** | **string** | | [optional] [default to undefined] +**fontSize** | **number** | | [optional] [default to undefined] +**fontFamily** | **string** | | [optional] [default to undefined] +**isBold** | **boolean** | | [optional] [default to undefined] +**isItalic** | **boolean** | | [optional] [default to undefined] +**isStrikethrough** | **boolean** | | [optional] [default to undefined] +**isUnderline** | **boolean** | | [optional] [default to undefined] + +## Example + +```typescript +import { NodeUpdateDTO } from './api'; + +const instance: NodeUpdateDTO = { + type, + positionX, + positionY, + label, + width, + height, + color, + borderColor, + borderWidth, + borderOpacity, + opacity, + textColor, + fontSize, + fontFamily, + isBold, + isItalic, + isStrikethrough, + isUnderline, +}; +``` + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/client/src/api/main/generated/docs/RemoveCollaboratorsRequest.md b/client/src/api/main/generated/docs/RemoveCollaboratorsRequest.md new file mode 100644 index 00000000..32a2ecc5 --- /dev/null +++ b/client/src/api/main/generated/docs/RemoveCollaboratorsRequest.md @@ -0,0 +1,20 @@ +# RemoveCollaboratorsRequest + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**userIds** | **Array<number>** | | [optional] [default to undefined] + +## Example + +```typescript +import { RemoveCollaboratorsRequest } from './api'; + +const instance: RemoveCollaboratorsRequest = { + userIds, +}; +``` + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/client/src/api/main/generated/docs/RootApi.md b/client/src/api/main/generated/docs/RootApi.md new file mode 100644 index 00000000..52f13206 --- /dev/null +++ b/client/src/api/main/generated/docs/RootApi.md @@ -0,0 +1,52 @@ +# RootApi + +All URIs are relative to *http://localhost:9091* + +|Method | HTTP request | Description| +|------------- | ------------- | -------------| +|[**root**](#root) | **GET** / | Root endpoint| + +# **root** +> string root() + +Returns a simple Hello World message. + +### Example + +```typescript +import { + RootApi, + Configuration +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new RootApi(configuration); + +const { status, data } = await apiInstance.root(); +``` + +### Parameters +This endpoint does not have any parameters. + + +### Return type + +**string** + +### Authorization + +[keycloak](../README.md#keycloak) + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: */* + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | OK | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/client/src/api/main/generated/docs/SaveWhiteboardStateRequest.md b/client/src/api/main/generated/docs/SaveWhiteboardStateRequest.md new file mode 100644 index 00000000..1635472a --- /dev/null +++ b/client/src/api/main/generated/docs/SaveWhiteboardStateRequest.md @@ -0,0 +1,24 @@ +# SaveWhiteboardStateRequest + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**nodes** | [**Array<Node>**](Node.md) | | [optional] [default to undefined] +**edges** | [**Array<Edge>**](Edge.md) | | [optional] [default to undefined] +**viewportResponse** | [**ViewportResponse**](ViewportResponse.md) | | [optional] [default to undefined] + +## Example + +```typescript +import { SaveWhiteboardStateRequest } from './api'; + +const instance: SaveWhiteboardStateRequest = { + nodes, + edges, + viewportResponse, +}; +``` + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/client/src/api/main/generated/docs/TextRequest.md b/client/src/api/main/generated/docs/TextRequest.md new file mode 100644 index 00000000..4501c09f --- /dev/null +++ b/client/src/api/main/generated/docs/TextRequest.md @@ -0,0 +1,20 @@ +# TextRequest + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**user_text** | **Array<string>** | | [default to undefined] + +## Example + +```typescript +import { TextRequest } from './api'; + +const instance: TextRequest = { + user_text, +}; +``` + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/client/src/api/main/generated/docs/TextResponse.md b/client/src/api/main/generated/docs/TextResponse.md new file mode 100644 index 00000000..9637caaf --- /dev/null +++ b/client/src/api/main/generated/docs/TextResponse.md @@ -0,0 +1,20 @@ +# TextResponse + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**llm_response** | **string** | | [default to undefined] + +## Example + +```typescript +import { TextResponse } from './api'; + +const instance: TextResponse = { + llm_response, +}; +``` + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/client/src/api/main/generated/docs/UpdateNodeRequest.md b/client/src/api/main/generated/docs/UpdateNodeRequest.md new file mode 100644 index 00000000..3a93fc4d --- /dev/null +++ b/client/src/api/main/generated/docs/UpdateNodeRequest.md @@ -0,0 +1,54 @@ +# UpdateNodeRequest + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**type** | **string** | | [optional] [default to undefined] +**positionX** | **number** | | [optional] [default to undefined] +**positionY** | **number** | | [optional] [default to undefined] +**label** | **string** | | [optional] [default to undefined] +**width** | **number** | | [optional] [default to undefined] +**height** | **number** | | [optional] [default to undefined] +**color** | **string** | | [optional] [default to undefined] +**borderColor** | **string** | | [optional] [default to undefined] +**borderWidth** | **number** | | [optional] [default to undefined] +**borderOpacity** | **number** | | [optional] [default to undefined] +**opacity** | **number** | | [optional] [default to undefined] +**textColor** | **string** | | [optional] [default to undefined] +**fontSize** | **number** | | [optional] [default to undefined] +**fontFamily** | **string** | | [optional] [default to undefined] +**isBold** | **boolean** | | [optional] [default to undefined] +**isItalic** | **boolean** | | [optional] [default to undefined] +**isStrikethrough** | **boolean** | | [optional] [default to undefined] +**isUnderline** | **boolean** | | [optional] [default to undefined] + +## Example + +```typescript +import { UpdateNodeRequest } from './api'; + +const instance: UpdateNodeRequest = { + type, + positionX, + positionY, + label, + width, + height, + color, + borderColor, + borderWidth, + borderOpacity, + opacity, + textColor, + fontSize, + fontFamily, + isBold, + isItalic, + isStrikethrough, + isUnderline, +}; +``` + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/client/src/api/main/generated/docs/User.md b/client/src/api/main/generated/docs/User.md new file mode 100644 index 00000000..1f2b08dc --- /dev/null +++ b/client/src/api/main/generated/docs/User.md @@ -0,0 +1,28 @@ +# User + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | **number** | | [optional] [default to undefined] +**firstName** | **string** | | [optional] [default to undefined] +**lastName** | **string** | | [optional] [default to undefined] +**username** | **string** | | [optional] [default to undefined] +**email** | **string** | | [optional] [default to undefined] + +## Example + +```typescript +import { User } from './api'; + +const instance: User = { + id, + firstName, + lastName, + username, + email, +}; +``` + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/client/src/api/main/generated/docs/UserResponse.md b/client/src/api/main/generated/docs/UserResponse.md new file mode 100644 index 00000000..dd6702c0 --- /dev/null +++ b/client/src/api/main/generated/docs/UserResponse.md @@ -0,0 +1,28 @@ +# UserResponse + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | **number** | | [optional] [default to undefined] +**firstName** | **string** | | [optional] [default to undefined] +**lastName** | **string** | | [optional] [default to undefined] +**username** | **string** | | [optional] [default to undefined] +**email** | **string** | | [optional] [default to undefined] + +## Example + +```typescript +import { UserResponse } from './api'; + +const instance: UserResponse = { + id, + firstName, + lastName, + username, + email, +}; +``` + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/client/src/api/main/generated/docs/ValidationError.md b/client/src/api/main/generated/docs/ValidationError.md new file mode 100644 index 00000000..d2e7ec10 --- /dev/null +++ b/client/src/api/main/generated/docs/ValidationError.md @@ -0,0 +1,24 @@ +# ValidationError + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**loc** | [**Array<ValidationErrorLocInner>**](ValidationErrorLocInner.md) | | [default to undefined] +**msg** | **string** | | [default to undefined] +**type** | **string** | | [default to undefined] + +## Example + +```typescript +import { ValidationError } from './api'; + +const instance: ValidationError = { + loc, + msg, + type, +}; +``` + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/client/src/api/main/generated/docs/ValidationErrorLocInner.md b/client/src/api/main/generated/docs/ValidationErrorLocInner.md new file mode 100644 index 00000000..8d54b451 --- /dev/null +++ b/client/src/api/main/generated/docs/ValidationErrorLocInner.md @@ -0,0 +1,18 @@ +# ValidationErrorLocInner + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- + +## Example + +```typescript +import { ValidationErrorLocInner } from './api'; + +const instance: ValidationErrorLocInner = { +}; +``` + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/client/src/api/main/generated/docs/Viewport.md b/client/src/api/main/generated/docs/Viewport.md new file mode 100644 index 00000000..80a04a10 --- /dev/null +++ b/client/src/api/main/generated/docs/Viewport.md @@ -0,0 +1,28 @@ +# Viewport + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | **number** | | [optional] [default to undefined] +**x** | **number** | | [optional] [default to undefined] +**y** | **number** | | [optional] [default to undefined] +**zoom** | **number** | | [optional] [default to undefined] +**whiteboardId** | **number** | | [optional] [default to undefined] + +## Example + +```typescript +import { Viewport } from './api'; + +const instance: Viewport = { + id, + x, + y, + zoom, + whiteboardId, +}; +``` + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/client/src/api/main/generated/docs/ViewportControllerApi.md b/client/src/api/main/generated/docs/ViewportControllerApi.md new file mode 100644 index 00000000..91b96b16 --- /dev/null +++ b/client/src/api/main/generated/docs/ViewportControllerApi.md @@ -0,0 +1,109 @@ +# ViewportControllerApi + +All URIs are relative to *http://localhost:9091* + +|Method | HTTP request | Description| +|------------- | ------------- | -------------| +|[**deleteViewport**](#deleteviewport) | **DELETE** /api/viewports/{id} | | +|[**getViewportByWhiteboardId**](#getviewportbywhiteboardid) | **GET** /api/viewports/whiteboard/{whiteboardId} | | + +# **deleteViewport** +> deleteViewport() + + +### Example + +```typescript +import { + ViewportControllerApi, + Configuration +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new ViewportControllerApi(configuration); + +let id: number; // (default to undefined) + +const { status, data } = await apiInstance.deleteViewport( + id +); +``` + +### Parameters + +|Name | Type | Description | Notes| +|------------- | ------------- | ------------- | -------------| +| **id** | [**number**] | | defaults to undefined| + + +### Return type + +void (empty response body) + +### Authorization + +[keycloak](../README.md#keycloak) + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | OK | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **getViewportByWhiteboardId** +> Viewport getViewportByWhiteboardId() + + +### Example + +```typescript +import { + ViewportControllerApi, + Configuration +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new ViewportControllerApi(configuration); + +let whiteboardId: number; // (default to undefined) + +const { status, data } = await apiInstance.getViewportByWhiteboardId( + whiteboardId +); +``` + +### Parameters + +|Name | Type | Description | Notes| +|------------- | ------------- | ------------- | -------------| +| **whiteboardId** | [**number**] | | defaults to undefined| + + +### Return type + +**Viewport** + +### Authorization + +[keycloak](../README.md#keycloak) + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: */* + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | OK | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/client/src/api/main/generated/docs/ViewportCreateRequest.md b/client/src/api/main/generated/docs/ViewportCreateRequest.md new file mode 100644 index 00000000..2e033227 --- /dev/null +++ b/client/src/api/main/generated/docs/ViewportCreateRequest.md @@ -0,0 +1,26 @@ +# ViewportCreateRequest + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**x** | **number** | | [optional] [default to undefined] +**y** | **number** | | [optional] [default to undefined] +**zoom** | **number** | | [optional] [default to undefined] +**whiteboardId** | **number** | | [optional] [default to undefined] + +## Example + +```typescript +import { ViewportCreateRequest } from './api'; + +const instance: ViewportCreateRequest = { + x, + y, + zoom, + whiteboardId, +}; +``` + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/client/src/api/main/generated/docs/ViewportDto.md b/client/src/api/main/generated/docs/ViewportDto.md new file mode 100644 index 00000000..ba8b79ce --- /dev/null +++ b/client/src/api/main/generated/docs/ViewportDto.md @@ -0,0 +1,24 @@ +# ViewportDto + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**x** | **number** | | [optional] [default to undefined] +**y** | **number** | | [optional] [default to undefined] +**zoom** | **number** | | [optional] [default to undefined] + +## Example + +```typescript +import { ViewportDto } from './api'; + +const instance: ViewportDto = { + x, + y, + zoom, +}; +``` + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/client/src/api/main/generated/docs/ViewportResponse.md b/client/src/api/main/generated/docs/ViewportResponse.md new file mode 100644 index 00000000..57189241 --- /dev/null +++ b/client/src/api/main/generated/docs/ViewportResponse.md @@ -0,0 +1,24 @@ +# ViewportResponse + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**x** | **number** | | [optional] [default to undefined] +**y** | **number** | | [optional] [default to undefined] +**zoom** | **number** | | [optional] [default to undefined] + +## Example + +```typescript +import { ViewportResponse } from './api'; + +const instance: ViewportResponse = { + x, + y, + zoom, +}; +``` + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/client/src/api/main/generated/docs/Whiteboard.md b/client/src/api/main/generated/docs/Whiteboard.md new file mode 100644 index 00000000..0458ddc2 --- /dev/null +++ b/client/src/api/main/generated/docs/Whiteboard.md @@ -0,0 +1,28 @@ +# Whiteboard + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | **number** | | [optional] [default to undefined] +**title** | **string** | | [optional] [default to undefined] +**createdAt** | **string** | | [optional] [default to undefined] +**lastUpdatedAt** | **string** | | [optional] [default to undefined] +**userId** | **number** | | [optional] [default to undefined] + +## Example + +```typescript +import { Whiteboard } from './api'; + +const instance: Whiteboard = { + id, + title, + createdAt, + lastUpdatedAt, + userId, +}; +``` + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/client/src/api/main/generated/docs/WhiteboardApi.md b/client/src/api/main/generated/docs/WhiteboardApi.md new file mode 100644 index 00000000..315bf079 --- /dev/null +++ b/client/src/api/main/generated/docs/WhiteboardApi.md @@ -0,0 +1,529 @@ +# WhiteboardApi + +All URIs are relative to *http://localhost:9091* + +|Method | HTTP request | Description| +|------------- | ------------- | -------------| +|[**createWhiteboard**](#createwhiteboard) | **POST** /whiteboards | Create whiteboard| +|[**deleteWhiteboard**](#deletewhiteboard) | **DELETE** /whiteboards/{id} | | +|[**getCollaborators**](#getcollaborators) | **GET** /whiteboards/{id}/collaborators | | +|[**getUserWhiteboards**](#getuserwhiteboards) | **GET** /whiteboards | Get whiteboards by user ID| +|[**getWhiteboardById**](#getwhiteboardbyid) | **GET** /whiteboards/{id} | | +|[**getWhiteboardTitle**](#getwhiteboardtitle) | **GET** /whiteboards/{id}/title | Get whiteboard title| +|[**inviteCollaborators**](#invitecollaborators) | **POST** /whiteboards/{id}/invitations | Invite users to collaborate on the whiteboard| +|[**removeCollaborators**](#removecollaborators) | **DELETE** /whiteboards/{id}/invitations | Remove collaborators from the whiteboard| +|[**saveWhiteboardState**](#savewhiteboardstate) | **POST** /whiteboards/{whiteboardId}/save | | +|[**updateTitle**](#updatetitle) | **PUT** /whiteboards/{id}/title | Update title| + +# **createWhiteboard** +> WhiteboardResponse createWhiteboard() + +Creates a new whiteboard for a user. + +### Example + +```typescript +import { + WhiteboardApi, + Configuration +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new WhiteboardApi(configuration); + +let title: string; // (default to undefined) + +const { status, data } = await apiInstance.createWhiteboard( + title +); +``` + +### Parameters + +|Name | Type | Description | Notes| +|------------- | ------------- | ------------- | -------------| +| **title** | [**string**] | | defaults to undefined| + + +### Return type + +**WhiteboardResponse** + +### Authorization + +[keycloak](../README.md#keycloak) + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: */* + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | OK | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **deleteWhiteboard** +> deleteWhiteboard() + + +### Example + +```typescript +import { + WhiteboardApi, + Configuration +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new WhiteboardApi(configuration); + +let id: number; // (default to undefined) + +const { status, data } = await apiInstance.deleteWhiteboard( + id +); +``` + +### Parameters + +|Name | Type | Description | Notes| +|------------- | ------------- | ------------- | -------------| +| **id** | [**number**] | | defaults to undefined| + + +### Return type + +void (empty response body) + +### Authorization + +[keycloak](../README.md#keycloak) + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: Not defined + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | OK | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **getCollaborators** +> Array getCollaborators() + + +### Example + +```typescript +import { + WhiteboardApi, + Configuration +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new WhiteboardApi(configuration); + +let id: number; //ID of the whiteboard (default to undefined) + +const { status, data } = await apiInstance.getCollaborators( + id +); +``` + +### Parameters + +|Name | Type | Description | Notes| +|------------- | ------------- | ------------- | -------------| +| **id** | [**number**] | ID of the whiteboard | defaults to undefined| + + +### Return type + +**Array** + +### Authorization + +[keycloak](../README.md#keycloak) + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: */* + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | OK | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **getUserWhiteboards** +> Array getUserWhiteboards() + +Returns a list of whiteboards for the current user. + +### Example + +```typescript +import { + WhiteboardApi, + Configuration +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new WhiteboardApi(configuration); + +const { status, data } = await apiInstance.getUserWhiteboards(); +``` + +### Parameters +This endpoint does not have any parameters. + + +### Return type + +**Array** + +### Authorization + +[keycloak](../README.md#keycloak) + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: */* + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | OK | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **getWhiteboardById** +> WhiteboardResponse getWhiteboardById() + + +### Example + +```typescript +import { + WhiteboardApi, + Configuration +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new WhiteboardApi(configuration); + +let id: number; //ID of the whiteboard (default to undefined) + +const { status, data } = await apiInstance.getWhiteboardById( + id +); +``` + +### Parameters + +|Name | Type | Description | Notes| +|------------- | ------------- | ------------- | -------------| +| **id** | [**number**] | ID of the whiteboard | defaults to undefined| + + +### Return type + +**WhiteboardResponse** + +### Authorization + +[keycloak](../README.md#keycloak) + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: */* + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | OK | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **getWhiteboardTitle** +> string getWhiteboardTitle() + +Returns the title of a whiteboard by its ID + +### Example + +```typescript +import { + WhiteboardApi, + Configuration +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new WhiteboardApi(configuration); + +let id: number; //ID of the whiteboard (default to undefined) + +const { status, data } = await apiInstance.getWhiteboardTitle( + id +); +``` + +### Parameters + +|Name | Type | Description | Notes| +|------------- | ------------- | ------------- | -------------| +| **id** | [**number**] | ID of the whiteboard | defaults to undefined| + + +### Return type + +**string** + +### Authorization + +[keycloak](../README.md#keycloak) + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: */* + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | OK | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **inviteCollaborators** +> inviteCollaborators(inviteCollaboratorsRequest) + + +### Example + +```typescript +import { + WhiteboardApi, + Configuration, + InviteCollaboratorsRequest +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new WhiteboardApi(configuration); + +let id: number; //ID of the whiteboard (default to undefined) +let inviteCollaboratorsRequest: InviteCollaboratorsRequest; // + +const { status, data } = await apiInstance.inviteCollaborators( + id, + inviteCollaboratorsRequest +); +``` + +### Parameters + +|Name | Type | Description | Notes| +|------------- | ------------- | ------------- | -------------| +| **inviteCollaboratorsRequest** | **InviteCollaboratorsRequest**| | | +| **id** | [**number**] | ID of the whiteboard | defaults to undefined| + + +### Return type + +void (empty response body) + +### Authorization + +[keycloak](../README.md#keycloak) + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: Not defined + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | OK | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **removeCollaborators** +> removeCollaborators(removeCollaboratorsRequest) + + +### Example + +```typescript +import { + WhiteboardApi, + Configuration, + RemoveCollaboratorsRequest +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new WhiteboardApi(configuration); + +let id: number; //ID of the whiteboard (default to undefined) +let removeCollaboratorsRequest: RemoveCollaboratorsRequest; // + +const { status, data } = await apiInstance.removeCollaborators( + id, + removeCollaboratorsRequest +); +``` + +### Parameters + +|Name | Type | Description | Notes| +|------------- | ------------- | ------------- | -------------| +| **removeCollaboratorsRequest** | **RemoveCollaboratorsRequest**| | | +| **id** | [**number**] | ID of the whiteboard | defaults to undefined| + + +### Return type + +void (empty response body) + +### Authorization + +[keycloak](../README.md#keycloak) + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: Not defined + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | OK | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **saveWhiteboardState** +> saveWhiteboardState(saveWhiteboardStateRequest) + + +### Example + +```typescript +import { + WhiteboardApi, + Configuration, + SaveWhiteboardStateRequest +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new WhiteboardApi(configuration); + +let whiteboardId: number; // (default to undefined) +let saveWhiteboardStateRequest: SaveWhiteboardStateRequest; // + +const { status, data } = await apiInstance.saveWhiteboardState( + whiteboardId, + saveWhiteboardStateRequest +); +``` + +### Parameters + +|Name | Type | Description | Notes| +|------------- | ------------- | ------------- | -------------| +| **saveWhiteboardStateRequest** | **SaveWhiteboardStateRequest**| | | +| **whiteboardId** | [**number**] | | defaults to undefined| + + +### Return type + +void (empty response body) + +### Authorization + +[keycloak](../README.md#keycloak) + +### HTTP request headers + + - **Content-Type**: application/json + - **Accept**: Not defined + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | OK | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + +# **updateTitle** +> string updateTitle() + +Updates the title of an existing whiteboard. + +### Example + +```typescript +import { + WhiteboardApi, + Configuration +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new WhiteboardApi(configuration); + +let id: number; //ID of the whiteboard (default to undefined) +let title: string; // (default to undefined) + +const { status, data } = await apiInstance.updateTitle( + id, + title +); +``` + +### Parameters + +|Name | Type | Description | Notes| +|------------- | ------------- | ------------- | -------------| +| **id** | [**number**] | ID of the whiteboard | defaults to undefined| +| **title** | [**string**] | | defaults to undefined| + + +### Return type + +**string** + +### Authorization + +[keycloak](../README.md#keycloak) + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: */* + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | OK | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/client/src/api/main/generated/docs/WhiteboardResponse.md b/client/src/api/main/generated/docs/WhiteboardResponse.md new file mode 100644 index 00000000..d8f9353c --- /dev/null +++ b/client/src/api/main/generated/docs/WhiteboardResponse.md @@ -0,0 +1,28 @@ +# WhiteboardResponse + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**id** | **number** | | [optional] [default to undefined] +**title** | **string** | | [optional] [default to undefined] +**user** | [**User**](User.md) | | [optional] [default to undefined] +**createdAt** | **string** | | [optional] [default to undefined] +**lastUpdatedAt** | **string** | | [optional] [default to undefined] + +## Example + +```typescript +import { WhiteboardResponse } from './api'; + +const instance: WhiteboardResponse = { + id, + title, + user, + createdAt, + lastUpdatedAt, +}; +``` + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/client/src/api/main/generated/docs/WhiteboardStateDto.md b/client/src/api/main/generated/docs/WhiteboardStateDto.md new file mode 100644 index 00000000..a31b74ba --- /dev/null +++ b/client/src/api/main/generated/docs/WhiteboardStateDto.md @@ -0,0 +1,24 @@ +# WhiteboardStateDto + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**nodes** | [**Array<Node>**](Node.md) | | [optional] [default to undefined] +**edges** | [**Array<Edge>**](Edge.md) | | [optional] [default to undefined] +**viewportResponse** | [**ViewportDto**](ViewportDto.md) | | [optional] [default to undefined] + +## Example + +```typescript +import { WhiteboardStateDto } from './api'; + +const instance: WhiteboardStateDto = { + nodes, + edges, + viewportResponse, +}; +``` + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/client/src/api/main/generated/git_push.sh b/client/src/api/main/generated/git_push.sh new file mode 100644 index 00000000..f53a75d4 --- /dev/null +++ b/client/src/api/main/generated/git_push.sh @@ -0,0 +1,57 @@ +#!/bin/sh +# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ +# +# Usage example: /bin/sh ./git_push.sh wing328 openapi-petstore-perl "minor update" "gitlab.com" + +git_user_id=$1 +git_repo_id=$2 +release_note=$3 +git_host=$4 + +if [ "$git_host" = "" ]; then + git_host="github.com" + echo "[INFO] No command line input provided. Set \$git_host to $git_host" +fi + +if [ "$git_user_id" = "" ]; then + git_user_id="GIT_USER_ID" + echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" +fi + +if [ "$git_repo_id" = "" ]; then + git_repo_id="GIT_REPO_ID" + echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" +fi + +if [ "$release_note" = "" ]; then + release_note="Minor update" + echo "[INFO] No command line input provided. Set \$release_note to $release_note" +fi + +# Initialize the local directory as a Git repository +git init + +# Adds the files in the local repository and stages them for commit. +git add . + +# Commits the tracked changes and prepares them to be pushed to a remote repository. +git commit -m "$release_note" + +# Sets the new remote +git_remote=$(git remote) +if [ "$git_remote" = "" ]; then # git remote not defined + + if [ "$GIT_TOKEN" = "" ]; then + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." + git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git + else + git remote add origin https://${git_user_id}:"${GIT_TOKEN}"@${git_host}/${git_user_id}/${git_repo_id}.git + fi + +fi + +git pull origin master + +# Pushes (Forces) the changes in the local repository up to the remote repository +echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git" +git push origin master 2>&1 | grep -v 'To https' diff --git a/client/src/api/main/generated/index.ts b/client/src/api/main/generated/index.ts new file mode 100644 index 00000000..3e2d6280 --- /dev/null +++ b/client/src/api/main/generated/index.ts @@ -0,0 +1,16 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Team Server Down + * DevOps Application + * + * The version of the OpenAPI document: v0.0.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +export * from "./api"; +export * from "./configuration"; diff --git a/client/src/api/realtime/dtos/WhiteboardEvent.ts b/client/src/api/realtime/dtos/WhiteboardEvent.ts new file mode 100644 index 00000000..48831452 --- /dev/null +++ b/client/src/api/realtime/dtos/WhiteboardEvent.ts @@ -0,0 +1,73 @@ +import { z } from "zod"; + +const Position = z.object({ + x: z.number(), + y: z.number(), +}); + +const NodeProperties = z.object({ + borderColor: z.string(), + borderOpacity: z.number(), + borderWidth: z.number(), + color: z.string(), + fontFamily: z.string(), + fontSize: z.number(), + isBold: z.boolean(), + isItalic: z.boolean(), + isStrikethrough: z.boolean(), + isUnderline: z.boolean(), + opacity: z.number(), + textColor: z.string(), +}); + +const Node = z.object({ + id: z.string(), + type: z.string(), + position: Position, + measured: z.object({ + width: z.number(), + height: z.number(), + }), + width: z.number().optional(), + height: z.number().optional(), + dragging: z.boolean().optional(), + selected: z.boolean().optional(), + data: z.object({ + label: z.string(), + shapeType: z.string().optional(), + nodeProperties: NodeProperties, + }), +}); + +const Edge = z.object({ + id: z.string(), + source: z.string(), + sourceHandle: z.string(), + target: z.string(), + targetHandle: z.string(), +}); + +const MousePositionEvent = z.object({ + type: z.literal("mousePosition"), + payload: z.object({ + id: z.number(), + username: z.string(), + position: Position.optional(), + }), +}); + +const NodePositionEvent = z.object({ + type: z.literal("nodePosition"), + payload: z.array(Node), +}); + +const EdgePositionEvent = z.object({ + type: z.literal("edgePosition"), + payload: z.array(Edge), +}); + +export const WhiteboardEvent = z.discriminatedUnion("type", [ + MousePositionEvent, + NodePositionEvent, + EdgePositionEvent, +]); diff --git a/client/src/api/realtime/generated/.gitignore b/client/src/api/realtime/generated/.gitignore new file mode 100644 index 00000000..149b5765 --- /dev/null +++ b/client/src/api/realtime/generated/.gitignore @@ -0,0 +1,4 @@ +wwwroot/*.js +node_modules +typings +dist diff --git a/client/src/api/realtime/generated/.npmignore b/client/src/api/realtime/generated/.npmignore new file mode 100644 index 00000000..999d88df --- /dev/null +++ b/client/src/api/realtime/generated/.npmignore @@ -0,0 +1 @@ +# empty npmignore to ensure all required files (e.g., in the dist folder) are published by npm \ No newline at end of file diff --git a/client/src/api/realtime/generated/.openapi-generator-ignore b/client/src/api/realtime/generated/.openapi-generator-ignore new file mode 100644 index 00000000..7484ee59 --- /dev/null +++ b/client/src/api/realtime/generated/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/client/src/api/realtime/generated/.openapi-generator/FILES b/client/src/api/realtime/generated/.openapi-generator/FILES new file mode 100644 index 00000000..09917f66 --- /dev/null +++ b/client/src/api/realtime/generated/.openapi-generator/FILES @@ -0,0 +1,10 @@ +.gitignore +.npmignore +api.ts +base.ts +common.ts +configuration.ts +docs/HandlerRootResponse.md +docs/RootApi.md +git_push.sh +index.ts diff --git a/client/src/api/realtime/generated/.openapi-generator/VERSION b/client/src/api/realtime/generated/.openapi-generator/VERSION new file mode 100644 index 00000000..eb1dc6a5 --- /dev/null +++ b/client/src/api/realtime/generated/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.13.0 diff --git a/client/src/api/realtime/generated/api.ts b/client/src/api/realtime/generated/api.ts new file mode 100644 index 00000000..950b3b95 --- /dev/null +++ b/client/src/api/realtime/generated/api.ts @@ -0,0 +1,190 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import type { Configuration } from "./configuration"; +import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from "axios"; +import globalAxios from "axios"; +// Some imports not used depending on template conditions +// @ts-ignore +import { + DUMMY_BASE_URL, + assertParamExists, + setApiKeyToObject, + setBasicAuthToObject, + setBearerAuthToObject, + setOAuthToObject, + setSearchParams, + serializeDataIfNeeded, + toPathString, + createRequestFunction, +} from "./common"; +import type { RequestArgs } from "./base"; +// @ts-ignore +import { + BASE_PATH, + COLLECTION_FORMATS, + BaseAPI, + RequiredError, + operationServerMap, +} from "./base"; + +/** + * + * @export + * @interface HandlerRootResponse + */ +export interface HandlerRootResponse { + /** + * + * @type {string} + * @memberof HandlerRootResponse + */ + message?: string; +} + +/** + * RootApi - axios parameter creator + * @export + */ +export const RootApiAxiosParamCreator = function ( + configuration?: Configuration, +) { + return { + /** + * + * @summary Get Root + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getRoot: async ( + options: RawAxiosRequestConfig = {}, + ): Promise => { + const localVarPath = `/`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "GET", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + }; +}; + +/** + * RootApi - functional programming interface + * @export + */ +export const RootApiFp = function (configuration?: Configuration) { + const localVarAxiosParamCreator = RootApiAxiosParamCreator(configuration); + return { + /** + * + * @summary Get Root + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getRoot( + options?: RawAxiosRequestConfig, + ): Promise< + ( + axios?: AxiosInstance, + basePath?: string, + ) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.getRoot(options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = + operationServerMap["RootApi.getRoot"]?.[localVarOperationServerIndex] + ?.url; + return (axios, basePath) => + createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration, + )(axios, localVarOperationServerBasePath || basePath); + }, + }; +}; + +/** + * RootApi - factory interface + * @export + */ +export const RootApiFactory = function ( + configuration?: Configuration, + basePath?: string, + axios?: AxiosInstance, +) { + const localVarFp = RootApiFp(configuration); + return { + /** + * + * @summary Get Root + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getRoot( + options?: RawAxiosRequestConfig, + ): AxiosPromise { + return localVarFp + .getRoot(options) + .then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * RootApi - object-oriented interface + * @export + * @class RootApi + * @extends {BaseAPI} + */ +export class RootApi extends BaseAPI { + /** + * + * @summary Get Root + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RootApi + */ + public getRoot(options?: RawAxiosRequestConfig) { + return RootApiFp(this.configuration) + .getRoot(options) + .then((request) => request(this.axios, this.basePath)); + } +} diff --git a/client/src/api/realtime/generated/base.ts b/client/src/api/realtime/generated/base.ts new file mode 100644 index 00000000..f989c25a --- /dev/null +++ b/client/src/api/realtime/generated/base.ts @@ -0,0 +1,91 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import type { Configuration } from "./configuration"; +// Some imports not used depending on template conditions +// @ts-ignore +import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from "axios"; +import globalAxios from "axios"; + +export const BASE_PATH = "http://localhost".replace(/\/+$/, ""); + +/** + * + * @export + */ +export const COLLECTION_FORMATS = { + csv: ",", + ssv: " ", + tsv: "\t", + pipes: "|", +}; + +/** + * + * @export + * @interface RequestArgs + */ +export interface RequestArgs { + url: string; + options: RawAxiosRequestConfig; +} + +/** + * + * @export + * @class BaseAPI + */ +export class BaseAPI { + protected configuration: Configuration | undefined; + + constructor( + configuration?: Configuration, + protected basePath: string = BASE_PATH, + protected axios: AxiosInstance = globalAxios, + ) { + if (configuration) { + this.configuration = configuration; + this.basePath = configuration.basePath ?? basePath; + } + } +} + +/** + * + * @export + * @class RequiredError + * @extends {Error} + */ +export class RequiredError extends Error { + constructor( + public field: string, + msg?: string, + ) { + super(msg); + this.name = "RequiredError"; + } +} + +interface ServerMap { + [key: string]: { + url: string; + description: string; + }[]; +} + +/** + * + * @export + */ +export const operationServerMap: ServerMap = {}; diff --git a/client/src/api/realtime/generated/common.ts b/client/src/api/realtime/generated/common.ts new file mode 100644 index 00000000..d769d1c2 --- /dev/null +++ b/client/src/api/realtime/generated/common.ts @@ -0,0 +1,202 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import type { Configuration } from "./configuration"; +import type { RequestArgs } from "./base"; +import type { AxiosInstance, AxiosResponse } from "axios"; +import { RequiredError } from "./base"; + +/** + * + * @export + */ +export const DUMMY_BASE_URL = "https://example.com"; + +/** + * + * @throws {RequiredError} + * @export + */ +export const assertParamExists = function ( + functionName: string, + paramName: string, + paramValue: unknown, +) { + if (paramValue === null || paramValue === undefined) { + throw new RequiredError( + paramName, + `Required parameter ${paramName} was null or undefined when calling ${functionName}.`, + ); + } +}; + +/** + * + * @export + */ +export const setApiKeyToObject = async function ( + object: any, + keyParamName: string, + configuration?: Configuration, +) { + if (configuration && configuration.apiKey) { + const localVarApiKeyValue = + typeof configuration.apiKey === "function" + ? await configuration.apiKey(keyParamName) + : await configuration.apiKey; + object[keyParamName] = localVarApiKeyValue; + } +}; + +/** + * + * @export + */ +export const setBasicAuthToObject = function ( + object: any, + configuration?: Configuration, +) { + if (configuration && (configuration.username || configuration.password)) { + object["auth"] = { + username: configuration.username, + password: configuration.password, + }; + } +}; + +/** + * + * @export + */ +export const setBearerAuthToObject = async function ( + object: any, + configuration?: Configuration, +) { + if (configuration && configuration.accessToken) { + const accessToken = + typeof configuration.accessToken === "function" + ? await configuration.accessToken() + : await configuration.accessToken; + object["Authorization"] = "Bearer " + accessToken; + } +}; + +/** + * + * @export + */ +export const setOAuthToObject = async function ( + object: any, + name: string, + scopes: string[], + configuration?: Configuration, +) { + if (configuration && configuration.accessToken) { + const localVarAccessTokenValue = + typeof configuration.accessToken === "function" + ? await configuration.accessToken(name, scopes) + : await configuration.accessToken; + object["Authorization"] = "Bearer " + localVarAccessTokenValue; + } +}; + +function setFlattenedQueryParams( + urlSearchParams: URLSearchParams, + parameter: any, + key: string = "", +): void { + if (parameter == null) return; + if (typeof parameter === "object") { + if (Array.isArray(parameter)) { + (parameter as any[]).forEach((item) => + setFlattenedQueryParams(urlSearchParams, item, key), + ); + } else { + Object.keys(parameter).forEach((currentKey) => + setFlattenedQueryParams( + urlSearchParams, + parameter[currentKey], + `${key}${key !== "" ? "." : ""}${currentKey}`, + ), + ); + } + } else { + if (urlSearchParams.has(key)) { + urlSearchParams.append(key, parameter); + } else { + urlSearchParams.set(key, parameter); + } + } +} + +/** + * + * @export + */ +export const setSearchParams = function (url: URL, ...objects: any[]) { + const searchParams = new URLSearchParams(url.search); + setFlattenedQueryParams(searchParams, objects); + url.search = searchParams.toString(); +}; + +/** + * + * @export + */ +export const serializeDataIfNeeded = function ( + value: any, + requestOptions: any, + configuration?: Configuration, +) { + const nonString = typeof value !== "string"; + const needsSerialization = + nonString && configuration && configuration.isJsonMime + ? configuration.isJsonMime(requestOptions.headers["Content-Type"]) + : nonString; + return needsSerialization + ? JSON.stringify(value !== undefined ? value : {}) + : value || ""; +}; + +/** + * + * @export + */ +export const toPathString = function (url: URL) { + return url.pathname + url.search + url.hash; +}; + +/** + * + * @export + */ +export const createRequestFunction = function ( + axiosArgs: RequestArgs, + globalAxios: AxiosInstance, + BASE_PATH: string, + configuration?: Configuration, +) { + return >( + axios: AxiosInstance = globalAxios, + basePath: string = BASE_PATH, + ) => { + const axiosRequestArgs = { + ...axiosArgs.options, + url: + (axios.defaults.baseURL ? "" : (configuration?.basePath ?? basePath)) + + axiosArgs.url, + }; + return axios.request(axiosRequestArgs); + }; +}; diff --git a/client/src/api/realtime/generated/configuration.ts b/client/src/api/realtime/generated/configuration.ts new file mode 100644 index 00000000..150eaebe --- /dev/null +++ b/client/src/api/realtime/generated/configuration.ts @@ -0,0 +1,137 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +export interface ConfigurationParameters { + apiKey?: + | string + | Promise + | ((name: string) => string) + | ((name: string) => Promise); + username?: string; + password?: string; + accessToken?: + | string + | Promise + | ((name?: string, scopes?: string[]) => string) + | ((name?: string, scopes?: string[]) => Promise); + basePath?: string; + serverIndex?: number; + baseOptions?: any; + formDataCtor?: new () => any; +} + +export class Configuration { + /** + * parameter for apiKey security + * @param name security name + * @memberof Configuration + */ + apiKey?: + | string + | Promise + | ((name: string) => string) + | ((name: string) => Promise); + /** + * parameter for basic security + * + * @type {string} + * @memberof Configuration + */ + username?: string; + /** + * parameter for basic security + * + * @type {string} + * @memberof Configuration + */ + password?: string; + /** + * parameter for oauth2 security + * @param name security name + * @param scopes oauth2 scope + * @memberof Configuration + */ + accessToken?: + | string + | Promise + | ((name?: string, scopes?: string[]) => string) + | ((name?: string, scopes?: string[]) => Promise); + /** + * override base path + * + * @type {string} + * @memberof Configuration + */ + basePath?: string; + /** + * override server index + * + * @type {number} + * @memberof Configuration + */ + serverIndex?: number; + /** + * base options for axios calls + * + * @type {any} + * @memberof Configuration + */ + baseOptions?: any; + /** + * The FormData constructor that will be used to create multipart form data + * requests. You can inject this here so that execution environments that + * do not support the FormData class can still run the generated client. + * + * @type {new () => FormData} + */ + formDataCtor?: new () => any; + + constructor(param: ConfigurationParameters = {}) { + this.apiKey = param.apiKey; + this.username = param.username; + this.password = param.password; + this.accessToken = param.accessToken; + this.basePath = param.basePath; + this.serverIndex = param.serverIndex; + this.baseOptions = { + ...param.baseOptions, + headers: { + ...param.baseOptions?.headers, + }, + }; + this.formDataCtor = param.formDataCtor; + } + + /** + * Check if the given MIME is a JSON MIME. + * JSON MIME examples: + * application/json + * application/json; charset=UTF8 + * APPLICATION/JSON + * application/vnd.company+json + * @param mime - MIME (Multipurpose Internet Mail Extensions) + * @return True if the given MIME is JSON, false otherwise. + */ + public isJsonMime(mime: string): boolean { + const jsonMime: RegExp = new RegExp( + "^(application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(;.*)?$", + "i", + ); + return ( + mime !== null && + (jsonMime.test(mime) || + mime.toLowerCase() === "application/json-patch+json") + ); + } +} diff --git a/client/src/api/realtime/generated/docs/HandlerRootResponse.md b/client/src/api/realtime/generated/docs/HandlerRootResponse.md new file mode 100644 index 00000000..e8b2e2b0 --- /dev/null +++ b/client/src/api/realtime/generated/docs/HandlerRootResponse.md @@ -0,0 +1,20 @@ +# HandlerRootResponse + + +## Properties + +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**message** | **string** | | [optional] [default to undefined] + +## Example + +```typescript +import { HandlerRootResponse } from './api'; + +const instance: HandlerRootResponse = { + message, +}; +``` + +[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) diff --git a/client/src/api/realtime/generated/docs/RootApi.md b/client/src/api/realtime/generated/docs/RootApi.md new file mode 100644 index 00000000..0838a98b --- /dev/null +++ b/client/src/api/realtime/generated/docs/RootApi.md @@ -0,0 +1,51 @@ +# RootApi + +All URIs are relative to *http://localhost* + +|Method | HTTP request | Description| +|------------- | ------------- | -------------| +|[**getRoot**](#getroot) | **GET** / | Get Root| + +# **getRoot** +> HandlerRootResponse getRoot() + + +### Example + +```typescript +import { + RootApi, + Configuration +} from './api'; + +const configuration = new Configuration(); +const apiInstance = new RootApi(configuration); + +const { status, data } = await apiInstance.getRoot(); +``` + +### Parameters +This endpoint does not have any parameters. + + +### Return type + +**HandlerRootResponse** + +### Authorization + +No authorization required + +### HTTP request headers + + - **Content-Type**: Not defined + - **Accept**: */* + + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +|**200** | OK | - | + +[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) + diff --git a/client/src/api/realtime/generated/git_push.sh b/client/src/api/realtime/generated/git_push.sh new file mode 100644 index 00000000..f53a75d4 --- /dev/null +++ b/client/src/api/realtime/generated/git_push.sh @@ -0,0 +1,57 @@ +#!/bin/sh +# ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ +# +# Usage example: /bin/sh ./git_push.sh wing328 openapi-petstore-perl "minor update" "gitlab.com" + +git_user_id=$1 +git_repo_id=$2 +release_note=$3 +git_host=$4 + +if [ "$git_host" = "" ]; then + git_host="github.com" + echo "[INFO] No command line input provided. Set \$git_host to $git_host" +fi + +if [ "$git_user_id" = "" ]; then + git_user_id="GIT_USER_ID" + echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" +fi + +if [ "$git_repo_id" = "" ]; then + git_repo_id="GIT_REPO_ID" + echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" +fi + +if [ "$release_note" = "" ]; then + release_note="Minor update" + echo "[INFO] No command line input provided. Set \$release_note to $release_note" +fi + +# Initialize the local directory as a Git repository +git init + +# Adds the files in the local repository and stages them for commit. +git add . + +# Commits the tracked changes and prepares them to be pushed to a remote repository. +git commit -m "$release_note" + +# Sets the new remote +git_remote=$(git remote) +if [ "$git_remote" = "" ]; then # git remote not defined + + if [ "$GIT_TOKEN" = "" ]; then + echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." + git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git + else + git remote add origin https://${git_user_id}:"${GIT_TOKEN}"@${git_host}/${git_user_id}/${git_repo_id}.git + fi + +fi + +git pull origin master + +# Pushes (Forces) the changes in the local repository up to the remote repository +echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git" +git push origin master 2>&1 | grep -v 'To https' diff --git a/client/src/api/realtime/generated/index.ts b/client/src/api/realtime/generated/index.ts new file mode 100644 index 00000000..cebd4486 --- /dev/null +++ b/client/src/api/realtime/generated/index.ts @@ -0,0 +1,16 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +export * from "./api"; +export * from "./configuration"; diff --git a/client/src/app/api/auth/[...nextauth]/route.ts b/client/src/app/api/auth/[...nextauth]/route.ts new file mode 100644 index 00000000..367b4c58 --- /dev/null +++ b/client/src/app/api/auth/[...nextauth]/route.ts @@ -0,0 +1,91 @@ +import NextAuth, { NextAuthOptions, TokenSet } from "next-auth"; +import KeycloakProvider from "next-auth/providers/keycloak"; +import { JWT } from "next-auth/jwt"; +import jwt from "jsonwebtoken"; + +function requestRefreshAccessToken(token: JWT) { + return fetch(`${process.env.KEYCLOAK_ISSUER}/protocol/openid-connect/token`, { + headers: { "Content-Type": "application/x-www-form-urlencoded" }, + // @ts-ignore + body: new URLSearchParams({ + client_id: process.env.KEYCLOAK_CLIENT_ID, + client_secret: process.env.KEYCLOAK_CLIENT_SECRET, + grant_type: "refresh_token", + refresh_token: token.refreshToken, + }), + method: "POST", + cache: "no-store", + }); +} + +const authOptions: NextAuthOptions = { + secret: process.env.NEXTAUTH_SECRET, + providers: [ + KeycloakProvider({ + clientId: process.env.KEYCLOAK_CLIENT_ID ?? "", + clientSecret: process.env.KEYCLOAK_CLIENT_SECRET ?? "", + issuer: process.env.KEYCLOAK_ISSUER, + httpOptions: { + timeout: 10000, + }, + }), + ], + session: { + maxAge: 60 * 30, + }, + callbacks: { + async jwt({ token, account }) { + if (account) { + token.idToken = account.id_token; + token.accessToken = account.access_token; + token.refreshToken = account.refresh_token; + token.expiresAt = account.expires_at; + + if (account.access_token) { + const decodedToken = jwt.decode(account.access_token); + // @ts-ignore + token.roles = decodedToken.resource_access.account.roles; + } + return token; + } + // @ts-ignore + if (Date.now() < token.expiresAt * 1000 - 60 * 1000) { + return token; + } else { + try { + const response = await requestRefreshAccessToken(token); + + const tokens: TokenSet = await response.json(); + + if (!response.ok) throw tokens; + + const updatedToken: JWT = { + ...token, + idToken: tokens.id_token, + accessToken: tokens.access_token, + expiresAt: Math.floor( + Date.now() / 1000 + (tokens.expires_in as number), + ), + refreshToken: tokens.refresh_token ?? token.refreshToken, + }; + return updatedToken; + } catch (error) { + console.error("Error refreshing access token", error); + return { ...token, error: "RefreshAccessTokenError" }; + } + } + }, + async session({ session, token }) { + // @ts-ignore + session.accessToken = token.accessToken; + // @ts-ignore + session.refreshToken = token.refreshToken; + // @ts-ignore + session.roles = token.roles; + return session; + }, + }, +}; +const handler = NextAuth(authOptions); + +export { handler as GET, handler as POST }; diff --git a/client/src/app/api/auth/sso-sign-out/route.ts b/client/src/app/api/auth/sso-sign-out/route.ts new file mode 100644 index 00000000..823d9ef8 --- /dev/null +++ b/client/src/app/api/auth/sso-sign-out/route.ts @@ -0,0 +1,39 @@ +import { NextRequest, NextResponse } from "next/server"; + +export async function GET(req: NextRequest) { + try { + const refreshToken = req.headers.get("refresh_token"); + + if ( + !refreshToken || + !process.env.KEYCLOAK_END_SESSION_ENDPOINT || + !process.env.KEYCLOAK_CLIENT_ID || + !process.env.KEYCLOAK_CLIENT_SECRET + ) { + throw Error; + } + + const body = `client_id=${process.env.KEYCLOAK_CLIENT_ID}&client_secret=${process.env.KEYCLOAK_CLIENT_SECRET}&refresh_token=${refreshToken}`; + + const endSession = await fetch(process.env.KEYCLOAK_END_SESSION_ENDPOINT, { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8", + Authorization: `Bearer ${refreshToken}`, + }, + body, + }); + + if (endSession && endSession.status && endSession.status >= 300) { + console.error("END_SESSION ERROR", endSession.status); + throw Error; + } + + return NextResponse.json({ success: true }); + } catch (error) { + return NextResponse.json({ + success: false, + message: `Error signing out: ${error}`, + }); + } +} diff --git a/client/src/app/board/[id]/page.tsx b/client/src/app/board/[id]/page.tsx new file mode 100644 index 00000000..9535c38f --- /dev/null +++ b/client/src/app/board/[id]/page.tsx @@ -0,0 +1,31 @@ +"use client"; + +import { use } from "react"; +import Whiteboard from "@/components/Whiteboard"; +import { ReactFlowProvider } from "@xyflow/react"; +import { redirect } from "next/navigation"; +import { useGetWhiteboardById } from "@/hooks/api/whiteboard.api"; + +export default function Board({ params }: { params: Promise<{ id: string }> }) { + const { id } = use(params); + + const isNumber = !isNaN(Number(id)); + + if (!isNumber) { + redirect("/dashboard"); + } + + const { isError, isLoading } = useGetWhiteboardById(Number(id)); + + if (isError) { + redirect("/dashboard"); + } + + if (!isLoading) { + return ( + + + + ); + } +} diff --git a/client/src/app/board/page.tsx b/client/src/app/board/page.tsx new file mode 100644 index 00000000..1b0c1530 --- /dev/null +++ b/client/src/app/board/page.tsx @@ -0,0 +1,6 @@ +"use client"; +import { redirect } from "next/navigation"; + +export default function BoardPage() { + redirect("/dashboard"); +} diff --git a/client/src/app/dashboard/page.tsx b/client/src/app/dashboard/page.tsx new file mode 100644 index 00000000..0330722f --- /dev/null +++ b/client/src/app/dashboard/page.tsx @@ -0,0 +1,142 @@ +"use client"; +import React, { useState } from "react"; +import WhiteboardCard from "@/components/whiteboard-card/WhiteboardCard"; +import CreateWhiteboardCard from "@/components/whiteboard-card/CreateWhiteboardCard"; +import { useWhiteboards } from "@/hooks/api/whiteboard.api"; +import Header from "@/components/header/Header"; +import FilterBar from "@/components/filters/Filterbar"; +import { useSortedWhiteboards } from "@/hooks/useSortedWhiteboards"; +import { Clock, Home } from "lucide-react"; +import UserDropdown from "@/components/user-dropdown/UserDropdown"; +import { useFilteredWhiteboards } from "@/hooks/useFilteredWhiteboards"; + +const Dashboard = () => { + const { data: whiteboards = [] } = useWhiteboards(); + const { filteredWhiteboards, filterBy, setFilterBy } = + useFilteredWhiteboards(whiteboards); + const { sortedWhiteboards, sortBy, setSortBy } = + useSortedWhiteboards(filteredWhiteboards); + const [activeSection, setActiveSection] = useState<"home" | "recent">("home"); + + const recentWhiteboards = React.useMemo(() => { + const oneWeekAgo = new Date(); + oneWeekAgo.setDate(oneWeekAgo.getDate() - 7); + + return whiteboards + .filter((board) => { + const lastEditDate = board.lastUpdatedAt + ? new Date(board.lastUpdatedAt) + : board.createdAt + ? new Date(board.createdAt) + : null; + + return lastEditDate && lastEditDate > oneWeekAgo; + }) + .sort((a, b) => { + const dateA = a.lastUpdatedAt + ? new Date(a.lastUpdatedAt) + : new Date(a.createdAt || 0); + const dateB = b.lastUpdatedAt + ? new Date(b.lastUpdatedAt) + : new Date(b.createdAt || 0); + return dateB.getTime() - dateA.getTime(); + }); + }, [whiteboards]); + + return ( +
+
+
+
+ +
+ + +
+
+ +
+
+ +
+
+

+ {activeSection === "home" ? "Your Boards" : "Recent Boards"} +

+ {activeSection === "home" && ( + + )} +
+ +
+ {activeSection === "home" ? ( +
+ + {sortedWhiteboards.map((project) => ( + + ))} +
+ ) : ( +
+ {recentWhiteboards.length > 0 ? ( + recentWhiteboards.map((project) => ( + + )) + ) : ( +
+
+ +

+ No recent boards +

+

+ Boards edited or created in the last 7 days will appear + here +

+
+
+ )} +
+ )} +
+
+
+
+ ); +}; + +export default Dashboard; diff --git a/client/src/app/globals.css b/client/src/app/globals.css index 55ecaf49..921a8a49 100644 --- a/client/src/app/globals.css +++ b/client/src/app/globals.css @@ -1,5 +1,12 @@ @import "tailwindcss"; -@import "tw-animate-css"; +@import url('https://fonts.googleapis.com/css2?family=Caveat:wght@400..700&family=Chewy&display=swap'); + +@import url('https://fonts.googleapis.com/css2?family=Fredoka+One&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Graduate&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Gravitas+One&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Playfair+Display&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Poppins&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Caveat&family=Fredoka+One&family=Gravitas+One&display=swap'); @custom-variant dark (&:is(.dark *)); diff --git a/client/src/app/layout.tsx b/client/src/app/layout.tsx index f7fa87eb..5f3ea929 100644 --- a/client/src/app/layout.tsx +++ b/client/src/app/layout.tsx @@ -2,6 +2,9 @@ import type { Metadata } from "next"; import { Geist, Geist_Mono } from "next/font/google"; import "./globals.css"; +import Providers from "@/app/providers"; +import SessionGuard from "@/components/auth/session-guard/SessionGuard"; + const geistSans = Geist({ variable: "--font-geist-sans", subsets: ["latin"], @@ -13,8 +16,8 @@ const geistMono = Geist_Mono({ }); export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: "AI-Powered Whiteboard", + description: "AI-Powered Whiteboard", }; export default function RootLayout({ @@ -24,10 +27,15 @@ export default function RootLayout({ }>) { return ( + + + - {children} + + {children} + ); diff --git a/client/src/app/page.tsx b/client/src/app/page.tsx index 078ea5f1..38330228 100644 --- a/client/src/app/page.tsx +++ b/client/src/app/page.tsx @@ -1,19 +1,7 @@ -import React from "react"; -import { - ReactFlow, - Background, - BackgroundVariant, - Controls, -} from "@xyflow/react"; -import "@xyflow/react/dist/style.css"; +"use client"; + +import { redirect } from "next/navigation"; export default function Home() { - return ( -
- - - - -
- ); + redirect("/dashboard"); } diff --git a/client/src/app/providers.tsx b/client/src/app/providers.tsx new file mode 100644 index 00000000..4f80690f --- /dev/null +++ b/client/src/app/providers.tsx @@ -0,0 +1,24 @@ +"use client"; + +import React, { PropsWithChildren } from "react"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { SessionProvider } from "next-auth/react"; + +export default function Providers({ children }: PropsWithChildren) { + const [queryClient] = React.useState( + () => + new QueryClient({ + defaultOptions: { + queries: { + staleTime: Infinity, + }, + }, + }), + ); + + return ( + + {children} + + ); +} diff --git a/client/src/assets/cursor.svg b/client/src/assets/cursor.svg new file mode 100644 index 00000000..42d7f363 --- /dev/null +++ b/client/src/assets/cursor.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/assets/shapes/circle.svg b/client/src/assets/shapes/circle.svg new file mode 100644 index 00000000..239831fa --- /dev/null +++ b/client/src/assets/shapes/circle.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/client/src/assets/shapes/diamond.svg b/client/src/assets/shapes/diamond.svg new file mode 100644 index 00000000..64abdd4a --- /dev/null +++ b/client/src/assets/shapes/diamond.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/client/src/assets/shapes/hexagon.svg b/client/src/assets/shapes/hexagon.svg new file mode 100644 index 00000000..350491bb --- /dev/null +++ b/client/src/assets/shapes/hexagon.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/client/src/assets/shapes/parallelogram.svg b/client/src/assets/shapes/parallelogram.svg new file mode 100644 index 00000000..5a246d8e --- /dev/null +++ b/client/src/assets/shapes/parallelogram.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/client/src/assets/shapes/rectangle.svg b/client/src/assets/shapes/rectangle.svg new file mode 100644 index 00000000..83820534 --- /dev/null +++ b/client/src/assets/shapes/rectangle.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/client/src/assets/shapes/trapezoid.svg b/client/src/assets/shapes/trapezoid.svg new file mode 100644 index 00000000..06f2ea11 --- /dev/null +++ b/client/src/assets/shapes/trapezoid.svg @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/client/src/assets/shapes/triangle.svg b/client/src/assets/shapes/triangle.svg new file mode 100644 index 00000000..3e03078b --- /dev/null +++ b/client/src/assets/shapes/triangle.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/client/src/components/Whiteboard.tsx b/client/src/components/Whiteboard.tsx new file mode 100644 index 00000000..aa3d3549 --- /dev/null +++ b/client/src/components/Whiteboard.tsx @@ -0,0 +1,472 @@ +"use client"; +import React, { + MouseEventHandler, + useCallback, + useEffect, + useState, + useRef, + useMemo, +} from "react"; +import { + ReactFlow, + Node, + addEdge, + Connection, + useNodesState, + useEdgesState, + Controls, + Background, + BackgroundVariant, + ReactFlowInstance, + useReactFlow, + Edge, +} from "@xyflow/react"; +import "@xyflow/react/dist/style.css"; +import Sidebar from "@/components/sidebar/Sidebar"; +import TextNode from "@/components/text-node/TextNode"; +import ShapeNode from "@/components/shape-node/ShapeNode"; +import { useSaveWhiteboardState } from "@/hooks/api/whiteboard.save.state.api"; +import { useRestoreWhiteboard } from "@/hooks/api/whiteboard.restore.state.api"; +import useInterval from "@/hooks/useInterval"; +import MenuBar from "./menu-bar/MenuBar"; +import CollaborationTopbar from "@/components/collaboration-topbar/CollaborationTopbar"; +import { + useAmIOwner, + usePublishWhiteboardEvents, + useSubscribeToWhiteboardEvents, +} from "@/hooks/api/whiteboard.api"; +import CustomCursor from "@/components/custom-cursor/CustomCursor"; +import { useGetMe } from "@/hooks/api/account.api"; +import { WhiteboardEvent } from "@/api/realtime/dtos/WhiteboardEvent"; +import { z } from "zod"; + +const nodeTypes = { + text: TextNode, + shapeNode: ShapeNode, +}; + +interface WhiteboardProps { + whiteboardId: number; +} + +interface Cursor { + id: number; + position?: { x: number; y: number }; + username: string; +} + +export default function Whiteboard({ whiteboardId }: WhiteboardProps) { + const [nodes, setNodes, onNodesChange] = useNodesState([]); + const [edges, setEdges, onEdgesChange] = useEdgesState([]); + const [rfInstance, setRfInstance] = useState(null); + + type ActivityMap = Record; + const [userActivity, setUserActivity] = useState({}); + const userTimeouts = useRef>({}); + + const { getNodes, getEdges, getViewport } = useReactFlow(); + + const { data: user } = useGetMe(); + + const { data: isOwner } = useAmIOwner(whiteboardId, user?.id); + + const [dragStart, setDragStart] = useState<{ + cursor: { x: number; y: number }; + node: { x: number; y: number }; + } | null>(null); + + useEffect(() => { + if (user && user.id && user.username) + setCursor({ + id: user.id, + username: user.username, + position: undefined, + }); + }, [user]); + + const [cursor, setCursor] = useState(); + + const [allCursors, setAllCursors] = useState([]); + + // whiteboard logic + const { saveWhiteboardState } = useSaveWhiteboardState({ + whiteboardId, + nodes: getNodes(), + edges: getEdges(), + viewport: getViewport(), + }); + + useInterval(saveWhiteboardState, 1000, isOwner); + + useRestoreWhiteboard({ + whiteboardId, + }); + + const handleAddNode = useCallback( + (newNode: Node) => { + setNodes((nds) => [...nds, newNode]); + }, + [setNodes, rfInstance], + ); + + const onConnect = useCallback( + (params: Connection) => setEdges((eds) => addEdge(params, eds)), + [setEdges], + ); + + // custom cursor logic + const prevCursorRef = useRef<{ x: number; y: number } | null>(null); + + const onMouseMove: MouseEventHandler = (event) => { + if (!rfInstance) return; + + const position = rfInstance.screenToFlowPosition({ + x: event.clientX, + y: event.clientY, + }); + + const prev = prevCursorRef.current; + if (!prev || prev.x !== position.x || prev.y !== position.y) { + prevCursorRef.current = position; + setCursor((prev) => (prev ? { ...prev, position } : prev)); + } + }; + + const onMove = () => { + if (cursor && cursor.position) { + const x = cursor.position.x; + const y = cursor.position.y; + + const position = { x, y }; + + setCursor((prev) => { + if (!prev) return prev; + + return { + ...prev, + position, + }; + }); + } + }; + + const handleNodeDragStart = (event: React.MouseEvent, node: Node) => { + if (!rfInstance) return; + const flowPos = rfInstance.screenToFlowPosition({ + x: event.clientX, + y: event.clientY, + }); + setDragStart({ + cursor: { x: flowPos.x, y: flowPos.y }, + node: { x: node.position.x, y: node.position.y }, + }); + }; + + const handleNodeDrag = (event: React.MouseEvent, node: Node) => { + if (!dragStart) return; + + const dx = node.position.x - dragStart.node.x; + const dy = node.position.y - dragStart.node.y; + + const x = dragStart.cursor.x + dx; + const y = dragStart.cursor.y + dy; + + setCursor((prev) => { + if (!prev) return prev; + + return { + ...prev, + position: { x, y }, + }; + }); + }; + + const handleNodeDragStop = () => { + setDragStart(null); + }; + + const handleMouseLeave = () => { + if (cursor) + setCursor((prev) => { + if (!prev) return prev; + + return { ...prev, position: undefined }; + }); + }; + + const renderedCursors = useMemo(() => { + if (!rfInstance) return []; + + const viewport = rfInstance.getViewport(); + + return allCursors + .filter((cursor) => cursor.position) + .map((cursor) => { + const pos = cursor.position!; + const position = { + x: pos.x * viewport.zoom + viewport.x, + y: pos.y * viewport.zoom + viewport.y, + }; + + return ( + + ); + }); + }, [allCursors, rfInstance]); + + // Realtime synchronisation logic + const handleWhiteboardEvent = useCallback( + (event: z.infer) => { + if (event.type === "mousePosition") { + const { id, username, position } = event.payload; + + if (id === user?.id) return; // skip current user + if (!position) return; + + setUserActivity((prev) => ({ ...prev, [username]: true })); + + if (userTimeouts.current[username]) { + clearTimeout(userTimeouts.current[username]); + } + + userTimeouts.current[username] = setTimeout(() => { + setUserActivity((prev) => ({ ...prev, [username]: false })); + }, 10000); + + setAllCursors((prevCursors) => { + const otherCursors = prevCursors.filter((c) => c.id !== id); + return [...otherCursors, { id, username, position }]; + }); + } + + if (isOwner) { + return; + } + + if (event.type === "nodePosition") { + const incomingNodes = event.payload; + + setNodes((prevNodes) => { + const incomingMap = new Map(incomingNodes.map((n) => [n.id, n])); + + const updatedNodes = incomingNodes.map((node) => { + const incoming = incomingMap.get(node.id); + if (!incoming) return node; + + return { + id: incoming.id, + type: incoming.type, + position: incoming.position, + width: incoming.width, + height: incoming.height, + measured: incoming.measured, + selected: incoming.selected, + dragging: incoming.dragging, + data: { + shapeType: incoming.data.shapeType, + label: incoming.data.label, + nodeProperties: incoming.data.nodeProperties, + }, + }; + }); + + const newNodes = incomingNodes.filter( + (incoming) => !prevNodes.some((n) => n.id === incoming.id), + ); + + return [...updatedNodes, ...newNodes]; + }); + } + + if (event.type === "edgePosition") { + const incomingEdges = event.payload; + + setEdges((prevEdges) => { + const incomingMap = new Map(incomingEdges.map((e) => [e.id, e])); + + const updatedEdges = incomingEdges.map((edge) => { + const incoming = incomingMap.get(edge.id); + if (!incoming) return edge; + + return { + id: incoming.id, + source: incoming.source, + sourceHandle: incoming.sourceHandle, + target: incoming.target, + targetHandle: incoming.targetHandle, + }; + }); + + const newEdges = incomingEdges.filter( + (incoming) => !prevEdges.some((e) => e.id === incoming.id), + ); + + return [...updatedEdges, ...newEdges]; + }); + } + }, + [user?.id, isOwner], + ); + + useSubscribeToWhiteboardEvents(whiteboardId, handleWhiteboardEvent); + const publishEvent = usePublishWhiteboardEvents(whiteboardId); + + // Publish cursor events + const latestCursorPositionRef = useRef(cursor); + const lastCursorPublishedPositionRef = useRef(null); + + useEffect(() => { + latestCursorPositionRef.current = cursor; + }, [cursor]); + + useEffect(() => { + const interval = setInterval(() => { + const current = latestCursorPositionRef.current; + const last = lastCursorPublishedPositionRef.current; + + const hasChanged = + !last || + current?.position?.x !== last.position?.x || + current?.position?.y !== last.position?.y; + + if (hasChanged) { + lastCursorPublishedPositionRef.current = current; + publishEvent( + JSON.stringify({ + type: "mousePosition", + payload: current, + }), + ); + } + }, 50); + + return () => clearInterval(interval); + }, []); + + // Publish node events + const latestNodesPositionRef = useRef(nodes); + const lastNodesPublishedPositionRef = useRef(null); + + useEffect(() => { + latestNodesPositionRef.current = nodes; + }, [nodes]); + + useEffect(() => { + if (!isOwner) return; + + const interval = setInterval(() => { + const current = latestNodesPositionRef.current; + const last = lastNodesPublishedPositionRef.current; + + const hasChanged = + !last || JSON.stringify(current) !== JSON.stringify(last); + + if (hasChanged) { + lastNodesPublishedPositionRef.current = current; + publishEvent( + JSON.stringify({ + type: "nodePosition", + payload: nodes, + }), + ); + } + }, 50); + + return () => clearInterval(interval); + }); + + // Publish edge events + const latestEdgesPositionRef = useRef(edges); + const lastEdgesPublishedPositionRef = useRef(null); + + useEffect(() => { + latestEdgesPositionRef.current = edges; + }, [edges]); + + const haveEdgesChanged = (a: Edge[], b: Edge[]) => { + if (a.length !== b.length) return true; + + const byId = new Map(b.map((edge) => [edge.id, edge])); + + return a.some((edge) => { + const prev = byId.get(edge.id); + if (!prev) return true; + return edge.source !== prev.source || edge.target !== prev.target; + }); + }; + + useEffect(() => { + if (!isOwner) return; + + const interval = setInterval(() => { + const current = latestEdgesPositionRef.current; + const last = lastEdgesPublishedPositionRef.current; + + const hasChanged = !last || haveEdgesChanged(current, last); + if (hasChanged) { + lastEdgesPublishedPositionRef.current = current; + publishEvent( + JSON.stringify({ + type: "edgePosition", + payload: edges, + }), + ); + } + }, 50); + + return () => clearInterval(interval); + }); + + return ( +
+
+
+ + +
+
+ {isOwner && ( +
+ +
+ )} + { + setRfInstance(instance); + }} + nodeTypes={nodeTypes} + > +
+ {renderedCursors} +
+ + +
+
+ ); +} diff --git a/client/src/components/access-dialog/AccessDialog.tsx b/client/src/components/access-dialog/AccessDialog.tsx new file mode 100644 index 00000000..d76a3248 --- /dev/null +++ b/client/src/components/access-dialog/AccessDialog.tsx @@ -0,0 +1,154 @@ +import React, { Dispatch, SetStateAction, useEffect, useState } from "react"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; + +import { + useGetWhiteboardCollaborators, + useInviteCollaboratorsToWhiteboard, + useRemoveCollaboratorsFromWhiteboard, +} from "@/hooks/api/whiteboard.api"; +import { UserResponse } from "@/api/main/generated"; +import InviteTab from "@/components/access-dialog/access-dialog-components/InviteTab"; +import ManageAccessTab from "@/components/access-dialog/access-dialog-components/ManageAccessTab"; + +type TabType = "invite" | "manage"; + +interface AccessDialogProps { + currentUserId?: number; + + whiteboardId: number; + isOpen: boolean; + setIsOpen: Dispatch>; +} + +interface TabButtonProps { + isActive: boolean; + label: string; + onClick: () => void; +} + +const TabButton: React.FC = ({ isActive, label, onClick }) => ( + +); + +const AccessDialog: React.FC = ({ + currentUserId, + + whiteboardId, + isOpen, + setIsOpen, +}) => { + const [pendingInvites, setPendingInvites] = useState([]); + const [activeCollaborators, setActiveCollaborators] = useState< + UserResponse[] + >([]); + const [removedCollaborators, setRemovedCollaborators] = useState< + UserResponse[] + >([]); + + const [activeTab, setActiveTab] = useState("invite"); + + const inviteCollaborators = useInviteCollaboratorsToWhiteboard(); + const removeCollaborators = useRemoveCollaboratorsFromWhiteboard(); + const { data: collaborators } = useGetWhiteboardCollaborators(whiteboardId); + + useEffect(() => { + if (collaborators) { + if (currentUserId) { + const collaboratorsWithoutCurrentUser = collaborators.filter( + (collaborator) => collaborator.id != currentUserId, + ); + setActiveCollaborators(collaboratorsWithoutCurrentUser); + } else { + setActiveCollaborators(collaborators); + } + setRemovedCollaborators([]); + } + }, [collaborators]); + + const handleAddInvite = (email: string) => { + setPendingInvites((prev) => [...prev, email]); + }; + + const handleRemoveInvite = (email: string) => { + setPendingInvites((prev) => prev.filter((inv) => inv !== email)); + }; + + const handleSendInvitations = () => { + inviteCollaborators.mutate({ whiteboardId, emails: pendingInvites }); + setPendingInvites([]); + setIsOpen(false); + }; + + const handleRemoveCollaborator = (email: string) => { + const collaboratorToRemove = activeCollaborators.find( + (collaborator) => collaborator.email === email, + ); + + if (collaboratorToRemove) { + setRemovedCollaborators((prev) => [...prev, collaboratorToRemove]); + setActiveCollaborators((prev) => + prev.filter((collaborator) => collaborator.email !== email), + ); + } + }; + + const handleSaveCollaboratorChanges = () => { + const userIds = removedCollaborators + .filter((collaborator) => collaborator.id) + .map((collaborator) => collaborator.id!); + removeCollaborators.mutate({ whiteboardId, userIds }); + setIsOpen(false); + }; + + return ( + + + + Dummy Title + +
+ setActiveTab("invite")} + /> + setActiveTab("manage")} + /> +
+ + {activeTab === "invite" ? ( + + ) : ( + + )} +
+
+ ); +}; + +export default AccessDialog; diff --git a/client/src/components/access-dialog/access-dialog-components/InviteTab.tsx b/client/src/components/access-dialog/access-dialog-components/InviteTab.tsx new file mode 100644 index 00000000..7b0eb736 --- /dev/null +++ b/client/src/components/access-dialog/access-dialog-components/InviteTab.tsx @@ -0,0 +1,57 @@ +import React from "react"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import EmailBadge from "@/components/email-badge/EmailBadge"; + +interface InviteTabProps { + pendingInvites: string[]; + onAddInvite: (email: string) => void; + onRemoveInvite: (email: string) => void; + onSendInvitations: () => void; +} + +export default function InviteTab({ + pendingInvites, + onAddInvite, + onRemoveInvite, + onSendInvitations, +}: InviteTabProps) { + const handleKeyDown = (e: React.KeyboardEvent): void => { + if (e.key === "Enter" && e.currentTarget.value.trim()) { + onAddInvite(e.currentTarget.value.trim()); + e.currentTarget.value = ""; + } + }; + + return ( + <> +
+ Share with viewers (read-only access) +
+ +
+ {pendingInvites.map((email, index) => ( + + ))} +
+
+ +
+ + ); +} diff --git a/client/src/components/access-dialog/access-dialog-components/ManageAccessTab.tsx b/client/src/components/access-dialog/access-dialog-components/ManageAccessTab.tsx new file mode 100644 index 00000000..d8adf36a --- /dev/null +++ b/client/src/components/access-dialog/access-dialog-components/ManageAccessTab.tsx @@ -0,0 +1,55 @@ +import React from "react"; +import { Button } from "@/components/ui/button"; +import EmailBadge from "@/components/email-badge/EmailBadge"; +import { UserResponse } from "@/api/main/generated"; +import generateColorFromString from "@/util/generateUserUniqueColor"; + +interface ManageAccessTabProps { + collaborators: UserResponse[]; + onRemoveCollaborator: (email: string) => void; + onSaveChanges: () => void; + isButtonDisabled: boolean; +} + +export default function ManageAccessTab({ + collaborators, + onRemoveCollaborator, + onSaveChanges, + isButtonDisabled, +}: ManageAccessTabProps) { + return ( + <> +
+ {collaborators.length > 0 ? ( + collaborators.map((collaborator, index) => { + const color = generateColorFromString(collaborator.username ?? ""); + + return ( + + ); + }) + ) : ( +
+ You're the only collaborator +
+ )} +
+
+ +
+ + ); +} diff --git a/client/src/components/account-modal/AccountModal.tsx b/client/src/components/account-modal/AccountModal.tsx new file mode 100644 index 00000000..c80bd02d --- /dev/null +++ b/client/src/components/account-modal/AccountModal.tsx @@ -0,0 +1,68 @@ +"use client"; + +import React from "react"; +import { Mail } from "lucide-react"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogDescription, +} from "@/components/ui/dialog"; +import { useGetMe } from "@/hooks/api/account.api"; +import Avatar from "@/components/avatar/Avatar"; + +interface AccountModalProps { + isOpen: boolean; + onClose: () => void; +} + +export default function AccountModal({ isOpen, onClose }: AccountModalProps) { + const { data: user } = useGetMe(); + + return ( + + + + Account + View your account details + + +
+
+ +
+ +
+
+

+ Username +

+

{user?.username}

+
+
+

Name

+

+ {user?.firstName} {user?.lastName} +

+
+ +
+

Email

+
+ +

{user?.email}

+
+
+
+
+
+
+ ); +} diff --git a/client/src/components/aiActionDropdown/aiActionDropdown.tsx b/client/src/components/aiActionDropdown/aiActionDropdown.tsx new file mode 100644 index 00000000..a7a903c6 --- /dev/null +++ b/client/src/components/aiActionDropdown/aiActionDropdown.tsx @@ -0,0 +1,91 @@ +import { + Sparkles, + Type, + FileText, + RefreshCw, + LoaderCircle, +} from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { useState } from "react"; + +interface AIActionsProps { + disabled?: boolean; + onAIAction: (action: "complete" | "summarize" | "rephrase") => void; +} + +export function AIActionDropdown({ + disabled = false, + onAIAction, +}: AIActionsProps) { + const [loading, setLoading] = useState(false); + + const handleAIAction = async ( + action: "complete" | "summarize" | "rephrase", + ) => { + if (disabled) return; + + setLoading(true); + try { + await onAIAction(action); + } finally { + setLoading(false); + } + }; + + return ( +
+ + + + + + handleAIAction("complete")} + className="cursor-pointer" + disabled={loading} + > + + Complete Text + + handleAIAction("summarize")} + className="cursor-pointer" + disabled={loading} + > + + Summarize Text + + handleAIAction("rephrase")} + className="cursor-pointer" + disabled={loading} + > + + Rephrase Text + + + +
+ ); +} diff --git a/client/src/components/auth/session-guard/SessionGuard.tsx b/client/src/components/auth/session-guard/SessionGuard.tsx new file mode 100644 index 00000000..b7313091 --- /dev/null +++ b/client/src/components/auth/session-guard/SessionGuard.tsx @@ -0,0 +1,18 @@ +"use client"; + +import { signIn, useSession } from "next-auth/react"; +import { ReactNode, useEffect } from "react"; + +export default function SessionGuard({ children }: { children: ReactNode }) { + const { data } = useSession({ + required: true, + }); + useEffect(() => { + // @ts-ignore + if (data?.error === "RefreshAccessTokenError") { + signIn("keycloak"); + } + }, [data]); + + return <>{children}; +} diff --git a/client/src/components/avatar/Avatar.tsx b/client/src/components/avatar/Avatar.tsx new file mode 100644 index 00000000..7472ae9a --- /dev/null +++ b/client/src/components/avatar/Avatar.tsx @@ -0,0 +1,35 @@ +import React from "react"; +import { + Avatar as ShadeCnAvatar, + AvatarFallback, +} from "@/components/ui/avatar"; +import generateColorFromString from "@/util/generateUserUniqueColor"; + +interface PersonalAvatarProps { + username: string; + firstname: string; + lastname: string; + className?: string; + fallbackClassName?: string; +} + +export default function Avatar({ + username, + firstname, + lastname, + className = "", + fallbackClassName = "", +}: PersonalAvatarProps) { + const color = generateColorFromString(username); + return ( + + + {firstname.charAt(0).toUpperCase()} + {lastname.charAt(0).toUpperCase()} + + + ); +} diff --git a/client/src/components/collaboration-topbar/CollaborationTopbar.tsx b/client/src/components/collaboration-topbar/CollaborationTopbar.tsx new file mode 100644 index 00000000..6495239c --- /dev/null +++ b/client/src/components/collaboration-topbar/CollaborationTopbar.tsx @@ -0,0 +1,79 @@ +import { useGetMe } from "@/hooks/api/account.api"; +import Avatar from "@/components/avatar/Avatar"; +import React, { useState } from "react"; +import AccessDialog from "@/components/access-dialog/AccessDialog"; +import { UsersIcon } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { useGetWhiteboardCollaborators } from "@/hooks/api/whiteboard.api"; + +interface CollaborationTopbarProps { + whiteboardId: number; + userActivity: Record; + + isSharable?: boolean; +} + +const CollaborationTopbar = ({ + whiteboardId, + userActivity, + + isSharable = true, +}: CollaborationTopbarProps) => { + const { data: user } = useGetMe(); + const { data: collaboratorsData } = + useGetWhiteboardCollaborators(whiteboardId); + + const collaboratorsWithoutSelf = collaboratorsData?.filter( + (collaborator) => collaborator.username !== user?.username, + ); + + const [isInviteDialogOpen, setIsInviteDialogOpen] = useState(false); + + return ( +
+
+ + {collaboratorsWithoutSelf?.map((collaborator, i) => { + if (!collaborator?.username) return null; + const isInactive = + collaborator.username in userActivity && + !userActivity[collaborator.username]; + + return ( + + ); + })} +
+ {isSharable && ( + + )} + +
+ ); +}; + +export default CollaborationTopbar; diff --git a/client/src/components/custom-cursor/CustomCursor.tsx b/client/src/components/custom-cursor/CustomCursor.tsx new file mode 100644 index 00000000..0e6a7e60 --- /dev/null +++ b/client/src/components/custom-cursor/CustomCursor.tsx @@ -0,0 +1,46 @@ +"use client"; +import CursorIcon from "@/assets/cursor.svg"; +import generateColorFromString from "@/util/generateUserUniqueColor"; + +interface CustomCursorProps { + username: string; + position: { x: number; y: number }; + visible: boolean; +} + +export default function CustomCursor({ + username, + position, + visible, +}: CustomCursorProps) { + const color = generateColorFromString(username); + + return ( +
+ + +
+ {username} +
+
+ ); +} diff --git a/client/src/components/dashboard-sidebar/DashboardSidebar.tsx b/client/src/components/dashboard-sidebar/DashboardSidebar.tsx new file mode 100644 index 00000000..8b04aab0 --- /dev/null +++ b/client/src/components/dashboard-sidebar/DashboardSidebar.tsx @@ -0,0 +1,30 @@ +import UserDropdown from "@/components/user-dropdown/UserDropdown"; +import { Clock, Home, Star } from "lucide-react"; +import React from "react"; + +export default function DashboardSidebar() { + return ( +
+
+
+ +
+ + +
+
+ ); +} diff --git a/client/src/components/deletion-alert-dialog/DeletionAlertDialog.tsx b/client/src/components/deletion-alert-dialog/DeletionAlertDialog.tsx new file mode 100644 index 00000000..7c0c5da2 --- /dev/null +++ b/client/src/components/deletion-alert-dialog/DeletionAlertDialog.tsx @@ -0,0 +1,49 @@ +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "@/components/ui/alert-dialog"; +import React from "react"; + +interface DeletionAlertDialogProps { + toDelete: string; + dialogOpen: boolean; + setDialogOpen: (open: boolean) => void; + handleConfirmDelete: () => void; +} + +export default function DeletionAlertDialog({ + toDelete, + dialogOpen, + setDialogOpen, + handleConfirmDelete, +}: DeletionAlertDialogProps) { + return ( + + + + Are you sure? + + This action cannot be undone. This will permanently delete your + {"\u00A0"} + {toDelete} from our servers. + + + + Cancel + + Delete + + + + + ); +} diff --git a/client/src/components/email-badge/EmailBadge.tsx b/client/src/components/email-badge/EmailBadge.tsx new file mode 100644 index 00000000..530dcb6d --- /dev/null +++ b/client/src/components/email-badge/EmailBadge.tsx @@ -0,0 +1,32 @@ +import { X } from "lucide-react"; + +interface UserBadgeInterface { + email: string; + onDelete: (email: string) => void; + bgColor?: string; + className?: string; +} + +const EmailBadge = ({ + email, + onDelete, + bgColor, + className, +}: UserBadgeInterface) => { + return ( +
+ onDelete(email)} + /> +

{email}

+
+ ); +}; + +export default EmailBadge; diff --git a/client/src/components/filters/Filterbar.tsx b/client/src/components/filters/Filterbar.tsx new file mode 100644 index 00000000..eb4a3320 --- /dev/null +++ b/client/src/components/filters/Filterbar.tsx @@ -0,0 +1,64 @@ +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import React from "react"; +import { SORT_OPTIONS, SortOption } from "@/types/SortingType"; +import { FilterOption } from "@/types/FilterType"; + +interface FilterBarProps { + sortBy: SortOption; + onSortChange: (value: SortOption) => void; + filterBy: string; + onFilterChange: (value: FilterOption) => void; +} + +export default function FilterBar({ + sortBy, + onSortChange, + filterBy, + onFilterChange, +}: FilterBarProps) { + return ( +
+
+ Filter by + +
+ +
+ Sort by + +
+
+ ); +} diff --git a/client/src/components/header/Header.tsx b/client/src/components/header/Header.tsx new file mode 100644 index 00000000..72e62a61 --- /dev/null +++ b/client/src/components/header/Header.tsx @@ -0,0 +1,13 @@ +import React from "react"; + +export default function Header() { + return ( +
+
+

+ AI-Powered Whiteboard +

+
+
+ ); +} diff --git a/client/src/components/menu-bar/MenuBar.tsx b/client/src/components/menu-bar/MenuBar.tsx new file mode 100644 index 00000000..44234a07 --- /dev/null +++ b/client/src/components/menu-bar/MenuBar.tsx @@ -0,0 +1,138 @@ +"use client"; +import { Palette } from "lucide-react"; +import React, { useState, useRef, useEffect } from "react"; +import { + useGetWhiteboardTitle, + useUpdateWhiteboardTitle, +} from "@/hooks/api/whiteboard.api"; +import { useRouter } from "next/navigation"; + +interface MenuBarProps { + whiteboardId: number; + isEditable?: boolean; +} + +const MenuBar: React.FC = ({ + whiteboardId, + isEditable = true, +}) => { + const { data: whiteboardTitle } = useGetWhiteboardTitle(whiteboardId); + const [isEditing, setIsEditing] = useState(false); + const [editedTitle, setEditedTitle] = useState(whiteboardTitle); + const inputRef = useRef(null); + const menuBarRef = useRef(null); + const router = useRouter(); + + const updateTitle = useUpdateWhiteboardTitle(whiteboardId); + + const cancelEdit = () => { + setEditedTitle(whiteboardTitle); + setIsEditing(false); + }; + + useEffect(() => { + setEditedTitle(whiteboardTitle); + }, [whiteboardTitle]); + + useEffect(() => { + if (!isEditing) return; + + const handleClickOutside = (event: MouseEvent | PointerEvent) => { + if ( + inputRef.current && + !inputRef.current.contains(event.target as Node) + ) { + cancelEdit(); + } + }; + + document.addEventListener("mousedown", handleClickOutside, true); + document.addEventListener("pointerdown", handleClickOutside, true); + + return () => { + document.removeEventListener("mousedown", handleClickOutside, true); + document.removeEventListener("pointerdown", handleClickOutside, true); + }; + }, [cancelEdit, isEditing]); + + useEffect(() => { + if (isEditing && inputRef.current) { + inputRef.current.focus(); + inputRef.current.select(); + } + }, [isEditing]); + + const handleRename = (e: React.MouseEvent) => { + e.stopPropagation(); + setIsEditing(true); + }; + + const saveTitle = () => { + const trimmedTitle = editedTitle?.trim(); + if (!trimmedTitle) { + setEditedTitle(whiteboardTitle); + } else if (trimmedTitle !== whiteboardTitle) { + updateTitle.mutate(trimmedTitle); + } + setIsEditing(false); + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + e.stopPropagation(); + if (e.key === "Enter") { + saveTitle(); + } else if (e.key === "Escape") { + cancelEdit(); + } + }; + + const handleInputClick = (e: React.MouseEvent) => { + e.stopPropagation(); + }; + + return ( +
e.stopPropagation()} + > +
+
router.push("/dashboard")} + > + +
+
+ {isEditing && isEditable ? ( + setEditedTitle(e.target.value)} + onKeyDown={handleKeyDown} + onClick={handleInputClick} + className="tex rounded border border-blue-400 bg-white py-1 text-gray-800 outline-none focus:border-blue-500 focus:ring-2 focus:ring-blue-200" + maxLength={50} + aria-label="Whiteboard title" + /> + ) : ( +
+ {editedTitle} + {!isEditable && ( + + (view only) + + )} +
+ )} +
+
+ ); +}; + +export default MenuBar; diff --git a/client/src/components/shape-node/ShapeNode.tsx b/client/src/components/shape-node/ShapeNode.tsx new file mode 100644 index 00000000..deb13c94 --- /dev/null +++ b/client/src/components/shape-node/ShapeNode.tsx @@ -0,0 +1,196 @@ +"use client"; + +import { memo, useCallback, useRef } from "react"; +import { useParams } from "next/navigation"; +import { + Handle, + NodeProps, + NodeResizer, + NodeToolbar, + Position, + useReactFlow, +} from "@xyflow/react"; +import StyleBar from "@/components/style-bar/StyleBar"; +import { + getFontStyle, + handleStyle, + NodeProperties, +} from "@/types/NodeProperties"; +import { updateNode } from "@/util/updateNode"; +import { useAmIOwner } from "@/hooks/api/whiteboard.api"; +import { useGetMe } from "@/hooks/api/account.api"; +import shapeRegistry from "@/util/shapeRegistry"; + +export interface ShapeNodeParams extends NodeProps { + id: string; + data: { + label: string; + shapeType: string; + nodeProperties: NodeProperties; + }; + selected: boolean; +} + +const ShapeNode = ({ id, data, selected }: ShapeNodeParams) => { + const { nodeProperties, label } = data; + const { setNodes } = useReactFlow(); + const inputRef = useRef(null); + + const params = useParams(); + const whiteboardId = Number(params.id); + + const { data: user } = useGetMe(); + const { data: isOwner } = useAmIOwner(whiteboardId, user?.id); + + const Shape = shapeRegistry({ shapeType: data.shapeType }); + + const onUpdateNode = (updater: { + label?: string; + nodeProperties?: Partial; + }) => { + setNodes(updateNode(id, updater)); + }; + + const handleClick = useCallback( + (evt: React.MouseEvent) => { + evt.stopPropagation(); + setNodes((nodes) => + nodes.map((node) => ({ + ...node, + selected: node.id === id, + })), + ); + }, + [id, setNodes], + ); + + return ( +
+ {isOwner && ( + +
+ ) => + onUpdateNode({ nodeProperties: updatedProperties }) + } + onUpdateLabel={(newLabel: string) => + onUpdateNode({ label: newLabel }) + } + selectedNodeLabel={label} + /> +
+
+ )} + + + +
+ {Shape && ( +
+ +
+ )} + + onUpdateNode({ label: e.target.value })} + placeholder="" + /> +
+ + + + + + + + + + + + +
+ ); +}; + +export default memo(ShapeNode); diff --git a/client/src/components/sidebar/Sidebar.tsx b/client/src/components/sidebar/Sidebar.tsx new file mode 100644 index 00000000..74a1b3d2 --- /dev/null +++ b/client/src/components/sidebar/Sidebar.tsx @@ -0,0 +1,121 @@ +import { Type } from "lucide-react"; +import { Node } from "@xyflow/react"; +import Circle from "@/assets/shapes/circle.svg"; +import Rectangle from "@/assets/shapes/rectangle.svg"; +import Trapezoid from "@/assets/shapes/trapezoid.svg"; +import Diamond from "@/assets/shapes/diamond.svg"; +import Hexagon from "@/assets/shapes/hexagon.svg"; +import Parallelogram from "@/assets/shapes/parallelogram.svg"; +import Triangle from "@/assets/shapes/triangle.svg"; +import { + defaultShapeNodeProperties, + defaultTextNodeProperties, +} from "@/types/NodeProperties"; + +interface SidebarProps { + onAddNode: (node: Node) => void; +} + +export default function Sidebar({ onAddNode }: SidebarProps) { + const menuItems = [ + { icon: Circle, label: "Circle", shape: "circle" }, + { + icon: Diamond, + label: "Diamond", + shape: "diamond", + }, + { + icon: Hexagon, + label: "Hexagon", + shape: "hexagon", + }, + { + icon: Parallelogram, + label: "Parallelogram", + shape: "parallelogram", + }, + { + icon: Rectangle, + label: "Rectangle", + shape: "rectangle", + }, + { + icon: Trapezoid, + label: "Trapezoid", + shape: "trapezoid", + }, + { + icon: Triangle, + label: "Triangle", + shape: "triangle", + }, + ]; + + const additionalItems = [{ icon: Type, label: "Text" }]; + + const handleAddTextNode = () => { + const newNode: Node = { + id: `text-${Date.now()}`, // Use timestamp for unique ID + type: "text", + position: { x: Math.random() * 300, y: Math.random() * 300 }, // Random position + data: { + label: "Add your text here", + nodeProperties: defaultTextNodeProperties, + }, + }; + onAddNode(newNode); + }; + + const handleAddShapeNode = (item: (typeof menuItems)[0]) => { + const newNode: Node = { + id: `shape-${Date.now()}`, // Use timestamp for unique ID // TODO combine with user id so it s actually unique + type: "shapeNode", + data: { + shapeType: item.shape, + label: item.label, + nodeProperties: defaultShapeNodeProperties, + }, + position: { x: Math.random() * 300, y: Math.random() * 300 }, // Random position + }; + onAddNode(newNode); + }; + + return ( +
+ {additionalItems.map((item, index) => { + const IconComponent = item.icon; + return ( + + ); + })} + +
+ +
+ {menuItems.map((item, index) => { + const IconComponent = item.icon; + return ( + + ); + })} +
+
+ ); +} diff --git a/client/src/components/style-bar/StyleBar.tsx b/client/src/components/style-bar/StyleBar.tsx new file mode 100644 index 00000000..955dea3d --- /dev/null +++ b/client/src/components/style-bar/StyleBar.tsx @@ -0,0 +1,242 @@ +"use client"; +import React from "react"; +import { memo } from "react"; +import StylePopover from "@/components/style-bar/style-bar-components/StylePopover"; +import FontSizeSelector from "@/components/style-bar/style-bar-components/FontSizeSelector"; +import TextStylingSelector from "@/components/style-bar/style-bar-components/TextStylingSelector"; +import FontFamilySelector from "@/components/style-bar/style-bar-components/FontFamilySelector"; +import { checkerboardStyle, NodeProperties } from "@/types/NodeProperties"; +import { + useTextRephrase, + useTextCompletion, + useTextSummarization, +} from "@/hooks/api/llm.api"; +import { AIActionDropdown } from "../aiActionDropdown/aiActionDropdown"; + +interface StyleBarProps { + nodeProperties: NodeProperties; + onUpdateNode: (updatedProperties: Partial) => void; + onUpdateLabel: (newLabel: string) => void; + selectedNodeLabel: string; +} + +const StyleBar = ({ + nodeProperties, + onUpdateNode, + onUpdateLabel, + selectedNodeLabel, +}: StyleBarProps) => { + const { + color: bgColor, + borderColor, + textColor, + fontSize, + fontFamily, + isBold, + isItalic, + isStrikethrough, + isUnderline, + borderWidth = 1, + borderOpacity = 1, + opacity = 1, + } = nodeProperties; + + const { mutateAsync: rephraseText } = useTextRephrase(); + const { mutateAsync: completeText } = useTextCompletion(); + const { mutateAsync: summarizedText } = useTextSummarization(); + + const onChangeBgColor = (color: string) => { + onUpdateNode({ color }); + }; + + const onChangeBorderColor = (color: string) => { + onUpdateNode({ borderColor: color }); + }; + + const onChangeTextColor = (color: string) => { + onUpdateNode({ textColor: color }); + }; + + const onChangeOpacity = (opacity: number) => { + onUpdateNode({ opacity }); + }; + + const onChangeBorderWidth = (borderWidth: number) => { + onUpdateNode({ borderWidth }); + }; + + const onChangeBorderOpacity = (borderOpacity: number) => { + onUpdateNode({ borderOpacity }); + }; + + const onChangeFontSize = (fontSize: number) => { + onUpdateNode({ fontSize }); + }; + + const onChangeFontFamily = (fontFamily: string) => { + onUpdateNode({ fontFamily }); + }; + + const onToggleBold = () => { + onUpdateNode({ isBold: !isBold }); + }; + + const onToggleItalic = () => { + onUpdateNode({ isItalic: !isItalic }); + }; + + const onToggleUnderline = () => { + onUpdateNode({ isUnderline: !isUnderline }); + }; + + const onToggleStrikethrough = () => { + onUpdateNode({ isStrikethrough: !isStrikethrough }); + }; + + const handleAIAction = async ( + action: "complete" | "summarize" | "rephrase", + ) => { + try { + let data; + + if (action === "rephrase") { + data = await rephraseText({ user_text: selectedNodeLabel }); + } else if (action === "complete") { + data = await completeText({ user_text: selectedNodeLabel }); + } else { + data = await summarizedText({ user_text: selectedNodeLabel }); + } + + const llmResponse = data.llm_response; + onUpdateLabel(llmResponse); + } catch (error) { + console.error("LLM error:", error); + } + }; + + return ( + <> +
+ + } + sliders={[ + { + title: "Opacity", + value: opacity, + onChange: onChangeOpacity, + min: 0, + max: 1, + step: 0.01, + unit: "×", + }, + ]} + /> + + +
+
+ } + sliders={[ + { + title: "Width", + value: borderWidth, + onChange: onChangeBorderWidth, + min: 0, + max: 10, + step: 1, + unit: "px", + }, + { + title: "Opacity", + value: borderOpacity, + onChange: onChangeBorderOpacity, + min: 0, + max: 1, + step: 0.01, + unit: "×", + }, + ]} + /> + +
+ + + A +
+
+ } + /> + +
+ + + +
+ + + +
+ + + + +
+ + ); +}; + +export default memo(StyleBar); diff --git a/client/src/components/style-bar/style-bar-components/FontFamilySelector.tsx b/client/src/components/style-bar/style-bar-components/FontFamilySelector.tsx new file mode 100644 index 00000000..6cafa4db --- /dev/null +++ b/client/src/components/style-bar/style-bar-components/FontFamilySelector.tsx @@ -0,0 +1,47 @@ +"use client"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, + SelectGroup, +} from "@/components/ui/select"; +import { allFonts, getFontStyle } from "@/types/NodeProperties"; + +interface FontSelectorProps { + fontFamily: string; + onChangeFontFamily: (font: string) => void; +} + +export default function FontFamilySelector({ + fontFamily, + onChangeFontFamily, +}: FontSelectorProps) { + return ( +
+ +
+ ); +} diff --git a/client/src/components/style-bar/style-bar-components/FontSizeSelector.tsx b/client/src/components/style-bar/style-bar-components/FontSizeSelector.tsx new file mode 100644 index 00000000..8340d36e --- /dev/null +++ b/client/src/components/style-bar/style-bar-components/FontSizeSelector.tsx @@ -0,0 +1,77 @@ +"use client"; +import { ChevronUp, ChevronDown } from "lucide-react"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { fontSizes } from "@/types/NodeProperties"; + +interface FontSizeSelectorProps { + fontSize: number; + onChangeFontSize: (fontSize: number) => void; +} + +export default function FontSizeSelector({ + fontSize, + onChangeFontSize, +}: FontSizeSelectorProps) { + const increaseFontSize = () => { + const currentIndex = fontSizes.indexOf(fontSize); + if (currentIndex < fontSizes.length - 1) { + onChangeFontSize(fontSizes[currentIndex + 1]); + } + }; + + const decreaseFontSize = () => { + const currentIndex = fontSizes.indexOf(fontSize); + if (currentIndex > 0) { + onChangeFontSize(fontSizes[currentIndex - 1]); + } + }; + + return ( +
+ + +
+ + +
+
+ ); +} diff --git a/client/src/components/style-bar/style-bar-components/StylePopover.tsx b/client/src/components/style-bar/style-bar-components/StylePopover.tsx new file mode 100644 index 00000000..ac0a98c4 --- /dev/null +++ b/client/src/components/style-bar/style-bar-components/StylePopover.tsx @@ -0,0 +1,79 @@ +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; +import ColorPickerPanel from "@/components/style-bar/style-bar-components/style-popover-components/ColorPickerPanel"; +import React, { useState } from "react"; +import SliderControl from "@/components/style-bar/style-bar-components/style-popover-components/SliderControl"; + +interface SliderConfig { + title: string; + value: number; + onChange: (value: number) => void; + min?: number; + max?: number; + step?: number; + unit?: string; +} + +interface StylePopoverProps { + title: string; + color: string; + onChangeColor: (color: string) => void; + buttonIcon: React.ReactNode; + sliders?: SliderConfig[]; +} + +export default function StylePopover({ + title, + color, + onChangeColor, + buttonIcon, + sliders, +}: StylePopoverProps) { + const [localColor, setLocalColor] = useState(color); + + const handleColorChange = (newColor: string) => { + setLocalColor(newColor); + onChangeColor(newColor); + }; + + return ( + + + + + + +
{title}
+ + {sliders && ( +
+ {sliders.map((slider, index) => ( + + ))} +
+ )} + + +
+
+ ); +} diff --git a/client/src/components/style-bar/style-bar-components/TextStylingSelector.tsx b/client/src/components/style-bar/style-bar-components/TextStylingSelector.tsx new file mode 100644 index 00000000..0e02cfb2 --- /dev/null +++ b/client/src/components/style-bar/style-bar-components/TextStylingSelector.tsx @@ -0,0 +1,83 @@ +import { ReactNode } from "react"; +import { Bold, Italic, Underline, Strikethrough } from "lucide-react"; + +interface TextStylingSelectorProps { + isBold: boolean; + isItalic: boolean; + isStrikethrough: boolean; + isUnderline: boolean; + onToggleBold: () => void; + onToggleItalic: () => void; + onToggleUnderline: () => void; + onToggleStrikethrough: () => void; +} + +interface TextStylingButtonProps { + isToggle: boolean; + onToggle: () => void; + children: ReactNode; +} + +function TextStylingButton({ + isToggle, + onToggle, + children, +}: TextStylingButtonProps) { + return ( + + ); +} + +export default function TextStylingSelector({ + isBold, + isItalic, + isStrikethrough, + isUnderline, + onToggleItalic, + onToggleUnderline, + onToggleStrikethrough, + onToggleBold, +}: TextStylingSelectorProps) { + return ( +
+ + + + + + + + + + + + + + + +
+ ); +} diff --git a/client/src/components/style-bar/style-bar-components/style-popover-components/ColorPickerPanel.tsx b/client/src/components/style-bar/style-bar-components/style-popover-components/ColorPickerPanel.tsx new file mode 100644 index 00000000..f255604f --- /dev/null +++ b/client/src/components/style-bar/style-bar-components/style-popover-components/ColorPickerPanel.tsx @@ -0,0 +1,106 @@ +import React from "react"; +import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"; +import { HexColorPicker, HexColorInput } from "react-colorful"; +import { checkerboardStyle, predefinedColors } from "@/types/NodeProperties"; + +type Props = { + color: string; + localColor: string; + handleColorChange: (color: string) => void; + isTextPopOver?: boolean; +}; + +const ColorOption = ({ + color, + onClick, + isSelected = false, +}: { + color: string; + onClick: () => void; + isSelected?: boolean; +}) => ( +
+); + +const TransparentOption = ({ + onClick, + isSelected = false, +}: { + onClick: () => void; + isSelected?: boolean; +}) => ( +
+); + +const ColorPickerPanel: React.FC = ({ + color, + localColor, + handleColorChange, + isTextPopOver, +}) => { + const isTransparent = color === "none"; + + return ( + + + + Palette + + + Custom + + + + +
+ {!isTextPopOver && ( + handleColorChange("none")} + isSelected={isTransparent} + /> + )} + {predefinedColors.map((hex, idx) => ( + handleColorChange(hex)} + isSelected={color === hex} + /> + ))} +
+
+ + + +
+ # + +
+
+
+ ); +}; + +export default ColorPickerPanel; diff --git a/client/src/components/style-bar/style-bar-components/style-popover-components/SliderControl.tsx b/client/src/components/style-bar/style-bar-components/style-popover-components/SliderControl.tsx new file mode 100644 index 00000000..cd150e21 --- /dev/null +++ b/client/src/components/style-bar/style-bar-components/style-popover-components/SliderControl.tsx @@ -0,0 +1,42 @@ +import { Slider } from "@/components/ui/slider"; + +interface SliderControlProps { + value: number; + title: string; + onChange: (value: number) => void; + min: number; + max: number; + step: number; + unit?: string; +} + +export default function SliderControl({ + value, + title, + onChange, + min, + max, + step, + unit = "", +}: SliderControlProps) { + return ( + <> + onChange(values[0])} + className="m-0" + /> + +
+

{title}

+

+ {value} + {unit} +

+
+ + ); +} diff --git a/client/src/components/text-node/TextNode.tsx b/client/src/components/text-node/TextNode.tsx new file mode 100644 index 00000000..c275efb9 --- /dev/null +++ b/client/src/components/text-node/TextNode.tsx @@ -0,0 +1,212 @@ +import React, { useEffect, useState } from "react"; +import { + Handle, + NodeProps, + NodeResizer, + NodeToolbar, + Position, + useReactFlow, +} from "@xyflow/react"; +import { + getFontStyle, + handleStyle, + NodeProperties, +} from "@/types/NodeProperties"; +import StyleBar from "@/components/style-bar/StyleBar"; +import { updateNode } from "@/util/updateNode"; +import { useAmIOwner } from "@/hooks/api/whiteboard.api"; +import { useGetMe } from "@/hooks/api/account.api"; +import { useParams } from "next/navigation"; + +interface TextNodeProps extends NodeProps { + id: string; + data: { + label?: string; + nodeProperties: NodeProperties; + }; + selected: boolean; +} + +function hexToRgb(hex: string) { + const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return result + ? { + r: parseInt(result[1], 16), + g: parseInt(result[2], 16), + b: parseInt(result[3], 16), + } + : null; +} + +export default function TextNode({ id, data, selected }: TextNodeProps) { + const [isEditing, setIsEditing] = useState(false); + const [text, setText] = useState(data.label as string); + const { setNodes } = useReactFlow(); + + const params = useParams(); + const whiteboardId = Number(params.id); + + const { data: user } = useGetMe(); + const { data: isOwner } = useAmIOwner(whiteboardId, user?.id); + + const { nodeProperties, label } = data; + const bgRgb = hexToRgb(nodeProperties.color); + const borderRgb = hexToRgb(nodeProperties.borderColor); + + const showBorder = borderRgb && nodeProperties.borderColor !== "none"; + const showBackground = bgRgb && nodeProperties.color !== "none"; + + const onUpdateNode = (updater: { + label?: string; + nodeProperties?: Partial; + }) => { + setNodes(updateNode(id, updater)); + }; + + const handleTextChange = (newText: string) => { + setText(newText); + onUpdateNode({ label: newText }); + }; + + useEffect(() => { + setText(data.label as string); + }, [data.label]); + + return ( + <> + {isOwner && ( + + ) => + onUpdateNode({ nodeProperties: updatedProperties }) + } + onUpdateLabel={(newLabel: string) => + onUpdateNode({ label: newLabel }) + } + selectedNodeLabel={text} + /> + + )} + + + +
+
{ + e.stopPropagation(); + setIsEditing(true); + }} + > + {isEditing ? ( + handleTextChange(e.target.value)} + onBlur={() => setIsEditing(false)} + onKeyDown={(e) => { + if (e.key === "Enter") { + setIsEditing(false); + } + }} + autoFocus + className="m-0 h-auto w-auto border-none bg-transparent p-0 text-center outline-none" + style={{ + color: nodeProperties.textColor, + fontSize: `${nodeProperties.fontSize}px`, + fontFamily: getFontStyle(nodeProperties.fontFamily), + fontWeight: nodeProperties.isBold ? "bold" : "normal", + fontStyle: nodeProperties.isItalic ? "italic" : "normal", + textDecoration: + `${nodeProperties.isUnderline ? "underline" : ""} ${nodeProperties.isStrikethrough ? "line-through" : ""}`.trim() || + "none", + }} + /> + ) : ( +

+ {label} +

+ )} +
+
+ + + + + + + + + + + + + + ); +} diff --git a/client/src/components/ui/alert-dialog.tsx b/client/src/components/ui/alert-dialog.tsx new file mode 100644 index 00000000..5006362e --- /dev/null +++ b/client/src/components/ui/alert-dialog.tsx @@ -0,0 +1,157 @@ +"use client"; + +import * as React from "react"; +import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"; + +import { cn } from "@/util/utils"; +import { buttonVariants } from "@/components/ui/button"; + +function AlertDialog({ + ...props +}: React.ComponentProps) { + return ; +} + +function AlertDialogTrigger({ + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AlertDialogPortal({ + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AlertDialogOverlay({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AlertDialogContent({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + + ); +} + +function AlertDialogHeader({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ); +} + +function AlertDialogFooter({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ); +} + +function AlertDialogTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AlertDialogDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AlertDialogAction({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AlertDialogCancel({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +export { + AlertDialog, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +}; diff --git a/client/src/components/ui/avatar.tsx b/client/src/components/ui/avatar.tsx new file mode 100644 index 00000000..19d1bfb4 --- /dev/null +++ b/client/src/components/ui/avatar.tsx @@ -0,0 +1,53 @@ +"use client"; + +import * as React from "react"; +import * as AvatarPrimitive from "@radix-ui/react-avatar"; + +import { cn } from "@/util/utils"; + +function Avatar({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AvatarImage({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AvatarFallback({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +export { Avatar, AvatarImage, AvatarFallback }; diff --git a/client/src/components/ui/badge.tsx b/client/src/components/ui/badge.tsx new file mode 100644 index 00000000..ccbb3e23 --- /dev/null +++ b/client/src/components/ui/badge.tsx @@ -0,0 +1,46 @@ +import * as React from "react"; +import { Slot } from "@radix-ui/react-slot"; +import { cva, type VariantProps } from "class-variance-authority"; + +import { cn } from "@/util/utils"; + +const badgeVariants = cva( + "inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden", + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90", + secondary: + "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90", + destructive: + "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + outline: + "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + }, +); + +function Badge({ + className, + variant, + asChild = false, + ...props +}: React.ComponentProps<"span"> & + VariantProps & { asChild?: boolean }) { + const Comp = asChild ? Slot : "span"; + + return ( + + ); +} + +export { Badge, badgeVariants }; diff --git a/client/src/components/ui/dialog.tsx b/client/src/components/ui/dialog.tsx new file mode 100644 index 00000000..f2221f59 --- /dev/null +++ b/client/src/components/ui/dialog.tsx @@ -0,0 +1,143 @@ +"use client"; + +import * as React from "react"; +import * as DialogPrimitive from "@radix-ui/react-dialog"; +import { XIcon } from "lucide-react"; + +import { cn } from "@/util/utils"; + +function Dialog({ + ...props +}: React.ComponentProps) { + return ; +} + +function DialogTrigger({ + ...props +}: React.ComponentProps) { + return ; +} + +function DialogPortal({ + ...props +}: React.ComponentProps) { + return ; +} + +function DialogClose({ + ...props +}: React.ComponentProps) { + return ; +} + +function DialogOverlay({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function DialogContent({ + className, + children, + showCloseButton = true, + ...props +}: React.ComponentProps & { + showCloseButton?: boolean; +}) { + return ( + + + + {children} + {showCloseButton && ( + + + Close + + )} + + + ); +} + +function DialogHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ); +} + +function DialogFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ); +} + +function DialogTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function DialogDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +export { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogOverlay, + DialogPortal, + DialogTitle, + DialogTrigger, +}; diff --git a/client/src/components/ui/dropdown-menu.tsx b/client/src/components/ui/dropdown-menu.tsx new file mode 100644 index 00000000..85bc2b5a --- /dev/null +++ b/client/src/components/ui/dropdown-menu.tsx @@ -0,0 +1,257 @@ +"use client"; + +import * as React from "react"; +import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"; +import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"; + +import { cn } from "@/util/utils"; + +function DropdownMenu({ + ...props +}: React.ComponentProps) { + return ; +} + +function DropdownMenuPortal({ + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function DropdownMenuTrigger({ + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function DropdownMenuContent({ + className, + sideOffset = 4, + ...props +}: React.ComponentProps) { + return ( + + + + ); +} + +function DropdownMenuGroup({ + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function DropdownMenuItem({ + className, + inset, + variant = "default", + ...props +}: React.ComponentProps & { + inset?: boolean; + variant?: "default" | "destructive"; +}) { + return ( + + ); +} + +function DropdownMenuCheckboxItem({ + className, + children, + checked, + ...props +}: React.ComponentProps) { + return ( + + + + + + + {children} + + ); +} + +function DropdownMenuRadioGroup({ + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function DropdownMenuRadioItem({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + + + + + + {children} + + ); +} + +function DropdownMenuLabel({ + className, + inset, + ...props +}: React.ComponentProps & { + inset?: boolean; +}) { + return ( + + ); +} + +function DropdownMenuSeparator({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function DropdownMenuShortcut({ + className, + ...props +}: React.ComponentProps<"span">) { + return ( + + ); +} + +function DropdownMenuSub({ + ...props +}: React.ComponentProps) { + return ; +} + +function DropdownMenuSubTrigger({ + className, + inset, + children, + ...props +}: React.ComponentProps & { + inset?: boolean; +}) { + return ( + + {children} + + + ); +} + +function DropdownMenuSubContent({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +export { + DropdownMenu, + DropdownMenuPortal, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuLabel, + DropdownMenuItem, + DropdownMenuCheckboxItem, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuSub, + DropdownMenuSubTrigger, + DropdownMenuSubContent, +}; diff --git a/client/src/components/ui/input.tsx b/client/src/components/ui/input.tsx new file mode 100644 index 00000000..793d637f --- /dev/null +++ b/client/src/components/ui/input.tsx @@ -0,0 +1,21 @@ +import * as React from "react"; + +import { cn } from "@/util/utils"; + +function Input({ className, type, ...props }: React.ComponentProps<"input">) { + return ( + + ); +} + +export { Input }; diff --git a/client/src/components/ui/popover.tsx b/client/src/components/ui/popover.tsx new file mode 100644 index 00000000..88c8bd06 --- /dev/null +++ b/client/src/components/ui/popover.tsx @@ -0,0 +1,48 @@ +"use client"; + +import * as React from "react"; +import * as PopoverPrimitive from "@radix-ui/react-popover"; + +import { cn } from "@/util/utils"; + +function Popover({ + ...props +}: React.ComponentProps) { + return ; +} + +function PopoverTrigger({ + ...props +}: React.ComponentProps) { + return ; +} + +function PopoverContent({ + className, + align = "center", + sideOffset = 4, + ...props +}: React.ComponentProps) { + return ( + + + + ); +} + +function PopoverAnchor({ + ...props +}: React.ComponentProps) { + return ; +} + +export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }; diff --git a/client/src/components/ui/select.tsx b/client/src/components/ui/select.tsx new file mode 100644 index 00000000..1281df1d --- /dev/null +++ b/client/src/components/ui/select.tsx @@ -0,0 +1,185 @@ +"use client"; + +import * as React from "react"; +import * as SelectPrimitive from "@radix-ui/react-select"; +import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"; + +import { cn } from "@/util/utils"; + +function Select({ + ...props +}: React.ComponentProps) { + return ; +} + +function SelectGroup({ + ...props +}: React.ComponentProps) { + return ; +} + +function SelectValue({ + ...props +}: React.ComponentProps) { + return ; +} + +function SelectTrigger({ + className, + size = "default", + children, + ...props +}: React.ComponentProps & { + size?: "sm" | "default"; +}) { + return ( + + {children} + + + + + ); +} + +function SelectContent({ + className, + children, + position = "popper", + ...props +}: React.ComponentProps) { + return ( + + + + + {children} + + + + + ); +} + +function SelectLabel({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function SelectItem({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + + + + + + {children} + + ); +} + +function SelectSeparator({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function SelectScrollUpButton({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + ); +} + +function SelectScrollDownButton({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + ); +} + +export { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectLabel, + SelectScrollDownButton, + SelectScrollUpButton, + SelectSeparator, + SelectTrigger, + SelectValue, +}; diff --git a/client/src/components/ui/separator.tsx b/client/src/components/ui/separator.tsx new file mode 100644 index 00000000..1f6315f2 --- /dev/null +++ b/client/src/components/ui/separator.tsx @@ -0,0 +1,28 @@ +"use client"; + +import * as React from "react"; +import * as SeparatorPrimitive from "@radix-ui/react-separator"; + +import { cn } from "@/util/utils"; + +function Separator({ + className, + orientation = "horizontal", + decorative = true, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +export { Separator }; diff --git a/client/src/components/ui/slider.tsx b/client/src/components/ui/slider.tsx new file mode 100644 index 00000000..60479658 --- /dev/null +++ b/client/src/components/ui/slider.tsx @@ -0,0 +1,63 @@ +"use client"; + +import * as React from "react"; +import * as SliderPrimitive from "@radix-ui/react-slider"; + +import { cn } from "@/util/utils"; + +function Slider({ + className, + defaultValue, + value, + min = 0, + max = 100, + ...props +}: React.ComponentProps) { + const _values = React.useMemo( + () => + Array.isArray(value) + ? value + : Array.isArray(defaultValue) + ? defaultValue + : [min, max], + [value, defaultValue, min, max], + ); + + return ( + + + + + {Array.from({ length: _values.length }, (_, index) => ( + + ))} + + ); +} + +export { Slider }; diff --git a/client/src/components/ui/tabs.tsx b/client/src/components/ui/tabs.tsx new file mode 100644 index 00000000..159c9739 --- /dev/null +++ b/client/src/components/ui/tabs.tsx @@ -0,0 +1,66 @@ +"use client"; + +import * as React from "react"; +import * as TabsPrimitive from "@radix-ui/react-tabs"; + +import { cn } from "@/util/utils"; + +function Tabs({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function TabsList({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function TabsTrigger({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function TabsContent({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +export { Tabs, TabsList, TabsTrigger, TabsContent }; diff --git a/client/src/components/user-dropdown/UserDropdown.tsx b/client/src/components/user-dropdown/UserDropdown.tsx new file mode 100644 index 00000000..ec9a8801 --- /dev/null +++ b/client/src/components/user-dropdown/UserDropdown.tsx @@ -0,0 +1,91 @@ +"use client"; +import { signOut as nextAuthSignOut, useSession } from "next-auth/react"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { LogOut, UserIcon } from "lucide-react"; +import React, { useState } from "react"; +import AccountModal from "@/components/account-modal/AccountModal"; +import { useGetMe } from "@/hooks/api/account.api"; +import Avatar from "@/components/avatar/Avatar"; + +export default function UserDropdown() { + const [userDropdownOpen, setUserDropdownOpen] = useState(false); + const [isAccountModalOpen, setIsAccountModalOpen] = useState(false); + + const { data: user } = useGetMe(); + + const session = useSession(); + + async function signOut() { + try { + await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/auth/sso-sign-out`, { + headers: { + // @ts-ignore + refresh_token: session.data?.refreshToken, + }, + }); + } catch (error) { + console.error("Error occurred during sign out:", error); + } + await nextAuthSignOut(); + } + + return ( + <> + + +
+ + Hi {user?.username}! +
+
+ +
+ + + {user?.firstName} {user?.lastName} + +
+ + setIsAccountModalOpen(true)} + > + + Profile + + + + + Log out + +
+
+ setIsAccountModalOpen(false)} + /> + + ); +} diff --git a/client/src/components/whiteboard-card/CreateWhiteboardCard.tsx b/client/src/components/whiteboard-card/CreateWhiteboardCard.tsx new file mode 100644 index 00000000..fd790c81 --- /dev/null +++ b/client/src/components/whiteboard-card/CreateWhiteboardCard.tsx @@ -0,0 +1,29 @@ +import { Plus } from "lucide-react"; +import { useCreateWhiteboard } from "@/hooks/api/whiteboard.api"; +import generateWhiteboardName from "@/util/generateWhiteboardName"; + +export default function CreateWhiteboardCard() { + const createMutation = useCreateWhiteboard(); + const title = generateWhiteboardName(); + const handleCreate = () => createMutation.mutate(title); + + return ( +
+
+
+
+ +
+
+
+
+

+ Create a new board +

+
+
+ ); +} diff --git a/client/src/components/whiteboard-card/WhiteboardCard.tsx b/client/src/components/whiteboard-card/WhiteboardCard.tsx new file mode 100644 index 00000000..1016e9e6 --- /dev/null +++ b/client/src/components/whiteboard-card/WhiteboardCard.tsx @@ -0,0 +1,126 @@ +"use client"; + +import React, { useState, useRef, useEffect, useMemo } from "react"; +import formatDate from "@/util/formatDate"; +import { useRouter } from "next/navigation"; +import { WhiteboardResponse } from "@/api/main/generated"; +import { + useDeleteWhiteboard, + useUpdateWhiteboardTitle, +} from "@/hooks/api/whiteboard.api"; +import WhiteboardThumbnail from "@/components/whiteboard-card/whiteboard-card-components/WhiteboardThumbnail"; +import WhiteboardEditPopover from "@/components/whiteboard-card/whiteboard-card-components/WhiteboardEditPopover"; +import { useGetMe } from "@/hooks/api/account.api"; + +export default function ProjectCard({ + project, +}: { + project: WhiteboardResponse; +}) { + const router = useRouter(); + + const [isEditing, setIsEditing] = useState(false); + const [editedTitle, setEditedTitle] = useState(project.title!); + const inputRef = useRef(null); + + const deleteWhiteboard = useDeleteWhiteboard(); + const updateTitle = useUpdateWhiteboardTitle(project.id!); + const getCurrentUser = useGetMe(); + + useEffect(() => { + if (!isEditing) return; + + const handleClickOutside = (event: MouseEvent) => { + if ( + inputRef.current && + !inputRef.current.contains(event.target as Node) + ) { + setIsEditing(false); + } + }; + + document.addEventListener("mousedown", handleClickOutside); + return () => document.removeEventListener("mousedown", handleClickOutside); + }, [isEditing, editedTitle]); + + useEffect(() => { + if (isEditing && inputRef.current) { + inputRef.current.focus(); + inputRef.current.select(); + } + }, [isEditing]); + + const handleRename = () => { + setIsEditing(true); + setEditedTitle(project.title!); + }; + + const handleDelete = () => { + deleteWhiteboard.mutate(project.id!); + }; + + const handleSave = () => { + const trimmedTitle = editedTitle.trim(); + if (trimmedTitle && trimmedTitle !== project.title) { + updateTitle.mutate(trimmedTitle); + } + setIsEditing(false); + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === "Enter") { + handleSave(); + } else if (e.key === "Escape") { + setEditedTitle(project.title!); + setIsEditing(false); + } + }; + const isOwner = useMemo(() => { + return getCurrentUser.data?.id === project.user?.id; + }, [getCurrentUser.data, project.user]); + + return ( +
router.push(`/board/${project.id}`)} + > +
+ +
+
+
+
+ {isEditing ? ( + setEditedTitle(e.target.value)} + onKeyDown={handleKeyDown} + className="w-full rounded border border-blue-500 bg-transparent px-1 py-0.5 font-medium text-gray-900 outline-none focus:ring-1 focus:ring-blue-500" + onClick={(e) => e.stopPropagation()} + /> + ) : ( +

+ {project.title} +

+ )} +
+ +
+ Last edited: + {formatDate(project.lastUpdatedAt!)} +
+
+ {isOwner && ( +
e.stopPropagation()}> + +
+ )} +
+
+ ); +} diff --git a/client/src/components/whiteboard-card/whiteboard-card-components/DeletionAlertDialog.tsx b/client/src/components/whiteboard-card/whiteboard-card-components/DeletionAlertDialog.tsx new file mode 100644 index 00000000..9d82d030 --- /dev/null +++ b/client/src/components/whiteboard-card/whiteboard-card-components/DeletionAlertDialog.tsx @@ -0,0 +1,46 @@ +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "@/components/ui/alert-dialog"; +import React from "react"; + +interface DeletionAlertDialogProps { + dialogOpen: boolean; + setDialogOpen: (open: boolean) => void; + handleConfirmDelete: () => void; +} + +export default function DeletionAlertDialog({ + dialogOpen, + setDialogOpen, + handleConfirmDelete, +}: DeletionAlertDialogProps) { + return ( + + + + Are you sure? + + This action cannot be undone. This will permanently delete your + board from our servers. + + + + Cancel + + Delete + + + + + ); +} diff --git a/client/src/components/whiteboard-card/whiteboard-card-components/WhiteboardEditPopover.tsx b/client/src/components/whiteboard-card/whiteboard-card-components/WhiteboardEditPopover.tsx new file mode 100644 index 00000000..0dc79183 --- /dev/null +++ b/client/src/components/whiteboard-card/whiteboard-card-components/WhiteboardEditPopover.tsx @@ -0,0 +1,92 @@ +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; +import { Ellipsis, Pencil, Trash2 } from "lucide-react"; +import React, { ComponentType, SVGProps, useState } from "react"; +import DeletionAlertDialog from "@/components/deletion-alert-dialog/DeletionAlertDialog"; + +interface ProjectEditPopoverProps { + onRename: () => void; + onDelete: () => void; +} + +interface PopoverOptionProps { + label: string; + Icon: ComponentType>; + onClick?: () => void; +} + +function PopoverOption({ label, Icon, onClick }: PopoverOptionProps) { + return ( +
{ + if (onClick) { + onClick(); + } + }} + > + + {label} +
+ ); +} + +export default function WhiteboardEditPopover({ + onRename, + onDelete, +}: ProjectEditPopoverProps) { + const [popoverOpen, setPopoverOpen] = useState(false); + const [dialogOpen, setDialogOpen] = useState(false); + + const handleRename = () => { + onRename(); + setPopoverOpen(false); + }; + + const handleDeleteClick = () => { + setPopoverOpen(false); + setDialogOpen(true); + }; + + const handleConfirmDelete = () => { + onDelete(); + setDialogOpen(false); + }; + + return ( + <> + + + + + + +
+ + +
+
+
+ + + + ); +} diff --git a/client/src/components/whiteboard-card/whiteboard-card-components/WhiteboardThumbnail.tsx b/client/src/components/whiteboard-card/whiteboard-card-components/WhiteboardThumbnail.tsx new file mode 100644 index 00000000..d923fddf --- /dev/null +++ b/client/src/components/whiteboard-card/whiteboard-card-components/WhiteboardThumbnail.tsx @@ -0,0 +1,204 @@ +import React, { useMemo } from "react"; + +const COLORS = [ + "bg-blue-100", + "bg-green-100", + "bg-yellow-100", + "bg-purple-100", + "bg-pink-100", + "bg-indigo-100", + "bg-red-100", + "bg-orange-100", + "bg-teal-100", +]; + +const EMOJI_PATTERNS = [ + "🧠", + "📊", + "✏️", + "🔍", + "💡", + "🚀", + "🎨", + "📝", + "🧩", + "📈", + "🔮", + "🌟", +]; + +const PATTERN_TYPES = [ + "dots", + "lines", + "waves", + "grid", + "triangles", + "circles", +]; + +interface ProjectThumbnailProps { + id: number; + title?: string; +} + +export default function WhiteboardThumbnail({ + id, + title, +}: ProjectThumbnailProps) { + const visualElements = useMemo(() => { + const idSum = + id + .toString() + .split("") + .reduce((sum, char) => sum + char.charCodeAt(0), 0) || 0; + + return { + color: COLORS[idSum % COLORS.length], + + emoji: EMOJI_PATTERNS[idSum % EMOJI_PATTERNS.length], + + patternType: PATTERN_TYPES[idSum % PATTERN_TYPES.length], + + initials: title + ? title + .split(" ") + .map((word) => word[0]?.toUpperCase() || "") + .join("") + .slice(0, 2) + : "?", + + shapeCount: (idSum % 5) + 3, + }; + }, [id, title]); + + switch (visualElements.patternType) { + case "dots": + return ( +
+
+ {[...Array(visualElements.shapeCount)].map((_, i) => ( +
+ ))} +
+ {visualElements.initials} +
+
+
+ ); + + case "lines": + return ( +
+
+ {[...Array(visualElements.shapeCount)].map((_, i) => ( +
+ ))} +
+ {visualElements.emoji} +
+
+
+ ); + + case "waves": + return ( +
+
+ {[...Array(visualElements.shapeCount)].map((_, i) => ( +
+ ))} +
+ {visualElements.emoji} +
+
+
+ ); + + case "grid": + return ( +
+
+ {[...Array(9)].map((_, i) => ( +
+ ))} +
+
+ {visualElements.initials} +
+
+ ); + + case "triangles": + return ( +
+
+ {[...Array(visualElements.shapeCount)].map((_, i) => ( +
+ ))} +
+ {visualElements.emoji} +
+
+
+ ); + + case "circles": + default: + return ( +
+
+ {visualElements.emoji} +
+
+ {visualElements.initials} +
+
+ ); + } +} diff --git a/client/src/hooks/api/account.api.ts b/client/src/hooks/api/account.api.ts new file mode 100644 index 00000000..9e6fcc28 --- /dev/null +++ b/client/src/hooks/api/account.api.ts @@ -0,0 +1,12 @@ +import { useQuery } from "@tanstack/react-query"; +import { accountApiFactory } from "@/api"; + +export const useGetMe = () => { + return useQuery({ + queryKey: ["me"], + queryFn: async () => { + const { data } = await accountApiFactory.getCurrentUser(); + return data; + }, + }); +}; diff --git a/client/src/hooks/api/llm.api.ts b/client/src/hooks/api/llm.api.ts new file mode 100644 index 00000000..1764eee9 --- /dev/null +++ b/client/src/hooks/api/llm.api.ts @@ -0,0 +1,23 @@ +import { llmApiFactory } from "@/api"; +import { useMutation } from "@tanstack/react-query"; +import { TextRequest } from "@/api/genai/generated"; + +export const useTextCompletion = () => + useMutation({ + mutationFn: (request: TextRequest) => + llmApiFactory.completeTextCompletionPost(request).then((res) => res.data), + }); + +export const useTextRephrase = () => + useMutation({ + mutationFn: (request: TextRequest) => + llmApiFactory.rephraseTextRephrasePost(request).then((res) => res.data), + }); + +export const useTextSummarization = () => + useMutation({ + mutationFn: (request: TextRequest) => + llmApiFactory + .summarizeTextSummarizationPost(request) + .then((res) => res.data), + }); diff --git a/client/src/hooks/api/root.api.ts b/client/src/hooks/api/root.api.ts new file mode 100644 index 00000000..2b988ea9 --- /dev/null +++ b/client/src/hooks/api/root.api.ts @@ -0,0 +1,9 @@ +import { useQuery } from "@tanstack/react-query"; +import { rootApiFactory } from "@/api"; + +export const useGetRoot = () => { + return useQuery({ + queryKey: ["root"], + queryFn: rootApiFactory.root, + }); +}; diff --git a/client/src/hooks/api/whiteboard.api.ts b/client/src/hooks/api/whiteboard.api.ts new file mode 100644 index 00000000..ce4b5316 --- /dev/null +++ b/client/src/hooks/api/whiteboard.api.ts @@ -0,0 +1,258 @@ +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { useRouter } from "next/navigation"; +import { whiteboardApiFactory } from "@/api"; +import { useCallback, useEffect, useRef } from "react"; +import { WhiteboardEvent } from "@/api/realtime/dtos/WhiteboardEvent"; +import { z } from "zod"; + +export function useWhiteboards() { + return useQuery({ + queryKey: ["whiteboards"], + queryFn: async () => { + const { data } = await whiteboardApiFactory.getUserWhiteboards(); + return data; + }, + }); +} + +export function useGetWhiteboardById(whiteboardId: number) { + return useQuery({ + queryKey: ["whiteboard", whiteboardId], + queryFn: async () => { + const { data } = + await whiteboardApiFactory.getWhiteboardById(whiteboardId); + return data; + }, + retry: 0, + }); +} + +export function useAmIOwner(whiteboardId: number, userId?: number) { + return useQuery({ + queryKey: ["whiteboard", whiteboardId, userId], + queryFn: async () => { + try { + const { data } = + await whiteboardApiFactory.getWhiteboardById(whiteboardId); + return data.user?.id === userId; + } catch { + return false; + } + }, + retry: false, + enabled: !!userId, + }); +} + +export function useGetWhiteboardTitle(whiteboardId: number) { + return useQuery({ + queryKey: ["whiteboardTitle", whiteboardId], + queryFn: async () => { + const { data } = + await whiteboardApiFactory.getWhiteboardTitle(whiteboardId); + return data; + }, + }); +} + +export function useCreateWhiteboard() { + const queryClient = useQueryClient(); + const router = useRouter(); + + return useMutation({ + mutationFn: (title: string = "Untitled") => + whiteboardApiFactory.createWhiteboard(title), + onSuccess: (response) => { + const newProject = response.data; + queryClient.invalidateQueries({ queryKey: ["whiteboards"] }); + router.push(`/board/${newProject.id}`); + }, + }); +} + +export const useDeleteWhiteboard = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (id: number) => whiteboardApiFactory.deleteWhiteboard(id), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["whiteboards"] }); + }, + }); +}; + +export const useUpdateWhiteboardTitle = (whiteboardId: number) => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (title: string) => + whiteboardApiFactory.updateTitle(whiteboardId, title), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["whiteboards"] }); + }, + }); +}; + +export const useInviteCollaboratorsToWhiteboard = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: ({ + whiteboardId, + emails, + }: { + whiteboardId: number; + emails: string[]; + }) => { + const inviteCollaboratorsRequest = { + emails: emails, + }; + return whiteboardApiFactory.inviteCollaborators( + whiteboardId, + inviteCollaboratorsRequest, + ); + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["whiteboard-collaborators"] }); + }, + }); +}; + +export const useRemoveCollaboratorsFromWhiteboard = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: ({ + whiteboardId, + userIds, + }: { + whiteboardId: number; + userIds: number[]; + }) => { + const removeCollaboratorsRequest = { + userIds: userIds, + }; + return whiteboardApiFactory.removeCollaborators( + whiteboardId, + removeCollaboratorsRequest, + ); + }, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ["whiteboard-collaborators"] }); + }, + }); +}; + +export const useGetWhiteboardCollaborators = (whiteboardId: number) => { + return useQuery({ + queryKey: ["whiteboard-collaborators"], + queryFn: async () => { + const { data } = + await whiteboardApiFactory.getCollaborators(whiteboardId); + return data; + }, + }); +}; + +export const useSubscribeToWhiteboardEvents = ( + whiteboardId: number, + onMessage: (data: z.infer) => void, +) => { + const retryTimeout = useRef(null); + const wsRef = useRef(null); + + useEffect(() => { + let shouldReconnect = true; + + const connect = () => { + const ws = new WebSocket( + `${process.env.NEXT_PUBLIC_REALTIME_URL}/ws/whiteboard/${whiteboardId}/subscribe`, + ); + wsRef.current = ws; + + ws.onopen = () => { + console.log("connected to subscription channel"); + }; + + ws.onmessage = (event) => { + const parsedJson = JSON.parse(event.data); + try { + const data = WhiteboardEvent.parse(parsedJson); + onMessage(data); + } catch (e) { + if (e instanceof z.ZodError) { + console.error("Zod validation error:", { + issues: e.issues, + originalPayload: parsedJson, + }); + } + } + }; + + ws.onclose = () => { + console.warn("WebSocket closed. Attempting to reconnect..."); + if (shouldReconnect) { + retryTimeout.current = setTimeout(connect, 2000); + } + }; + + ws.onerror = (err) => { + console.error("WebSocket error:", err); + ws.close(); + }; + }; + + connect(); + + return () => { + shouldReconnect = false; + if (retryTimeout.current) { + clearTimeout(retryTimeout.current); + } + wsRef.current?.close(); + }; + }, [whiteboardId, onMessage]); +}; + +export const usePublishWhiteboardEvents = (whiteboardId: number) => { + const retryTimeout = useRef(null); + const wsRef = useRef(null); + + useEffect(() => { + let shouldReconnect = true; + + const connect = () => { + const ws = new WebSocket( + `${process.env.NEXT_PUBLIC_REALTIME_URL}/ws/whiteboard/${whiteboardId}/publish`, + ); + wsRef.current = ws; + + ws.onopen = () => { + console.log("connected to publishing channel"); + }; + + ws.onclose = () => { + console.warn("WebSocket closed. Attempting to reconnect..."); + if (shouldReconnect) { + retryTimeout.current = setTimeout(connect, 2000); + } + }; + }; + + connect(); + + return () => { + shouldReconnect = false; + if (retryTimeout.current) { + clearTimeout(retryTimeout.current); + } + wsRef.current?.close(); + }; + }, []); + + return useCallback((message: string) => { + if (wsRef.current?.readyState === WebSocket.OPEN) { + wsRef.current.send(message); + } + }, []); +}; diff --git a/client/src/hooks/api/whiteboard.restore.state.api.ts b/client/src/hooks/api/whiteboard.restore.state.api.ts new file mode 100644 index 00000000..a8b92539 --- /dev/null +++ b/client/src/hooks/api/whiteboard.restore.state.api.ts @@ -0,0 +1,159 @@ +import { useQuery } from "@tanstack/react-query"; +import { Node, Edge, useReactFlow } from "@xyflow/react"; +import { edgeApiFactory, nodeApiFactory, viewportFactory } from "@/api"; +import { + defaultShapeNodeProperties, + NodeProperties, +} from "@/types/NodeProperties"; +import shapeRegistry from "@/util/shapeRegistry"; +import { useEffect } from "react"; + +interface UseRestoreWhiteboardProps { + whiteboardId: number; + enabled?: boolean; +} + +export function useRestoreWhiteboard({ + whiteboardId, + enabled = true, +}: UseRestoreWhiteboardProps) { + const { setNodes, setEdges, setViewport } = useReactFlow(); + + const result = useQuery({ + queryKey: ["restoreWhiteboard", whiteboardId], + queryFn: async () => { + const [nodesResponse, edgesResponse, viewportResponse] = + await Promise.all([ + nodeApiFactory.getAllByWhiteboardId(whiteboardId), + edgeApiFactory.getEdgesByWhiteboard(whiteboardId), + viewportFactory + .getViewportByWhiteboardId(whiteboardId) + .catch(() => ({ data: null })), + ]); + + return { + backendNodes: nodesResponse.data, + backendEdges: edgesResponse.data, + viewportDto: viewportResponse.data, + }; + }, + enabled, + }); + + const { data, isLoading, error } = result; + + useEffect(() => { + if (data && !isLoading) { + const { backendNodes, backendEdges, viewportDto } = data; + + const reactFlowNodes: Node[] = backendNodes.map((backendNode) => { + let nodeType = backendNode.type; + let shapeType = ""; + + if (nodeType && nodeType.startsWith("shapeNode:")) { + const parts = nodeType.split(":"); + nodeType = parts[0]; + shapeType = parts[1]; + } else if (nodeType !== "TextNode") { + shapeType = "textNode"; + } + + const nodeProperties: NodeProperties = { + color: backendNode.color || defaultShapeNodeProperties.color, + borderColor: + backendNode.borderColor || defaultShapeNodeProperties.borderColor, + borderWidth: + backendNode.borderWidth || defaultShapeNodeProperties.borderWidth, + borderOpacity: + backendNode.borderOpacity || + defaultShapeNodeProperties.borderOpacity, + opacity: backendNode.opacity || defaultShapeNodeProperties.opacity, + textColor: + backendNode.textColor || defaultShapeNodeProperties.textColor, + fontSize: backendNode.fontSize || defaultShapeNodeProperties.fontSize, + fontFamily: + backendNode.fontFamily || defaultShapeNodeProperties.fontFamily, + isBold: backendNode.bold || defaultShapeNodeProperties.isBold, + isItalic: backendNode.italic || defaultShapeNodeProperties.isItalic, + isStrikethrough: + backendNode.strikethrough || + defaultShapeNodeProperties.isStrikethrough, + isUnderline: + backendNode.underline || defaultShapeNodeProperties.isUnderline, + }; + + const baseNode = { + id: backendNode.id!, + type: nodeType, + position: { + x: backendNode.positionX || Math.random() * 300, + y: backendNode.positionY || Math.random() * 300, + }, + width: backendNode.width, + height: backendNode.height, + }; + + if (nodeType === "shapeNode") { + const Shape = shapeRegistry({ shapeType: shapeType }); + + return { + ...baseNode, + data: { + label: backendNode.label || "", + shapeType, + Shape, + nodeProperties, + }, + }; + } else { + return { + ...baseNode, + data: { + label: backendNode.label || "", + nodeProperties, + }, + }; + } + }); + + const reactFlowEdges: Edge[] = backendEdges + .filter((edge) => edge.id && edge.source && edge.target) + .map((backendEdge) => ({ + id: backendEdge.id!, + source: backendEdge.source!, + sourceHandle: backendEdge.sourceHandle ?? null, + target: backendEdge.target!, + targetHandle: backendEdge.targetHandle ?? null, + })); + + setNodes(reactFlowNodes); + setEdges(reactFlowEdges); + + setTimeout(() => { + if (viewportDto && viewportDto.x && viewportDto.y && viewportDto.zoom) { + setViewport( + { + x: viewportDto.x, + y: viewportDto.y, + zoom: viewportDto.zoom, + }, + { duration: 800 }, + ); + } + }, 0); + } + }, [data, isLoading, setNodes, setEdges, setViewport]); + + return { + isLoading, + error, + refetch: result.refetch, + data: result.data + ? { + nodes: result.data.backendNodes, + edges: result.data.backendEdges, + viewport: result.data.viewportDto, + } + : null, + }; +} diff --git a/client/src/hooks/api/whiteboard.save.state.api.ts b/client/src/hooks/api/whiteboard.save.state.api.ts new file mode 100644 index 00000000..ca8840d1 --- /dev/null +++ b/client/src/hooks/api/whiteboard.save.state.api.ts @@ -0,0 +1,111 @@ +import { useState, useCallback } from "react"; +import { + SaveWhiteboardStateRequest, + Node as BackendNode, + Edge as BackendEdge, + ViewportResponse, +} from "@/api/main/generated/api"; +import { whiteboardApiFactory } from "@/api"; + +import { Node, Edge } from "@xyflow/react"; +import { NodeProperties } from "@/types/NodeProperties"; + +interface UseSaveWhiteboardStateProps { + whiteboardId: number; + nodes: Node[]; + edges: Edge[]; + viewport: { x: number; y: number; zoom: number }; +} + +export function useSaveWhiteboardState({ + whiteboardId, + nodes, + edges, + viewport, +}: UseSaveWhiteboardStateProps) { + const [isSaving, setIsSaving] = useState(false); + const [error, setError] = useState(null); + + const saveWhiteboardState = useCallback(async () => { + setIsSaving(true); + setError(null); + + try { + const mappedNodes: BackendNode[] = nodes.map((node) => { + const { data } = node; + const nodeProperties = data.nodeProperties as NodeProperties; + + const nodeType = + node.type === "shapeNode" && data.shapeType + ? `${node.type}:${data.shapeType}` + : node.type || ""; + + const label = + "label" in data + ? typeof data.label === "string" + ? data.label + : String(data.label || "") + : ""; + + return { + id: node.id, + whiteboardId: whiteboardId, + type: nodeType, + positionX: node.position.x, + positionY: node.position.y, + label: label, + width: node.width || node.measured?.width || 0, + height: node.height || node.measured?.height || 0, + color: nodeProperties.color || "", + borderColor: nodeProperties.borderColor || "", + borderWidth: nodeProperties.borderWidth || 0, + borderOpacity: nodeProperties.borderOpacity || 1, + opacity: nodeProperties.opacity || 1, + textColor: nodeProperties.textColor || "", + fontSize: nodeProperties.fontSize || 16, + fontFamily: nodeProperties.fontFamily || "", + isBold: nodeProperties.isBold || false, + isItalic: nodeProperties.isItalic || false, + isStrikethrough: nodeProperties.isStrikethrough || false, + isUnderline: nodeProperties.isUnderline || false, + }; + }); + + const mappedEdges: BackendEdge[] = edges.map((edge) => ({ + id: edge.id || "", + whiteboardId: whiteboardId, + source: edge.source, + sourceHandle: edge.sourceHandle || undefined, + target: edge.target, + targetHandle: edge.targetHandle || undefined, + })); + + const viewportResponse: ViewportResponse = { + x: viewport.x, + y: viewport.y, + zoom: viewport.zoom, + }; + + const whiteboardStateDto: SaveWhiteboardStateRequest = { + nodes: mappedNodes, + edges: mappedEdges, + viewportResponse, + }; + + const response = await whiteboardApiFactory.saveWhiteboardState( + whiteboardId, + whiteboardStateDto, + ); + + setIsSaving(false); + return response; + } catch (err) { + const error = err instanceof Error ? err : new Error(String(err)); + setError(error); + setIsSaving(false); + throw error; + } + }, [whiteboardId, nodes, edges, viewport]); + + return { saveWhiteboardState, isSaving, error }; +} diff --git a/client/src/hooks/useFilteredWhiteboards.ts b/client/src/hooks/useFilteredWhiteboards.ts new file mode 100644 index 00000000..42b90883 --- /dev/null +++ b/client/src/hooks/useFilteredWhiteboards.ts @@ -0,0 +1,31 @@ +import { useState, useMemo } from "react"; +import { WhiteboardResponse } from "@/api/main/generated/api"; +import { FilterOption } from "@/types/FilterType"; +import { useGetMe } from "@/hooks/api/account.api"; + +export function useFilteredWhiteboards(whiteboards: WhiteboardResponse[]) { + const [filterBy, setFilterBy] = useState("all-boards"); + const getCurrentUser = useGetMe(); + + const filteredWhiteboards = useMemo(() => { + switch (filterBy) { + case "my-boards": + return whiteboards.filter( + (board) => board.user?.id === getCurrentUser.data?.id, + ); + case "shared": + return whiteboards.filter( + (board) => board.user?.id != getCurrentUser.data?.id, + ); + case "all-boards": + default: + return whiteboards; + } + }, [whiteboards, filterBy, getCurrentUser.data]); + + return { + filteredWhiteboards, + filterBy, + setFilterBy, + }; +} diff --git a/client/src/hooks/useInterval.ts b/client/src/hooks/useInterval.ts new file mode 100644 index 00000000..17a0b23a --- /dev/null +++ b/client/src/hooks/useInterval.ts @@ -0,0 +1,16 @@ +"use client"; + +import { useEffect } from "react"; + +export default function useInterval( + callback: () => void, + delay: number, + isEnabled?: boolean, +) { + useEffect(() => { + if (!isEnabled) return; + const intervalId = setInterval(callback, delay); + + return () => clearInterval(intervalId); + }, [callback, isEnabled, delay]); +} diff --git a/client/src/hooks/useSortedWhiteboards.ts b/client/src/hooks/useSortedWhiteboards.ts new file mode 100644 index 00000000..cf171b7d --- /dev/null +++ b/client/src/hooks/useSortedWhiteboards.ts @@ -0,0 +1,46 @@ +import { useState, useMemo } from "react"; +import { WhiteboardResponse } from "@/api/main/generated/api"; +import { SortOption } from "@/types/SortingType"; + +export function useSortedWhiteboards(whiteboards: WhiteboardResponse[]) { + const [sortBy, setSortBy] = useState("last-edited"); + + const sortedWhiteboards = useMemo(() => { + const boards = [...whiteboards]; + + switch (sortBy) { + case "name": + return boards.sort((a, b) => { + if (!a.title) return 1; + if (!b.title) return -1; + return a.title.localeCompare(b.title); + }); + + case "created": + return boards.sort((a, b) => { + if (!a.createdAt) return 1; + if (!b.createdAt) return -1; + return ( + new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime() + ); + }); + + case "last-edited": + default: + return boards.sort((a, b) => { + if (!a.lastUpdatedAt) return 1; + if (!b.lastUpdatedAt) return -1; + return ( + new Date(b.lastUpdatedAt).getTime() - + new Date(a.lastUpdatedAt).getTime() + ); + }); + } + }, [whiteboards, sortBy]); + + return { + sortedWhiteboards, + sortBy, + setSortBy, + }; +} diff --git a/client/src/types/FilterType.ts b/client/src/types/FilterType.ts new file mode 100644 index 00000000..a37cd1fa --- /dev/null +++ b/client/src/types/FilterType.ts @@ -0,0 +1,12 @@ +export type FilterOption = "all-boards" | "my-boards" | "shared"; + +export interface SortConfig { + key: FilterOption; + label: string; +} + +export const SORT_OPTIONS: SortConfig[] = [ + { key: "all-boards", label: "All boards" }, + { key: "my-boards", label: "My boards" }, + { key: "shared", label: "Shared with me" }, +]; diff --git a/client/src/types/NodeProperties.ts b/client/src/types/NodeProperties.ts new file mode 100644 index 00000000..5909b675 --- /dev/null +++ b/client/src/types/NodeProperties.ts @@ -0,0 +1,107 @@ +export interface NodeProperties { + color: string; + borderColor: string; + borderWidth?: number; + borderOpacity?: number; + opacity?: number; + textColor: string; + fontSize: number; + fontFamily: string; + isBold: boolean; + isItalic: boolean; + isStrikethrough: boolean; + isUnderline: boolean; +} + +export const fontSizes = [10, 12, 14, 18, 24, 36, 48, 64, 80, 144, 288]; + +export const predefinedColors = [ + "#FFFFFF", + "#FFF9C4", + "#FFCC80", + "#FFAB91", + "#C8E6C9", + "#B3E5FC", + "#D1C4E9", + "#FFF176", + "#FFAB40", + "#FF7043", + "#4CAF50", + "#2196F3", + "#9575CD", + "#8D6E63", + "#5D4037", + "#B71C1C", + "#1B5E20", + "#EEEEEE", + "#6C0F0F", + "#000000", +]; + +export const allFonts: { [key: string]: string } = { + "Abril Fatface": "'Abril Fatface', serif", + Arial: "Arial, sans-serif", + Caveat: "'Caveat', cursive", + "EB Garamond": "'EB Garamond', serif", + Georgia: "Georgia, serif", + Helvetica: "Helvetica, sans-serif", + Inter: "'Inter', sans-serif", + Lato: "'Lato', sans-serif", + Montserrat: "'Montserrat', sans-serif", + "Open Sans": "'Open Sans', sans-serif", + "Playfair Display": "'Playfair Display', serif", + Poppins: "'Poppins', sans-serif", + Roboto: "'Roboto', sans-serif", + "Times New Roman": "'Times New Roman', Times, serif", +}; + +export const getFontStyle = (fontName: string) => { + return allFonts[fontName] || "sans-serif"; +}; + +export const defaultShapeNodeProperties: NodeProperties = { + color: "#FFCC80", + borderColor: "#FFAB40", + borderWidth: 3, + borderOpacity: 1, + opacity: 1, + textColor: "#4A4A4A", + fontSize: 16, + fontFamily: "Arial", + isBold: false, + isItalic: false, + isStrikethrough: false, + isUnderline: false, +}; +export const defaultTextNodeProperties: NodeProperties = { + color: "none", + borderColor: "none", + borderWidth: 3, + borderOpacity: 1, + opacity: 1, + textColor: "#000000", + fontSize: 16, + fontFamily: "Arial", + isBold: false, + isItalic: false, + isStrikethrough: false, + isUnderline: false, +}; + +export const checkerboardStyle = { + background: ` + linear-gradient(45deg, #ccc 25%, transparent 25%), + linear-gradient(-45deg, #ccc 25%, transparent 25%), + linear-gradient(45deg, transparent 75%, #ccc 75%), + linear-gradient(-45deg, transparent 75%, #ccc 75%) + `, + backgroundSize: "6px 6px", + backgroundPosition: "0 0, 0 3px, 3px -3px, -3px 0px", +}; + +export const handleStyle = { + width: 2, + height: 2, + background: "#ffffff", + border: "1px solid gray", +}; diff --git a/client/src/types/SortingType.ts b/client/src/types/SortingType.ts new file mode 100644 index 00000000..c6e057cf --- /dev/null +++ b/client/src/types/SortingType.ts @@ -0,0 +1,12 @@ +export type SortOption = "last-edited" | "name" | "created"; + +export interface SortConfig { + key: SortOption; + label: string; +} + +export const SORT_OPTIONS: SortConfig[] = [ + { key: "last-edited", label: "Last edited" }, + { key: "name", label: "Name" }, + { key: "created", label: "Created" }, +]; diff --git a/client/src/util/formatDate.ts b/client/src/util/formatDate.ts new file mode 100644 index 00000000..9b0295ac --- /dev/null +++ b/client/src/util/formatDate.ts @@ -0,0 +1,11 @@ +export default function formatDate(dateString: string) { + const date = new Date(dateString); + + return new Intl.DateTimeFormat("en-US", { + month: "long", + day: "numeric", + hour: "numeric", + minute: "2-digit", + hour12: true, + }).format(date); +} diff --git a/client/src/util/generateUserUniqueColor.ts b/client/src/util/generateUserUniqueColor.ts new file mode 100644 index 00000000..d8998988 --- /dev/null +++ b/client/src/util/generateUserUniqueColor.ts @@ -0,0 +1,22 @@ +// edit: deterministic way to calculate color for user +function getHashOfString(str: string): number { + let hash = 0; + for (let i = 0; i < str.length; i++) { + hash = str.charCodeAt(i) + ((hash << 5) - hash); + } + return Math.abs(hash); +} + +function normalizeHash(hash: number, min: number, max: number): number { + return Math.floor((hash % (max - min)) + min); +} + +export default function generateColorFromString(text: string): string { + const hash = getHashOfString(text); + + const hue = normalizeHash(hash, 0, 360); + const saturation = normalizeHash(hash, 50, 75); + const lightness = normalizeHash(hash, 25, 60); + + return `hsl(${hue}, ${saturation}%, ${lightness}%)`; +} diff --git a/client/src/util/generateWhiteboardName.ts b/client/src/util/generateWhiteboardName.ts new file mode 100644 index 00000000..0e6136e5 --- /dev/null +++ b/client/src/util/generateWhiteboardName.ts @@ -0,0 +1,46 @@ +const nameGenerators = { + project: { + adjectives: [ + "Active", + "Agile", + "Bold", + "Core", + "Prime", + "Smart", + "Strategic", + ], + nouns: [ + "Project", + "Sprint", + "Plan", + "Vision", + "Mission", + "Strategy", + "Goal", + ], + }, + creative: { + adjectives: ["Creative", "Artistic", "Wild", "Free", "Inspired", "Vibrant"], + nouns: ["Canvas", "Studio", "Design", "Sketch", "Draft", "Artwork"], + }, + tech: { + adjectives: ["Digital", "Tech", "Cloud", "Cyber", "Smart", "Virtual"], + nouns: ["Hub", "Lab", "Space", "Zone", "Platform", "System"], + }, + nature: { + adjectives: ["Green", "Blue", "Crystal", "Forest", "Ocean", "Mountain"], + nouns: ["Garden", "Valley", "Peak", "Stream", "Field", "Path"], + }, +}; + +export default function generateWhiteboardName(): string { + const selectedCategory = Object.keys(nameGenerators)[ + Math.floor(Math.random() * Object.keys(nameGenerators).length) + ] as keyof typeof nameGenerators; + + const { adjectives, nouns } = nameGenerators[selectedCategory]; + const adjective = adjectives[Math.floor(Math.random() * adjectives.length)]; + const noun = nouns[Math.floor(Math.random() * nouns.length)]; + + return `${adjective} ${noun}`; +} diff --git a/client/src/util/shapeRegistry.tsx b/client/src/util/shapeRegistry.tsx new file mode 100644 index 00000000..861a74f3 --- /dev/null +++ b/client/src/util/shapeRegistry.tsx @@ -0,0 +1,32 @@ +import Circle from "@/assets/shapes/circle.svg"; +import Rectangle from "@/assets/shapes/rectangle.svg"; +import Trapezoid from "@/assets/shapes/trapezoid.svg"; +import Diamond from "@/assets/shapes/diamond.svg"; +import Hexagon from "@/assets/shapes/hexagon.svg"; +import Parallelogram from "@/assets/shapes/parallelogram.svg"; +import Triangle from "@/assets/shapes/triangle.svg"; + +interface ShapeRegistryParams { + shapeType: string; +} + +export default function shapeRegistry({ shapeType }: ShapeRegistryParams) { + switch (shapeType.toLowerCase()) { + case "circle": + return Circle; + case "rectangle": + return Rectangle; + case "trapezoid": + return Trapezoid; + case "diamond": + return Diamond; + case "hexagon": + return Hexagon; + case "parallelogram": + return Parallelogram; + case "triangle": + return Triangle; + default: + return Circle; + } +} diff --git a/client/src/util/updateNode.ts b/client/src/util/updateNode.ts new file mode 100644 index 00000000..03ff867e --- /dev/null +++ b/client/src/util/updateNode.ts @@ -0,0 +1,31 @@ +import { Node } from "@xyflow/react"; +import { NodeProperties } from "@/types/NodeProperties"; + +type UpdateNodeFn = ( + id: string, + updater: Partial<{ + label: string; + nodeProperties: Partial; + }>, +) => (nodes: Node[]) => Node[]; + +export const updateNode: UpdateNodeFn = (id, updater) => (nodes) => { + return nodes.map((node) => { + if (node.id === id) { + return { + ...node, + data: { + ...node.data, + ...(updater.label !== undefined && { label: updater.label }), + ...(updater.nodeProperties && { + nodeProperties: { + ...(node.data.nodeProperties || {}), + ...updater.nodeProperties, + }, + }), + }, + }; + } + return node; + }); +}; diff --git a/client/svgr.d.ts b/client/svgr.d.ts new file mode 100644 index 00000000..080ecc85 --- /dev/null +++ b/client/svgr.d.ts @@ -0,0 +1,5 @@ +declare module '*.svg' { + import React from 'react'; + const SVG: React.FC>; + export default SVG; +} \ No newline at end of file diff --git a/client/tailwind.config.ts b/client/tailwind.config.ts new file mode 100644 index 00000000..414c20c7 --- /dev/null +++ b/client/tailwind.config.ts @@ -0,0 +1,36 @@ +import {Config} from 'tailwindcss'; + +const config: Config = { + content: [ + './src/pages/**/*.{js,ts,jsx,tsx}', // Scan pages in the src/pages folder + './src/components/**/*.{js,ts,jsx,tsx}', // Scan components in the src/components folder + './src/app/**/*.{js,ts,jsx,tsx}', // Scan app directory (if using App Router) + './src/types/**/*.{ts}', + ], + theme: { + extend: { + fontSize: { + 'xxs': '0.625rem', // 10px + }, + fontFamily: { + caveat: ['Caveat', 'cursive'], + fredoka: ['Fredoka One', 'cursive'], + graduate: ['Graduate', 'cursive'], + gravitas: ['Gravitas One', 'cursive'], + playfair: ['Playfair Display', 'serif'], + poppins: ['Poppins', 'sans-serif'], + georgia: ['Georgia', 'serif'], + helvetica: ['Helvetica', 'sans-serif'], + }, + animation: { + 'spin-slow': 'spin 5s linear infinite', // Slower spinning animation + 'spin-medium': 'spin 2s linear infinite', // Medium speed + 'spin-fast': 'spin 1s linear infinite', // Fast spinning + }, + }, + }, + + plugins: [], +}; + +export default config; diff --git a/client/tsconfig.json b/client/tsconfig.json index c1334095..31534d20 100644 --- a/client/tsconfig.json +++ b/client/tsconfig.json @@ -22,6 +22,6 @@ "@/*": ["./src/*"] } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "include": ["svgr.d.ts", "next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "exclude": ["node_modules"] } diff --git a/compose.aws.yml b/compose.aws.yml index 5c570946..b0024f2e 100644 --- a/compose.aws.yml +++ b/compose.aws.yml @@ -44,7 +44,8 @@ services: client: image: ghcr.io/aet-devops25/team-server-down/client:latest environment: - - PUBLIC_API_URL=${PUBLIC_API_URL} + - NEXT_PUBLIC_API_URL=${PUBLIC_API_URL} + - NEXT_PUBLIC_GENAI_URL=${PUBLIC_GENAI_URL} depends_on: - server restart: unless-stopped @@ -72,12 +73,13 @@ services: ports: - "5432:5432" volumes: - - db-data:/var/lib/postgresql/data + - /mnt/ebs/db-data:/var/lib/postgresql/data networks: - server volumes: db-data: + networks: server: diff --git a/compose.yml b/compose.yml new file mode 100644 index 00000000..24e3ed4c --- /dev/null +++ b/compose.yml @@ -0,0 +1,251 @@ +name: team-server-down + +services: + mailhog: + restart: always + build: + context: ./docker/mailhog + dockerfile: Dockerfile + ports: + - "1025:1025" + - "8025:8025" + networks: + - server + + keycloak: + restart: always + build: + context: ./docker/keycloak + dockerfile: Dockerfile + ports: + - "8080:8080" + command: + - start-dev + - --import-realm + - -Dkeycloak.migration.strategy=OVERWRITE_EXISTING + - --transaction-xa-enabled=false + depends_on: + db: + condition: service_healthy + environment: + KC_DB: postgres + KC_DB_URL: jdbc:postgresql://db:5432/keycloak + KC_DB_USERNAME: root + KC_DB_PASSWORD: password + KC_HOSTNAME: localhost + KEYCLOAK_ADMIN: admin + KEYCLOAK_ADMIN_PASSWORD: password + KC_HTTP_ENABLED: true + KC_HOSTNAME_STRICT: false + KC_HEALTH_ENABLED: true + volumes: + - ./docker/keycloak/realm-export.json:/opt/keycloak/data/import/realm-export.json:ro + networks: + - server + + server: + restart: always + build: + context: ./server + dockerfile: Dockerfile + target: development + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:9091/"] + interval: 10s + timeout: 3s + environment: + DB_HOST: ${DB_HOST:-db} + DB_PORT: ${DB_PORT:-5432} + DB_NAME: ${DB_NAME:-main} + DB_USER: ${DB_USER:-root} + DB_PASSWORD: ${DB_PASSWORD:-password} + ALLOWED_ORIGIN: ${ALLOWED_ORIGIN:-http://localhost:3000} + IDP_INTERNAL_URI: ${IDP_INTERNAL_URI:-http://keycloak:8080/realms/development} + IDP_EXTERNAL_URI: ${IDP_EXTERNAL_URI:-http://localhost:8080/realms/development} + SERVER_URL: ${SERVER_URL:-http://localhost:9091} + depends_on: + db: + condition: service_healthy + ports: + - "9091:9091" + volumes: + - ./server:/app + networks: + - server + + realtime: + restart: always + build: + context: ./realtime + dockerfile: Dockerfile.local + environment: + REDIS_HOST: ${REDIS_HOST:-redis} + REDIS_PORT: ${REDIS_PORT:-6379} + ports: + - "9090:9090" + volumes: + - ./realtime:/app + networks: + - server + + client: + restart: always + build: + context: ./client + dockerfile: Dockerfile.local + entrypoint: npm run dev + volumes: + - ./client:/app + - node-modules-client:/app/node_modules + healthcheck: + test: "curl -f http://localhost:3000 || exit 1" + interval: 5s + environment: + NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-http://localhost:9091} + NEXT_PUBLIC_BASE_URL: ${NEXT_PUBLIC_API_URL:-http://localhost:3000} + NEXT_PUBLIC_GENAI_URL: ${NEXT_PUBLIC_GENAI_URL:-http://localhost:8000} + NEXT_PUBLIC_REALTIME_URL: ${NEXT_PUBLIC_REALTIME_URL:-ws://localhost:9090} + NEXTAUTH_URL: ${NEXTAUTH_URL:-http://localhost:3000/api/auth/} + NEXTAUTH_SECRET: ${NEXTAUTH_SECRET:-feZJWB3mcQ93VBmqHKQI5er5NEIxcDPb3wtT/KaLB9s=} + KEYCLOAK_CLIENT_ID: ${KEYCLOAK_CLIENT_ID:-webclient} + KEYCLOAK_ISSUER: ${KEYCLOAK_ISSUER:-http://keycloak:8080/realms/development} + KEYCLOAK_END_SESSION_ENDPOINT: ${KEYCLOAK_END_SESSION_ENDPOINT:-http://keycloak:8080/realms/development/protocol/openid-connect/logout} + KEYCLOAK_CLIENT_SECRET: ${KEYCLOAK_CLIENT_SECRET:-SXiMvr1GG10bk2J63ODZC9SOaoAZ4dbe} + ports: + - "3000:3000" + networks: + - server + + db: + restart: always + build: + context: ./docker/postgresql + dockerfile: Dockerfile + environment: + POSTGRES_USER: ${POSTGRES_USER:-root} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-password} + healthcheck: + test: [ "CMD-SHELL", "sh -c 'pg_isready -U root -d main'" ] + interval: 10s + timeout: 3s + retries: 3 + ports: + - "5432:5432" + volumes: + - db-data:/var/lib/postgresql/data + networks: + - server + + redis: + build: + context: ./docker/redis + dockerfile: Dockerfile + healthcheck: + test: [ "CMD-SHELL", "redis-cli ping | grep PONG" ] + interval: 1s + timeout: 3s + retries: 5 + ports: + - "6379:6379" + command: + - redis-server + volumes: + - redis-data:/data + networks: + - server + + redisinsight: + image: redis/redisinsight:latest + ports: + - "5540:5540" + volumes: + - redis-insight-data:/data + networks: + - server + + genai: + restart: always + build: + context: ./genai + dockerfile: Dockerfile + ports: + - "8000:8000" + environment: + OPEN_WEB_UI_API_KEY: ${OPEN_WEB_UI_API_KEY} + SERVER_URL: ${SERVER_URL:-http://localhost:9091} + CLIENT_URL: ${CLIENT_URL:-http://localhost:3000} + GENAI_URL: ${GENAI_URL:-http://localhost:8000} + API_URL: ${API_URL:-https://gpu.aet.cit.tum.de/api/chat/completions} + volumes: + - ./genai:/app + networks: + - server + + grafana: + restart: always + build: + context: ./docker/grafana + dockerfile: Dockerfile + ports: + - "3001:3000" + volumes: + - grafana-data:/var/lib/grafana + - ./docker/grafana/provisioning:/etc/grafana/provisioning:ro + environment: + - GF_SECURITY_ADMIN_USER=admin + - GF_SECURITY_ADMIN_PASSWORD=admin + - GF_USERS_ALLOW_SIGN_UP=false + - GF_FEATURE_TOGGLES_ENABLE=logsInExplore + - GF_LOG_CONSOLECOLORS=true + depends_on: + - prometheus + networks: + - server + + prometheus: + restart: always + build: + context: ./docker/prometheus + dockerfile: Dockerfile + ports: + - "9092:9090" + volumes: + - ./docker/prometheus/config:/etc/prometheus + - prometheus-data:/prometheus + command: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--storage.tsdb.path=/prometheus' + - '--web.console.libraries=/etc/prometheus/console_libraries' + - '--web.console.templates=/etc/prometheus/consoles' + - '--web.enable-lifecycle' + networks: + - server + + alertmanager: + restart: always + build: + context: ./docker/alertmanager + dockerfile: Dockerfile + ports: + - "9093:9093" + volumes: + - ./docker/alertmanager/config.yml:/etc/alertmanager/config.yml:ro + - alertmanager-data:/alertmanager + command: + - '--config.file=/etc/alertmanager/config.yml' + - '--storage.path=/alertmanager' + - '--log.level=debug' + networks: + - server + +networks: + server: + +volumes: + db-data: + node-modules-client: + redis-data: + redis-insight-data: + grafana-data: + prometheus-data: + alertmanager-data: \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 82f6738b..00000000 --- a/docker-compose.yml +++ /dev/null @@ -1,153 +0,0 @@ -name: team-server-down - -services: - nginx: - restart: always - image: nginx:stable-alpine - healthcheck: - test: "curl -f http://localhost:80 || exit 1" - interval: 5s - volumes: - - ./docker/nginx/conf.d:/etc/nginx/conf.d:ro - - ./docker/nginx/cert.crt:/etc/nginx/cert.crt:ro - - ./docker/nginx/private.key:/etc/nginx/private.key:ro - depends_on: - - server - ports: - - "80:80" - - "443:443" - networks: - - server - - server: - restart: always - build: - context: ./server - dockerfile: Dockerfile - target: development - healthcheck: - test: ["CMD", "curl", "-f", "http://server:8080/"] - interval: 10s - timeout: 3s - environment: - DB_HOST: ${DB_HOST:-db} - DB_PORT: ${DB_PORT:-5432} - DB_NAME: ${DB_NAME:-postgres} - DB_USER: ${DB_USER:-postgres} - DB_PASSWORD: ${DB_PASSWORD:-postgres} - depends_on: - db: - condition: service_healthy - ports: - - "8080:8080" - volumes: - - ./server:/app - networks: - - server - - client: - restart: always - build: - context: ./client - dockerfile: Dockerfile - target: deps - entrypoint: npm run dev - volumes: - - ./client:/app - - node-modules-client:/app/node_modules - healthcheck: - test: "curl -f http://localhost:3000 || exit 1" - interval: 5s - ports: - - "3000:3000" - networks: - - server - - db: - restart: always - build: - context: ./docker/postgresql - dockerfile: Dockerfile - environment: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - healthcheck: - test: [ "CMD-SHELL", "sh -c 'pg_isready -U postgres -d postgres'" ] - interval: 10s - timeout: 3s - retries: 3 - ports: - - "5432:5432" - volumes: - - db-data:/var/lib/postgresql/data - networks: - - server - - genai: - restart: always - build: - context: ./genai - dockerfile: Dockerfile - ports: - - "8000:8000" - environment: - DB_HOST: ${DB_HOST:-weaviate} - DB_PORT: ${DB_PORT:-9090} - DB_GRPC_PORT: ${DB_GRPC_PORT:-50051} - depends_on: - - weaviate - volumes: - - ./genai:/app - networks: - - server - - rag - - weaviate: - command: [ "--host", "0.0.0.0", "--port", "9090", "--scheme", "http"] - image: cr.weaviate.io/semitechnologies/weaviate:1.30.0 - ports: - - "9090:9090" - - "50051:50051" - volumes: - - weaviate-data:/var/lib/weaviate - restart: on-failure:0 - environment: - TRANSFORMERS_INFERENCE_API: 'http://t2v-transformers:8080' - IMAGE_INFERENCE_API: 'http://i2v-neural:8080' - SPELLCHECK_INFERENCE_API: 'http://text-spellcheck:8080' - QUERY_DEFAULTS_LIMIT: 25 - AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: 'true' - PERSISTENCE_DATA_PATH: '/var/lib/weaviate' - DEFAULT_VECTORIZER_MODULE: 'text2vec-transformers' - ENABLE_MODULES: 'text2vec-transformers,text-spellcheck,img2vec-neural' - CLUSTER_HOSTNAME: 'node1' - networks: - - rag - - t2v-transformers: - image: cr.weaviate.io/semitechnologies/transformers-inference:sentence-transformers-multi-qa-MiniLM-L6-cos-v1 - environment: - ENABLE_CUDA: '0' - networks: - - rag - - text-spellcheck: - image: cr.weaviate.io/semitechnologies/text-spellcheck-model:pyspellchecker-en - networks: - - rag - - i2v-neural: - image: cr.weaviate.io/semitechnologies/img2vec-pytorch:resnet50 - environment: - ENABLE_CUDA: '0' - networks: - - rag - -networks: - server: - rag: - -volumes: - db-data: - node-modules-client: - weaviate-data: \ No newline at end of file diff --git a/docker/alertmanager/Dockerfile b/docker/alertmanager/Dockerfile new file mode 100644 index 00000000..55f59f7a --- /dev/null +++ b/docker/alertmanager/Dockerfile @@ -0,0 +1 @@ +FROM prom/alertmanager:v0.26.0 \ No newline at end of file diff --git a/docker/alertmanager/config.yml b/docker/alertmanager/config.yml new file mode 100644 index 00000000..32b599f1 --- /dev/null +++ b/docker/alertmanager/config.yml @@ -0,0 +1,17 @@ +global: + smtp_smarthost: 'mailhog:1025' + smtp_from: 'alertmanager@whiteboard.student.k8s.aet.cit.tum.de' + smtp_require_tls: false +route: + receiver: 'mailhog-alerts' + group_by: [ 'alertname' ] + group_wait: 10s + group_interval: 1m + repeat_interval: 30m +receivers: + - name: 'mailhog-alerts' + email_configs: + - to: 'teamserverdown@whiteboard.student.k8s.aet.cit.tum.de' + from: 'alertmanager@whiteboard.student.k8s.aet.cit.tum.de' + smarthost: 'mailhog:1025' + send_resolved: true \ No newline at end of file diff --git a/docker/grafana/Dockerfile b/docker/grafana/Dockerfile new file mode 100644 index 00000000..e48a79f8 --- /dev/null +++ b/docker/grafana/Dockerfile @@ -0,0 +1 @@ +FROM grafana/grafana-oss:12.0.2-ubuntu diff --git a/docker/grafana/provisioning/dashboards/dashboards.yml b/docker/grafana/provisioning/dashboards/dashboards.yml new file mode 100644 index 00000000..0e8f7220 --- /dev/null +++ b/docker/grafana/provisioning/dashboards/dashboards.yml @@ -0,0 +1,23 @@ +apiVersion: 1 + +providers: + - name: "Server System Metrics Dashboard" + type: file + editable: true + updateIntervalSeconds: 10 + options: + path: /etc/grafana/provisioning/dashboards/server-dashboard.json + + - name: "GenAi System Metrics Dashboard" + type: file + editable: true + updateIntervalSeconds: 10 + options: + path: /etc/grafana/provisioning/dashboards/genai-dashboard.json + + - name: "Realtime System Metrics Dashboard" + type: file + editable: true + updateIntervalSeconds: 10 + options: + path: /etc/grafana/provisioning/dashboards/realtime-dashboard.json \ No newline at end of file diff --git a/docker/grafana/provisioning/dashboards/genai-dashboard.json b/docker/grafana/provisioning/dashboards/genai-dashboard.json new file mode 100644 index 00000000..bc3a1fc6 --- /dev/null +++ b/docker/grafana/provisioning/dashboards/genai-dashboard.json @@ -0,0 +1,522 @@ +{ + "annotations":{ + "list":[ + { + "builtIn":1, + "datasource":{ + "type":"grafana", + "uid":"-- Grafana --" + }, + "enable":true, + "hide":true, + "iconColor":"rgba(0, 211, 255, 1)", + "name":"Annotations & Alerts", + "type":"dashboard" + }, + { + "datasource":{ + "type":"prometheus", + "uid":"PBFA97CFB590B2093" + }, + "enable":true, + "hide":false, + "iconColor":"orange", + "name":"Client Error Spike", + "target":{ + "expr":"sum(rate(http_requests_total{status=~\"4xx\"}[5m])) > 0.1", + "interval":"", + "refId":"Anno" + }, + "textFormat":"High rate of client errors detected, exceeding 0.1 requests per second", + "titleFormat":"Client Error Spike" + }, + { + "datasource":{ + "type":"prometheus", + "uid":"PBFA97CFB590B2093" + }, + "enable":true, + "hide":false, + "iconColor":"red", + "name":"Server Error Spike", + "tagKeys":"server, error", + "target":{ + "expr":"sum(rate(http_requests_total{status=~\"5xx\"}[5m])) > 0.1", + "interval":"", + "refId":"Anno" + }, + "textFormat":"Server error rate exceeded threshold (>0.1 req/s)", + "titleFormat":"Server Error Spike" + } + ] + }, + "description":"Dashboard showing system metrics including request count, latency, and error rate.", + "editable":true, + "fiscalYearStartMonth":0, + "graphTooltip":0, + "id":3, + "links":[ + + ], + "panels":[ + { + "datasource":{ + "type":"prometheus", + "uid":"PBFA97CFB590B2093" + }, + "fieldConfig":{ + "defaults":{ + "color":{ + "mode":"palette-classic" + }, + "custom":{ + "axisBorderShow":false, + "axisCenteredZero":false, + "axisColorMode":"text", + "axisLabel":"", + "axisPlacement":"auto", + "barAlignment":0, + "barWidthFactor":0.6, + "drawStyle":"line", + "fillOpacity":20, + "gradientMode":"none", + "hideFrom":{ + "legend":false, + "tooltip":false, + "viz":false + }, + "insertNulls":false, + "lineInterpolation":"smooth", + "lineWidth":2, + "pointSize":5, + "scaleDistribution":{ + "type":"linear" + }, + "showPoints":"auto", + "spanNulls":false, + "stacking":{ + "group":"A", + "mode":"none" + }, + "thresholdsStyle":{ + "mode":"area" + } + }, + "mappings":[ + + ], + "thresholds":{ + "mode":"absolute", + "steps":[ + { + "color":"green" + }, + { + "color":"orange", + "value":70 + }, + { + "color":"red", + "value":80 + } + ] + } + }, + "overrides":[ + + ] + }, + "gridPos":{ + "h":17, + "w":12, + "x":0, + "y":0 + }, + "id":1, + "options":{ + "legend":{ + "calcs":[ + "mean", + "max" + ], + "displayMode":"table", + "placement":"bottom", + "showLegend":true + }, + "tooltip":{ + "hideZeros":false, + "mode":"multi", + "sort":"none" + } + }, + "pluginVersion":"12.0.2", + "targets":[ + { + "datasource":{ + "type":"prometheus", + "uid":"PBFA97CFB590B2093" + }, + "editorMode":"code", + "expr":"sum by (handler, method, status) (increase(http_requests_total{job=\"genai_job\"}[5m]))", + "legendFormat":"{{method}} {{status}} {{uri}}", + "range":true, + "refId":"A" + } + ], + "title":"Request Count", + "type":"timeseries" + }, + { + "datasource":{ + "type":"prometheus", + "uid":"PBFA97CFB590B2093" + }, + "fieldConfig":{ + "defaults":{ + "color":{ + "mode":"palette-classic" + }, + "custom":{ + "axisBorderShow":false, + "axisCenteredZero":false, + "axisColorMode":"text", + "axisLabel":"Average Latency (seconds)", + "axisPlacement":"auto", + "barAlignment":0, + "barWidthFactor":0.6, + "drawStyle":"line", + "fillOpacity":20, + "gradientMode":"none", + "hideFrom":{ + "legend":false, + "tooltip":false, + "viz":false + }, + "insertNulls":false, + "lineInterpolation":"smooth", + "lineWidth":2, + "pointSize":5, + "scaleDistribution":{ + "type":"linear" + }, + "showPoints":"auto", + "spanNulls":false, + "stacking":{ + "group":"A", + "mode":"none" + }, + "thresholdsStyle":{ + "mode":"area" + } + }, + "mappings":[ + + ], + "thresholds":{ + "mode":"absolute", + "steps":[ + { + "color":"green" + }, + { + "color":"red", + "value":80 + } + ] + } + }, + "overrides":[ + + ] + }, + "gridPos":{ + "h":17, + "w":12, + "x":12, + "y":0 + }, + "id":2, + "options":{ + "legend":{ + "calcs":[ + "mean", + "max" + ], + "displayMode":"table", + "placement":"bottom", + "showLegend":true + }, + "tooltip":{ + "hideZeros":false, + "mode":"multi", + "sort":"none" + } + }, + "pluginVersion":"12.0.2", + "targets":[ + { + "datasource":{ + "type":"prometheus", + "uid":"PBFA97CFB590B2093" + }, + "editorMode":"code", + "expr":"sum(rate(http_request_duration_seconds_sum{job=\"genai_job\"}[5m])) by (method, handler) /\nsum(rate(http_request_duration_seconds_count{job=\"genai_job\"}[5m])) by (method, handler)", + "legendFormat":"{{method}} {{handler}}", + "range":true, + "refId":"A" + } + ], + "title":"Latency", + "type":"timeseries" + }, + { + "datasource":{ + "type":"prometheus", + "uid":"PBFA97CFB590B2093" + }, + "fieldConfig":{ + "defaults":{ + "color":{ + "mode":"palette-classic" + }, + "custom":{ + "axisBorderShow":false, + "axisCenteredZero":false, + "axisColorMode":"text", + "axisLabel":"", + "axisPlacement":"auto", + "barAlignment":0, + "barWidthFactor":0.6, + "drawStyle":"line", + "fillOpacity":0, + "gradientMode":"none", + "hideFrom":{ + "legend":false, + "tooltip":false, + "viz":false + }, + "insertNulls":false, + "lineInterpolation":"linear", + "lineWidth":1, + "pointSize":5, + "scaleDistribution":{ + "type":"linear" + }, + "showPoints":"auto", + "spanNulls":false, + "stacking":{ + "group":"A", + "mode":"none" + }, + "thresholdsStyle":{ + "mode":"off" + } + }, + "mappings":[ + + ], + "thresholds":{ + "mode":"absolute", + "steps":[ + { + "color":"green" + }, + { + "color":"red", + "value":80 + } + ] + } + }, + "overrides":[ + + ] + }, + "gridPos":{ + "h":12, + "w":12, + "x":0, + "y":17 + }, + "id":4, + "options":{ + "legend":{ + "calcs":[ + + ], + "displayMode":"list", + "placement":"bottom", + "showLegend":true + }, + "tooltip":{ + "hideZeros":false, + "mode":"single", + "sort":"none" + } + }, + "pluginVersion":"12.0.2", + "targets":[ + { + "editorMode":"code", + "expr":"sum by (handler, method) (increase(http_requests_total{status=~\"4..\", job=\"genai_job\"}[5m]))", + "legendFormat":"{{label_name}}", + "range":true, + "refId":"A" + }, + { + "datasource":{ + "type":"prometheus", + "uid":"PBFA97CFB590B2093" + }, + "editorMode":"code", + "expr":"sum by (handler, method) (increase(http_requests_total{status=~\"4..\", job=\"genai_job\"}[5m]))", + "hide":false, + "instant":false, + "legendFormat":"__auto", + "range":true, + "refId":"B" + } + ], + "title":"Errors", + "type":"timeseries" + }, + { + "datasource":{ + "type":"prometheus", + "uid":"PBFA97CFB590B2093" + }, + "description":"This dashboard displays the average number of input and output tokens generated per request for each operation (completion, summarization, rephrase_text) over time. The values represent the mean input and output token count, calculated every 5 minutes, grouped by operation.", + "fieldConfig":{ + "defaults":{ + "color":{ + "mode":"palette-classic" + }, + "custom":{ + "axisBorderShow":false, + "axisCenteredZero":false, + "axisColorMode":"text", + "axisLabel":"", + "axisPlacement":"auto", + "barAlignment":0, + "barWidthFactor":0.6, + "drawStyle":"line", + "fillOpacity":0, + "gradientMode":"none", + "hideFrom":{ + "legend":false, + "tooltip":false, + "viz":false + }, + "insertNulls":false, + "lineInterpolation":"linear", + "lineWidth":1, + "pointSize":5, + "scaleDistribution":{ + "type":"linear" + }, + "showPoints":"auto", + "spanNulls":false, + "stacking":{ + "group":"A", + "mode":"none" + }, + "thresholdsStyle":{ + "mode":"off" + } + }, + "mappings":[ + + ], + "thresholds":{ + "mode":"absolute", + "steps":[ + { + "color":"green" + }, + { + "color":"red", + "value":80 + } + ] + } + }, + "overrides":[ + + ] + }, + "gridPos":{ + "h":12, + "w":12, + "x":12, + "y":17 + }, + "id":5, + "options":{ + "legend":{ + "calcs":[ + + ], + "displayMode":"list", + "placement":"bottom", + "showLegend":true + }, + "tooltip":{ + "hideZeros":false, + "mode":"single", + "sort":"none" + } + }, + "pluginVersion":"12.0.2", + "targets":[ + { + "editorMode":"code", + "exemplar":false, + "expr":"rate(llm_token_count_sum{type=\"output\"}[5m]) \n/\nrate(llm_token_count_count{type=\"output\"}[5m])", + "format":"time_series", + "instant":false, + "legendFormat":"output - {{operation}}", + "range":true, + "refId":"A" + }, + { + "datasource":{ + "type":"prometheus", + "uid":"PBFA97CFB590B2093" + }, + "editorMode":"code", + "expr":"rate(llm_token_count_sum{type=\"input\"}[5m]) \n/\nrate(llm_token_count_count{type=\"input\"}[5m])", + "hide":false, + "instant":false, + "legendFormat":"input - {{operation}}", + "range":true, + "refId":"B" + } + ], + "title":"LLM API Token Analytics", + "type":"timeseries" + } + ], + "preload":false, + "refresh":"10s", + "schemaVersion":41, + "tags":[ + "monitoring", + "alerts" + ], + "templating":{ + "list":[ + + ] + }, + "time":{ + "from":"now-6h", + "to":"now" + }, + "timepicker":{ + + }, + "timezone":"browser", + "title":"GenAi System Metrics Dashboard", + "uid":"genai-metrics-dashboard1111", + "version":2 +} diff --git a/docker/grafana/provisioning/dashboards/realtime-dashboard.json b/docker/grafana/provisioning/dashboards/realtime-dashboard.json new file mode 100644 index 00000000..fac0edbf --- /dev/null +++ b/docker/grafana/provisioning/dashboards/realtime-dashboard.json @@ -0,0 +1,593 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "enable": true, + "hide": false, + "iconColor": "red", + "name": "WebSocket error", + "tagKeys": "websocket_errors", + "target": { + "expr": "(\n rate(websocket_read_errors[5m]) +\n rate(websocket_write_errors[5m]) +\n rate(websocket_upgrade_errors[5m])\n) > 0.1", + "interval": "", + "refId": "Anno" + }, + "textFormat": "High WebSocket error rate detected - exceeding 0.1 errors per second", + "titleFormat": "WebSocket Error Rate" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 10, + "x": 0, + "y": 0 + }, + "id": 5, + "options": { + "minVizHeight": 75, + "minVizWidth": 75, + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "sizing": "auto" + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "editorMode": "code", + "expr": "websocket_connections_active", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Websocket Connections Active", + "type": "gauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 15, + "w": 14, + "x": 10, + "y": 0 + }, + "id": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "editorMode": "code", + "expr": "rate(websocket_connection_duration_sum[5m]) / rate(websocket_connection_duration_count[5m])", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Connection Duration Summary", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": [ + "histogram_quantile(0.95, sum(rate(websocket_connection_duration_bucket[5m])) by (le))" + ], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [] + } + ] + }, + "gridPos": { + "h": 11, + "w": 10, + "x": 0, + "y": 8 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(websocket_sent_messages[1m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "Messages Sent", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "rate(websocket_received_messages[1m])", + "hide": false, + "instant": false, + "legendFormat": "Messages Received", + "range": true, + "refId": "B" + } + ], + "title": "Messages Sent/Received Rate", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "description": "Distribution of WebSocket connection durations across different time buckets. Each bar represents a cumulative count of connections that lasted less than or equal to the specified duration (in seconds). For example, '60s' shows connections lasting up to 60 seconds, '120s' shows connections up to 120 seconds, and so on up to '+Inf' (unlimited duration).", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Value" + }, + "properties": [ + { + "id": "noValue" + } + ] + } + ] + }, + "gridPos": { + "h": 17, + "w": 14, + "x": 10, + "y": 15 + }, + "id": 4, + "options": { + "displayMode": "gradient", + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "maxVizHeight": 300, + "minVizHeight": 16, + "minVizWidth": 8, + "namePlacement": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showUnfilled": true, + "sizing": "auto", + "valueMode": "color" + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "exemplar": false, + "expr": "websocket_connection_duration_bucket{le=~\".+\"}", + "format": "table", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": true, + "legendFormat": "{{le}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Websocket Connection Duration", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "Time", + "60.0", + "120.0", + "300.0", + "600.0", + "900.0", + "1200.0", + "1800.0", + "+Inf" + ] + } + } + } + ], + "type": "bargauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": [ + "websocket_read_errors" + ], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": false, + "tooltip": false, + "viz": true + } + } + ] + } + ] + }, + "gridPos": { + "h": 13, + "w": 10, + "x": 0, + "y": 19 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "editorMode": "code", + "expr": "rate(websocket_read_errors[1m])", + "legendFormat": "websocket_read_errors", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "rate(websocket_write_errors[1m])", + "hide": false, + "instant": false, + "legendFormat": "websocket_write_errors", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "rate(websocket_upgrade_errors[1m])", + "hide": false, + "instant": false, + "legendFormat": "websocket_upgrade_errors", + "range": true, + "refId": "C" + } + ], + "title": "Websocket Error Rates", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 41, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-3h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Realtime System Metrics", + "uid": "275dd5ce-c9b5-4ed9-a6d0-cbac478716a1", + "version": 4 +} \ No newline at end of file diff --git a/docker/grafana/provisioning/dashboards/server-dashboard.json b/docker/grafana/provisioning/dashboards/server-dashboard.json new file mode 100644 index 00000000..caa6f15f --- /dev/null +++ b/docker/grafana/provisioning/dashboards/server-dashboard.json @@ -0,0 +1,394 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "enable": true, + "hide": false, + "iconColor": "orange", + "name": "Client Error Spike", + "tagKeys": "client-error, http-4xx", + "target": { + "expr": "sum(rate(http_server_requests_seconds_count{status=~\"4..\"}[5m])) > 0.1", + "interval": "", + "refId": "Anno" + }, + "textFormat": "High rate of client errors detected, exceeding 0.1 requests per second", + "titleFormat": "Client Error Spike" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "enable": true, + "hide": false, + "iconColor": "red", + "name": "Server Error Spike", + "tagKeys": "server, error", + "target": { + "expr": "sum(rate(http_server_requests_seconds_count{status=~\"5..\"}[5m])) > 0.1", + "interval": "", + "refId": "Anno" + }, + "textFormat": "Server error rate exceeded threshold (>0.1 req/s)", + "titleFormat": "Server Error Spike" + } + ] + }, + "description": "Dashboard showing system metrics including request count, latency, and error rate.", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "smooth", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "area" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "orange", + "value": 70 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 17, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "legend": { + "calcs": [ + "mean", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "sum(rate(http_server_requests_seconds_count[5m])) by (method, uri)", + "legendFormat": "{{method}} {{status}} {{uri}}", + "range": true, + "refId": "A" + } + ], + "title": "Request Count", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "Average Latency (seconds)", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "smooth", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "area" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 17, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "legend": { + "calcs": [ + "mean", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": " sum(rate(http_server_requests_seconds_sum[5m])) by (method, uri) /\nsum(rate(http_server_requests_seconds_count[5m])) by (method, uri)", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Latency", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 12, + "w": 12, + "x": 0, + "y": 17 + }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "editorMode": "code", + "expr": "sum(rate(http_server_requests_seconds_count{outcome=\"CLIENT_ERROR\"}[5m])) by (method, uri, status)", + "legendFormat": "__auto", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "sum(rate(http_server_requests_seconds_count{status=~\"5..\"}[5m])) by (method, uri, status)", + "hide": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B" + } + ], + "title": "Errors", + "type": "timeseries" + } + ], + "preload": false, + "refresh": "10s", + "schemaVersion": 41, + "tags": [ + "monitoring", + "alerts" + ], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Server System Metrics Dashboard", + "uid": "system-metrics-dashboard111112", + "version": 1 +} \ No newline at end of file diff --git a/docker/grafana/provisioning/datasources/prometheus.yml b/docker/grafana/provisioning/datasources/prometheus.yml new file mode 100644 index 00000000..8049912b --- /dev/null +++ b/docker/grafana/provisioning/datasources/prometheus.yml @@ -0,0 +1,8 @@ +apiVersion: 1 + +datasources: + - name: Prometheus + type: prometheus + access: proxy + url: http://prometheus:9090 + isDefault: true \ No newline at end of file diff --git a/docker/keycloak/Dockerfile b/docker/keycloak/Dockerfile new file mode 100644 index 00000000..926ab106 --- /dev/null +++ b/docker/keycloak/Dockerfile @@ -0,0 +1 @@ +FROM quay.io/keycloak/keycloak:24.0.1 \ No newline at end of file diff --git a/docker/keycloak/realm-export.json b/docker/keycloak/realm-export.json new file mode 100644 index 00000000..eeace2c4 --- /dev/null +++ b/docker/keycloak/realm-export.json @@ -0,0 +1,2229 @@ +{ + "id": "b36292b9-c4ed-4b3b-84b6-e9274821b709", + "realm": "development", + "displayName": "", + "displayNameHtml": "", + "notBefore": 0, + "defaultSignatureAlgorithm": "RS256", + "revokeRefreshToken": false, + "refreshTokenMaxReuse": 0, + "accessTokenLifespan": 300, + "accessTokenLifespanForImplicitFlow": 900, + "ssoSessionIdleTimeout": 1800, + "ssoSessionMaxLifespan": 36000, + "ssoSessionIdleTimeoutRememberMe": 0, + "ssoSessionMaxLifespanRememberMe": 0, + "offlineSessionIdleTimeout": 2592000, + "offlineSessionMaxLifespanEnabled": false, + "offlineSessionMaxLifespan": 5184000, + "clientSessionIdleTimeout": 0, + "clientSessionMaxLifespan": 0, + "clientOfflineSessionIdleTimeout": 0, + "clientOfflineSessionMaxLifespan": 0, + "accessCodeLifespan": 60, + "accessCodeLifespanUserAction": 300, + "accessCodeLifespanLogin": 1800, + "actionTokenGeneratedByAdminLifespan": 43200, + "actionTokenGeneratedByUserLifespan": 300, + "oauth2DeviceCodeLifespan": 600, + "oauth2DevicePollingInterval": 5, + "enabled": true, + "sslRequired": "external", + "registrationAllowed": true, + "registrationEmailAsUsername": false, + "rememberMe": true, + "verifyEmail": false, + "loginWithEmailAllowed": true, + "duplicateEmailsAllowed": false, + "resetPasswordAllowed": true, + "editUsernameAllowed": false, + "bruteForceProtected": false, + "permanentLockout": false, + "maxTemporaryLockouts": 0, + "maxFailureWaitSeconds": 900, + "minimumQuickLoginWaitSeconds": 60, + "waitIncrementSeconds": 60, + "quickLoginCheckMilliSeconds": 1000, + "maxDeltaTimeSeconds": 43200, + "failureFactor": 30, + "roles": { + "realm": [ + { + "id": "500d1053-80ba-47b9-8206-986de9ae2289", + "name": "default-roles-development", + "description": "${role_default-roles}", + "composite": true, + "composites": { + "realm": [ + "offline_access", + "uma_authorization" + ], + "client": { + "account": [ + "view-profile", + "manage-account" + ] + } + }, + "clientRole": false, + "containerId": "b36292b9-c4ed-4b3b-84b6-e9274821b709", + "attributes": {} + }, + { + "id": "5fb22171-e348-4a48-b9fa-5b76bb8630fe", + "name": "offline_access", + "description": "${role_offline-access}", + "composite": false, + "clientRole": false, + "containerId": "b36292b9-c4ed-4b3b-84b6-e9274821b709", + "attributes": {} + }, + { + "id": "a26758de-1edf-42b1-b4cb-f047892df01b", + "name": "uma_authorization", + "description": "${role_uma_authorization}", + "composite": false, + "clientRole": false, + "containerId": "b36292b9-c4ed-4b3b-84b6-e9274821b709", + "attributes": {} + } + ], + "client": { + "realm-management": [ + { + "id": "2b522345-da51-466c-a806-1674e9301f56", + "name": "manage-realm", + "description": "${role_manage-realm}", + "composite": false, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "605492c9-b8bd-4e04-aecf-fbe8530cc7e0", + "name": "view-events", + "description": "${role_view-events}", + "composite": false, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "81a6a76e-a98b-4abf-b2b1-c63b08584ed0", + "name": "view-clients", + "description": "${role_view-clients}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-clients" + ] + } + }, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "7ff04bad-a542-416d-868f-dd4900157d67", + "name": "realm-admin", + "description": "${role_realm-admin}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "manage-realm", + "view-events", + "view-clients", + "view-identity-providers", + "view-users", + "create-client", + "manage-authorization", + "view-realm", + "query-users", + "manage-users", + "query-realms", + "manage-clients", + "manage-identity-providers", + "query-clients", + "impersonation", + "view-authorization", + "manage-events", + "query-groups" + ] + } + }, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "0616ad64-c775-4d12-9d41-47307cf35146", + "name": "view-identity-providers", + "description": "${role_view-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "59b677f4-4a97-4582-b329-d0912df25aa0", + "name": "view-users", + "description": "${role_view-users}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-users", + "query-groups" + ] + } + }, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "568b7218-5deb-4d6b-8120-41363b074a53", + "name": "create-client", + "description": "${role_create-client}", + "composite": false, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "5b08223e-7fe8-412b-ab50-146f39d1d551", + "name": "manage-authorization", + "description": "${role_manage-authorization}", + "composite": false, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "a7f8535f-4a8b-4ae1-84ea-e1d0ac3d4863", + "name": "view-realm", + "description": "${role_view-realm}", + "composite": false, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "ac41f160-f85a-4a62-948d-8320d6718d8d", + "name": "query-users", + "description": "${role_query-users}", + "composite": false, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "b20d4d27-464f-40d1-b301-891d142ed6a1", + "name": "manage-users", + "description": "${role_manage-users}", + "composite": false, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "b8410aa6-a4e4-4b47-8c0c-eae9a804bd70", + "name": "query-realms", + "description": "${role_query-realms}", + "composite": false, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "be951022-3b18-4736-ba10-5c2f811d30db", + "name": "manage-clients", + "description": "${role_manage-clients}", + "composite": false, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "5864bf1e-d0a0-4fb4-a0cc-46d4fdee532c", + "name": "manage-identity-providers", + "description": "${role_manage-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "41da20d4-a791-41af-835d-345836333126", + "name": "query-clients", + "description": "${role_query-clients}", + "composite": false, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "58dbca2a-df9a-482c-a1ab-38ec319886c7", + "name": "impersonation", + "description": "${role_impersonation}", + "composite": false, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "bc662f5c-df51-44bb-8c0f-d8b9ed97e06b", + "name": "view-authorization", + "description": "${role_view-authorization}", + "composite": false, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "ef24224a-d3f0-44a7-b08b-122d3985a7b6", + "name": "manage-events", + "description": "${role_manage-events}", + "composite": false, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "62de30b4-b660-4ac3-af5b-64b791a2338f", + "name": "query-groups", + "description": "${role_query-groups}", + "composite": false, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + } + ], + "webclient": [], + "security-admin-console": [], + "admin-cli": [], + "account-console": [], + "broker": [ + { + "id": "458e2422-0101-4b2a-9667-dfca9e9b738a", + "name": "read-token", + "description": "${role_read-token}", + "composite": false, + "clientRole": true, + "containerId": "ac218318-0c76-4e01-93fa-4c4e1c8dc300", + "attributes": {} + } + ], + "account": [ + { + "id": "37af2be4-a416-4c22-b459-4afdb2a48060", + "name": "manage-consent", + "description": "${role_manage-consent}", + "composite": true, + "composites": { + "client": { + "account": [ + "view-consent" + ] + } + }, + "clientRole": true, + "containerId": "02c640d1-327d-4227-a6e9-0435f9b1760e", + "attributes": {} + }, + { + "id": "b8b2f48f-e41a-46f9-8307-374c65bb1df2", + "name": "manage-account-links", + "description": "${role_manage-account-links}", + "composite": false, + "clientRole": true, + "containerId": "02c640d1-327d-4227-a6e9-0435f9b1760e", + "attributes": {} + }, + { + "id": "d3ef0fb9-8e0c-4324-b487-128621b8e7ce", + "name": "view-groups", + "description": "${role_view-groups}", + "composite": false, + "clientRole": true, + "containerId": "02c640d1-327d-4227-a6e9-0435f9b1760e", + "attributes": {} + }, + { + "id": "3ba1fc71-79b7-48a5-85dc-0dea8a1f5bd0", + "name": "delete-account", + "description": "${role_delete-account}", + "composite": false, + "clientRole": true, + "containerId": "02c640d1-327d-4227-a6e9-0435f9b1760e", + "attributes": {} + }, + { + "id": "98aee1c3-6c94-498f-8477-4e89a7fc03fc", + "name": "view-profile", + "description": "${role_view-profile}", + "composite": false, + "clientRole": true, + "containerId": "02c640d1-327d-4227-a6e9-0435f9b1760e", + "attributes": {} + }, + { + "id": "e9dba9be-7fc6-4845-a93c-9acc3ea2e37b", + "name": "view-consent", + "description": "${role_view-consent}", + "composite": false, + "clientRole": true, + "containerId": "02c640d1-327d-4227-a6e9-0435f9b1760e", + "attributes": {} + }, + { + "id": "c38032c3-73de-4d37-9c4b-24480b3f98b6", + "name": "manage-account", + "description": "${role_manage-account}", + "composite": true, + "composites": { + "client": { + "account": [ + "manage-account-links" + ] + } + }, + "clientRole": true, + "containerId": "02c640d1-327d-4227-a6e9-0435f9b1760e", + "attributes": {} + }, + { + "id": "d43a2e81-cb7b-4932-b3bf-26c05f484305", + "name": "view-applications", + "description": "${role_view-applications}", + "composite": false, + "clientRole": true, + "containerId": "02c640d1-327d-4227-a6e9-0435f9b1760e", + "attributes": {} + } + ] + } + }, + "groups": [], + "defaultRole": { + "id": "500d1053-80ba-47b9-8206-986de9ae2289", + "name": "default-roles-development", + "description": "${role_default-roles}", + "composite": true, + "clientRole": false, + "containerId": "b36292b9-c4ed-4b3b-84b6-e9274821b709" + }, + "requiredCredentials": [ + "password" + ], + "otpPolicyType": "totp", + "otpPolicyAlgorithm": "HmacSHA1", + "otpPolicyInitialCounter": 0, + "otpPolicyDigits": 6, + "otpPolicyLookAheadWindow": 1, + "otpPolicyPeriod": 30, + "otpPolicyCodeReusable": false, + "otpSupportedApplications": [ + "totpAppFreeOTPName", + "totpAppGoogleName", + "totpAppMicrosoftAuthenticatorName" + ], + "localizationTexts": {}, + "webAuthnPolicyRpEntityName": "keycloak", + "webAuthnPolicySignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyRpId": "", + "webAuthnPolicyAttestationConveyancePreference": "not specified", + "webAuthnPolicyAuthenticatorAttachment": "not specified", + "webAuthnPolicyRequireResidentKey": "not specified", + "webAuthnPolicyUserVerificationRequirement": "not specified", + "webAuthnPolicyCreateTimeout": 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyAcceptableAaguids": [], + "webAuthnPolicyExtraOrigins": [], + "webAuthnPolicyPasswordlessRpEntityName": "keycloak", + "webAuthnPolicyPasswordlessSignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyPasswordlessRpId": "", + "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified", + "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified", + "webAuthnPolicyPasswordlessRequireResidentKey": "not specified", + "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified", + "webAuthnPolicyPasswordlessCreateTimeout": 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyPasswordlessAcceptableAaguids": [], + "webAuthnPolicyPasswordlessExtraOrigins": [], + "scopeMappings": [ + { + "clientScope": "offline_access", + "roles": [ + "offline_access" + ] + } + ], + "clientScopeMappings": { + "account": [ + { + "client": "account-console", + "roles": [ + "manage-account", + "view-groups" + ] + } + ] + }, + "clients": [ + { + "id": "02c640d1-327d-4227-a6e9-0435f9b1760e", + "clientId": "account", + "name": "${client_account}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/development/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/realms/development/account/*" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "7762b786-1fa6-4dea-b51c-0af0e38c8348", + "clientId": "account-console", + "name": "${client_account-console}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/development/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/realms/development/account/*" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+", + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "bba76bcf-bc89-4728-9356-41ca420a0326", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + } + ], + "defaultClientScopes": [ + "web-origins", + "acr", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "ecc207f7-abfc-49a6-8fa6-e1c6e89d84c6", + "clientId": "admin-cli", + "name": "${client_admin-cli}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": false, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "ac218318-0c76-4e01-93fa-4c4e1c8dc300", + "clientId": "broker", + "name": "${client_broker}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "clientId": "realm-management", + "name": "${client_realm-management}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "d55ad226-81d8-49c4-b8be-2c62ece0fe2d", + "clientId": "security-admin-console", + "name": "${client_security-admin-console}", + "rootUrl": "${authAdminUrl}", + "baseUrl": "/admin/development/console/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/admin/development/console/*" + ], + "webOrigins": [ + "+" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+", + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "70c74099-3ccd-4b9d-ad29-2dbe9bf5a0f8", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "acr", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "8036d461-3b22-4e7a-b2f3-a80ab6ffccea", + "clientId": "webclient", + "name": "Web Client", + "description": "", + "rootUrl": "", + "adminUrl": "", + "baseUrl": "", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": true, + "clientAuthenticatorType": "client-secret", + "secret": "SXiMvr1GG10bk2J63ODZC9SOaoAZ4dbe", + "redirectUris": [ + "http://localhost:3000/api/auth/callback/keycloak", + "http://localhost:9091/*" + ], + "webOrigins": [ + "http://localhost:9091", + "http://localhost:3000" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": true, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": true, + "protocol": "openid-connect", + "attributes": { + "oidc.ciba.grant.enabled": "false", + "client.secret.creation.time": "1708880081", + "backchannel.logout.session.required": "true", + "post.logout.redirect.uris": "http://localhost:3000##http://localhost:9091", + "display.on.consent.screen": "false", + "oauth2.device.authorization.grant.enabled": "true", + "backchannel.logout.revoke.offline.tokens": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": [ + "web-origins", + "acr", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + } + ], + "clientScopes": [ + { + "id": "5247110f-f0e2-4a01-a64e-22aa4e1f0551", + "name": "phone", + "description": "OpenID Connect built-in scope: phone", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${phoneScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "261edfee-8d55-4616-8c7a-17ca0509989e", + "name": "phone number verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "phoneNumberVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number_verified", + "jsonType.label": "boolean" + } + }, + { + "id": "eb01d2e6-e1c6-40ca-9da4-f3f1e42dca49", + "name": "phone number", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "phoneNumber", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "e1bae987-e8de-4096-a2bb-dfff8f069022", + "name": "address", + "description": "OpenID Connect built-in scope: address", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${addressScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "aacb899e-0fbd-406f-b537-8bd1cf4e6715", + "name": "address", + "protocol": "openid-connect", + "protocolMapper": "oidc-address-mapper", + "consentRequired": false, + "config": { + "user.attribute.formatted": "formatted", + "user.attribute.country": "country", + "introspection.token.claim": "true", + "user.attribute.postal_code": "postal_code", + "userinfo.token.claim": "true", + "user.attribute.street": "street", + "id.token.claim": "true", + "user.attribute.region": "region", + "access.token.claim": "true", + "user.attribute.locality": "locality" + } + } + ] + }, + { + "id": "beb25224-a81a-40c6-9862-ea01d19ddad7", + "name": "roles", + "description": "OpenID Connect scope for add user roles to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "true", + "consent.screen.text": "${rolesScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "ab24de96-09e8-4d92-b61c-917ff2ef6763", + "name": "realm roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "multivalued": "true", + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "realm_access.roles", + "jsonType.label": "String" + } + }, + { + "id": "1a6f0ed1-6ecb-40d9-af82-3906ddffa7e5", + "name": "client roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-client-role-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "multivalued": "true", + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "resource_access.${client_id}.roles", + "jsonType.label": "String" + } + }, + { + "id": "4ac49eaf-d476-4dc5-a226-77324e5e56b4", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": { + "access.token.claim": "true", + "introspection.token.claim": "true" + } + } + ] + }, + { + "id": "c490baca-b14a-4832-916c-ace1df532810", + "name": "offline_access", + "description": "OpenID Connect built-in scope: offline_access", + "protocol": "openid-connect", + "attributes": { + "consent.screen.text": "${offlineAccessScopeConsentText}", + "display.on.consent.screen": "true" + } + }, + { + "id": "0961b697-1074-4924-8082-b83c541258fe", + "name": "microprofile-jwt", + "description": "Microprofile - JWT built-in scope", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "6edd981a-d225-47c7-8fc5-2f713b971242", + "name": "upn", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "upn", + "jsonType.label": "String" + } + }, + { + "id": "7fc0fc5c-d450-48e3-b285-ed4bb3a29a72", + "name": "groups", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "multivalued": "true", + "userinfo.token.claim": "true", + "user.attribute": "foo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "groups", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "3486255b-1770-4a9f-bd24-4e2dd80f28e6", + "name": "role_list", + "description": "SAML role list", + "protocol": "saml", + "attributes": { + "consent.screen.text": "${samlRoleListScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "e87d4e86-d35f-41c6-b13b-dfefd277bcde", + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", + "consentRequired": false, + "config": { + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + } + ] + }, + { + "id": "7822b2f4-bb70-4108-ab13-88773ac3f397", + "name": "acr", + "description": "OpenID Connect scope for add acr (authentication context class reference) to the token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "5c0b08fd-041d-4f21-a24d-5d20f116ede6", + "name": "acr loa level", + "protocol": "openid-connect", + "protocolMapper": "oidc-acr-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "introspection.token.claim": "true", + "userinfo.token.claim": "true" + } + } + ] + }, + { + "id": "f1640b2e-98eb-4175-8c19-7f0b642f8d62", + "name": "email", + "description": "OpenID Connect built-in scope: email", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${emailScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "a60c53b6-1b6e-4465-9457-27bb099e6921", + "name": "email verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "emailVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email_verified", + "jsonType.label": "boolean" + } + }, + { + "id": "dc526c04-1053-47e4-8f92-d45318c6f600", + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "4601b69d-ea83-4d66-bfb7-d6095a8b8b51", + "name": "web-origins", + "description": "OpenID Connect scope for add allowed web origins to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false", + "consent.screen.text": "" + }, + "protocolMappers": [ + { + "id": "002884fc-7bc8-4583-bffb-e5772413cf14", + "name": "allowed web origins", + "protocol": "openid-connect", + "protocolMapper": "oidc-allowed-origins-mapper", + "consentRequired": false, + "config": { + "access.token.claim": "true", + "introspection.token.claim": "true" + } + } + ] + }, + { + "id": "309d4a2c-208f-4549-a2a5-40f2f0867765", + "name": "profile", + "description": "OpenID Connect built-in scope: profile", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${profileScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "d6153d95-a33a-4c0e-8fa3-a5ea9df103e6", + "name": "gender", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "gender", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "gender", + "jsonType.label": "String" + } + }, + { + "id": "bae8e15c-3a27-4b05-8895-ab073dff8683", + "name": "nickname", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "nickname", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "nickname", + "jsonType.label": "String" + } + }, + { + "id": "913c01af-7409-4e17-b94f-60521ab49a9f", + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "introspection.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "id": "6d6aa12f-1e6b-4d27-9b6d-3e8955881593", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + }, + { + "id": "b527e182-898a-441a-afdf-cc2319aaacc0", + "name": "website", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "website", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "website", + "jsonType.label": "String" + } + }, + { + "id": "c8e7eb48-7055-411c-86a2-36d116454b8b", + "name": "updated at", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "updatedAt", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "updated_at", + "jsonType.label": "long" + } + }, + { + "id": "e014fa8b-a03e-40c4-a174-b2a7a956aa57", + "name": "zoneinfo", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "zoneinfo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "zoneinfo", + "jsonType.label": "String" + } + }, + { + "id": "64e5a635-85a9-42ad-86f2-2d43a72524b2", + "name": "picture", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "picture", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "picture", + "jsonType.label": "String" + } + }, + { + "id": "824d1c93-0d1c-48bc-901a-17d00b74364b", + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + }, + { + "id": "36876d97-38a5-4994-9f13-b92ef4cd7ea9", + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + }, + { + "id": "d998e5f2-7c65-4b68-a8ee-34386fdf404b", + "name": "middle name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "middleName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "middle_name", + "jsonType.label": "String" + } + }, + { + "id": "ebc5d2cd-3442-4cef-8d5b-5146cb5e2824", + "name": "birthdate", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "birthdate", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "birthdate", + "jsonType.label": "String" + } + }, + { + "id": "e6e76b52-b790-44aa-beda-e20e2626cd45", + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "id": "59d693cc-d6e9-452d-96cd-cacf4ead1dc8", + "name": "profile", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "profile", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "profile", + "jsonType.label": "String" + } + } + ] + } + ], + "defaultDefaultClientScopes": [ + "profile", + "role_list", + "web-origins", + "acr", + "roles", + "email" + ], + "defaultOptionalClientScopes": [ + "microprofile-jwt", + "phone", + "offline_access", + "address" + ], + "browserSecurityHeaders": { + "contentSecurityPolicyReportOnly": "", + "xContentTypeOptions": "nosniff", + "referrerPolicy": "no-referrer", + "xRobotsTag": "none", + "xFrameOptions": "SAMEORIGIN", + "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "xXSSProtection": "1; mode=block", + "strictTransportSecurity": "max-age=31536000; includeSubDomains" + }, + "smtpServer": {}, + "eventsEnabled": false, + "eventsListeners": [ + "jboss-logging" + ], + "enabledEventTypes": [], + "adminEventsEnabled": false, + "adminEventsDetailsEnabled": false, + "identityProviders": [], + "identityProviderMappers": [], + "components": { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [ + { + "id": "3526f51d-762e-45dd-b034-7cccbfa7e6c0", + "name": "Consent Required", + "providerId": "consent-required", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "52ce25a4-1a2d-463f-8f88-a46ab1e08995", + "name": "Trusted Hosts", + "providerId": "trusted-hosts", + "subType": "anonymous", + "subComponents": {}, + "config": { + "host-sending-registration-request-must-match": [ + "true" + ], + "client-uris-must-match": [ + "true" + ] + } + }, + { + "id": "78230ed7-c91d-4bf4-993c-d1463105063d", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-usermodel-attribute-mapper", + "oidc-address-mapper", + "oidc-full-name-mapper", + "oidc-usermodel-property-mapper", + "saml-user-attribute-mapper", + "saml-user-property-mapper", + "saml-role-list-mapper", + "oidc-sha256-pairwise-sub-mapper" + ] + } + }, + { + "id": "08f58cfd-4847-4246-bc79-2268dc51305d", + "name": "Max Clients Limit", + "providerId": "max-clients", + "subType": "anonymous", + "subComponents": {}, + "config": { + "max-clients": [ + "200" + ] + } + }, + { + "id": "fb5f3eb5-406f-465c-b3d1-19fd1fb1ffc1", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allow-default-scopes": [ + "true" + ] + } + }, + { + "id": "5ffb837f-8001-42fb-bfa6-c98ae7530b37", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allow-default-scopes": [ + "true" + ] + } + }, + { + "id": "6da6ac13-31e5-4f15-8606-e16d1e834006", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-usermodel-attribute-mapper", + "saml-user-property-mapper", + "oidc-usermodel-property-mapper", + "saml-user-attribute-mapper", + "oidc-address-mapper", + "saml-role-list-mapper", + "oidc-sha256-pairwise-sub-mapper", + "oidc-full-name-mapper" + ] + } + }, + { + "id": "35358f5e-137f-4689-b87f-873f372f2396", + "name": "Full Scope Disabled", + "providerId": "scope", + "subType": "anonymous", + "subComponents": {}, + "config": {} + } + ], + "org.keycloak.userprofile.UserProfileProvider": [ + { + "id": "74e1ebbf-606a-4293-86a2-f9cc0d8e91fb", + "providerId": "declarative-user-profile", + "subComponents": {}, + "config": { + "kc.user.profile.config": [ + "{\"attributes\":[{\"name\":\"username\",\"displayName\":\"${username}\",\"validations\":{\"length\":{\"min\":3,\"max\":255},\"username-prohibited-characters\":{},\"up-username-not-idn-homograph\":{}},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"email\",\"displayName\":\"${email}\",\"validations\":{\"email\":{},\"length\":{\"max\":255}},\"required\":{\"roles\":[\"user\"]},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"firstName\",\"displayName\":\"${firstName}\",\"validations\":{\"length\":{\"max\":255},\"person-name-prohibited-characters\":{}},\"required\":{\"roles\":[\"user\"]},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"lastName\",\"displayName\":\"${lastName}\",\"validations\":{\"length\":{\"max\":255},\"person-name-prohibited-characters\":{}},\"required\":{\"roles\":[\"user\"]},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false}],\"groups\":[{\"name\":\"user-metadata\",\"displayHeader\":\"User metadata\",\"displayDescription\":\"Attributes, which refer to user metadata\"}],\"unmanagedAttributePolicy\":\"ENABLED\"}" + ] + } + } + ], + "org.keycloak.keys.KeyProvider": [ + { + "id": "86b3f7c5-64c9-48ad-9355-fe913ac203c1", + "name": "aes-generated", + "providerId": "aes-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ] + } + }, + { + "id": "1d6fd7c8-76d1-4a9d-9877-60eca8a30336", + "name": "rsa-generated", + "providerId": "rsa-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ] + } + }, + { + "id": "1c57ab36-e4f7-4118-b225-6fdabc10321e", + "name": "rsa-enc-generated", + "providerId": "rsa-enc-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ], + "algorithm": [ + "RSA-OAEP" + ] + } + }, + { + "id": "59724de6-ec76-43de-b8f8-6218085971c9", + "name": "hmac-generated", + "providerId": "hmac-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ], + "algorithm": [ + "HS256" + ] + } + }, + { + "id": "08057a00-49e2-47b3-89c1-ec25f5fdcc2f", + "name": "hmac-generated-hs512", + "providerId": "hmac-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ], + "algorithm": [ + "HS512" + ] + } + } + ] + }, + "internationalizationEnabled": false, + "supportedLocales": [], + "authenticationFlows": [ + { + "id": "b3499093-847b-494f-b0eb-2dcd82c5d9cb", + "alias": "Account verification options", + "description": "Method with which to verity the existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-email-verification", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Verify Existing Account by Re-authentication", + "userSetupAllowed": false + } + ] + }, + { + "id": "dffa8a81-4fde-4b55-ae4f-e002525a2185", + "alias": "Browser - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "dd9679f7-401d-4468-9098-d649fb3f1464", + "alias": "Direct Grant - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "direct-grant-validate-otp", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "112394b4-c201-4faa-8538-c80eeb94746a", + "alias": "First broker login - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "bc5bbc57-1be2-4c9f-9a68-3fe0afda1070", + "alias": "Handle Existing Account", + "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-confirm-link", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Account verification options", + "userSetupAllowed": false + } + ] + }, + { + "id": "682f2aac-0139-4ca0-8d2d-004d658ef2b2", + "alias": "Reset - Conditional OTP", + "description": "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-otp", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "c9b04df1-d208-4a80-8363-cd6023154468", + "alias": "User creation or linking", + "description": "Flow for the existing/non-existing user alternatives", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "create unique user config", + "authenticator": "idp-create-user-if-unique", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Handle Existing Account", + "userSetupAllowed": false + } + ] + }, + { + "id": "313bf938-0fa7-4f8e-a7f9-95ef5f136614", + "alias": "Verify Existing Account by Re-authentication", + "description": "Reauthentication of existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-username-password-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "First broker login - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "a9f14a30-1eb2-46de-b4f0-72a378e82fc5", + "alias": "browser", + "description": "browser based authentication", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-cookie", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-spnego", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "identity-provider-redirector", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 25, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 30, + "autheticatorFlow": true, + "flowAlias": "forms", + "userSetupAllowed": false + } + ] + }, + { + "id": "b0dae209-2a32-44a1-8e44-33fa25b1d7dc", + "alias": "clients", + "description": "Base authentication for clients", + "providerId": "client-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "client-secret", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-jwt", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-secret-jwt", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-x509", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 40, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "b935570a-e80c-433c-a19f-918f9714395e", + "alias": "direct grant", + "description": "OpenID Connect Resource Owner Grant", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "direct-grant-validate-username", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "direct-grant-validate-password", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 30, + "autheticatorFlow": true, + "flowAlias": "Direct Grant - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "aa6af759-eca7-435f-ba9f-b73f3f607b47", + "alias": "docker auth", + "description": "Used by Docker clients to authenticate against the IDP", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "docker-http-basic-authenticator", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "b5572683-b1a9-4c85-935d-dccd1f8aa595", + "alias": "first broker login", + "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "review profile config", + "authenticator": "idp-review-profile", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "User creation or linking", + "userSetupAllowed": false + } + ] + }, + { + "id": "6cc4262d-78c1-4eda-8851-e277c3ac53b4", + "alias": "forms", + "description": "Username, password, otp and other auth forms.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-username-password-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Browser - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "a7c8ab1c-8f6b-4772-b892-242e8cbbe84c", + "alias": "registration", + "description": "registration flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-page-form", + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": true, + "flowAlias": "registration form", + "userSetupAllowed": false + } + ] + }, + { + "id": "a3a22ca3-dc16-4c4d-9bd7-725790fe874c", + "alias": "registration form", + "description": "registration form", + "providerId": "form-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-user-creation", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-password-action", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 50, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-recaptcha-action", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 60, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "0ddae3de-4d23-4325-af3e-ef6058c954e6", + "alias": "reset credentials", + "description": "Reset credentials for a user if they forgot their password or something", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "reset-credentials-choose-user", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-credential-email", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-password", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 40, + "autheticatorFlow": true, + "flowAlias": "Reset - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "d4648fe7-5538-4f23-91cf-6c76e59b641f", + "alias": "saml ecp", + "description": "SAML ECP Profile Authentication Flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "http-basic-authenticator", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + } + ], + "authenticatorConfig": [ + { + "id": "e5b43b21-14cb-44d1-9752-f74729d1a3af", + "alias": "create unique user config", + "config": { + "require.password.update.after.registration": "false" + } + }, + { + "id": "f0a855aa-67e8-4e5d-a65a-d1d5e207ddaa", + "alias": "review profile config", + "config": { + "update.profile.on.first.login": "missing" + } + } + ], + "requiredActions": [ + { + "alias": "CONFIGURE_TOTP", + "name": "Configure OTP", + "providerId": "CONFIGURE_TOTP", + "enabled": true, + "defaultAction": false, + "priority": 10, + "config": {} + }, + { + "alias": "TERMS_AND_CONDITIONS", + "name": "Terms and Conditions", + "providerId": "TERMS_AND_CONDITIONS", + "enabled": false, + "defaultAction": false, + "priority": 20, + "config": {} + }, + { + "alias": "UPDATE_PASSWORD", + "name": "Update Password", + "providerId": "UPDATE_PASSWORD", + "enabled": true, + "defaultAction": false, + "priority": 30, + "config": {} + }, + { + "alias": "UPDATE_PROFILE", + "name": "Update Profile", + "providerId": "UPDATE_PROFILE", + "enabled": true, + "defaultAction": false, + "priority": 40, + "config": {} + }, + { + "alias": "VERIFY_EMAIL", + "name": "Verify Email", + "providerId": "VERIFY_EMAIL", + "enabled": true, + "defaultAction": false, + "priority": 50, + "config": {} + }, + { + "alias": "delete_account", + "name": "Delete Account", + "providerId": "delete_account", + "enabled": false, + "defaultAction": false, + "priority": 60, + "config": {} + }, + { + "alias": "webauthn-register", + "name": "Webauthn Register", + "providerId": "webauthn-register", + "enabled": true, + "defaultAction": false, + "priority": 70, + "config": {} + }, + { + "alias": "webauthn-register-passwordless", + "name": "Webauthn Register Passwordless", + "providerId": "webauthn-register-passwordless", + "enabled": true, + "defaultAction": false, + "priority": 80, + "config": {} + }, + { + "alias": "update_user_locale", + "name": "Update User Locale", + "providerId": "update_user_locale", + "enabled": true, + "defaultAction": false, + "priority": 1000, + "config": {} + } + ], + "browserFlow": "browser", + "registrationFlow": "registration", + "directGrantFlow": "direct grant", + "resetCredentialsFlow": "reset credentials", + "clientAuthenticationFlow": "clients", + "dockerAuthenticationFlow": "docker auth", + "firstBrokerLoginFlow": "first broker login", + "attributes": { + "cibaBackchannelTokenDeliveryMode": "poll", + "cibaAuthRequestedUserHint": "login_hint", + "clientOfflineSessionMaxLifespan": "0", + "oauth2DevicePollingInterval": "5", + "clientSessionIdleTimeout": "0", + "clientOfflineSessionIdleTimeout": "0", + "cibaInterval": "5", + "realmReusableOtpCode": "false", + "cibaExpiresIn": "120", + "oauth2DeviceCodeLifespan": "600", + "parRequestUriLifespan": "60", + "clientSessionMaxLifespan": "0", + "frontendUrl": "", + "acr.loa.map": "{}" + }, + "keycloakVersion": "24.0.2", + "userManagedAccessAllowed": true, + "clientProfiles": { + "profiles": [] + }, + "clientPolicies": { + "policies": [] + }, + "users": [] +} \ No newline at end of file diff --git a/docker/mailhog/Dockerfile b/docker/mailhog/Dockerfile new file mode 100644 index 00000000..51730005 --- /dev/null +++ b/docker/mailhog/Dockerfile @@ -0,0 +1,25 @@ +FROM golang:1.18-alpine as builder + +# Install MailHog: +RUN apk --no-cache add --virtual build-dependencies \ + git \ + && mkdir -p /root/gocode \ + && export GOPATH=/root/gocode \ + && go install github.com/mailhog/MailHog@latest + +FROM alpine:3 +# Add mailhog user/group with uid/gid 1000. +# This is a workaround for boot2docker issue #581, see +# https://github.com/boot2docker/boot2docker/issues/581 +RUN adduser -D -u 1000 mailhog + +COPY --from=builder /root/gocode/bin/MailHog /usr/local/bin/ + +USER mailhog + +WORKDIR /home/mailhog + +ENTRYPOINT ["MailHog"] + +# Expose the SMTP and HTTP ports: +EXPOSE 1025 8025 \ No newline at end of file diff --git a/docker/nginx/cert.crt b/docker/nginx/cert.crt deleted file mode 100644 index ecf1f156..00000000 --- a/docker/nginx/cert.crt +++ /dev/null @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFnDCCA4SgAwIBAgIUH0B5yFqAiamyKtuHWS2LbR2n+lgwDQYJKoZIhvcNAQEL -BQAwLzEtMCsGA1UEAwwkdGVhbXNlcnZlcmRvd24uZGV2b3BzLmFldC5jaXQudHVt -LmRlMB4XDTI1MDUwNTEyMTgwMloXDTI2MDUwNTEyMTgwMlowLzEtMCsGA1UEAwwk -dGVhbXNlcnZlcmRvd24uZGV2b3BzLmFldC5jaXQudHVtLmRlMIICIjANBgkqhkiG -9w0BAQEFAAOCAg8AMIICCgKCAgEAwvnZcrpW89Ym9rqXt6RU0vi4t1+0KmFmu0aK -Z047/iYVWxYZDF+GGoRE+lhM3GJO4Hk4V0S9QPgEi43Hu1vimBUO86c48sQ6VsmN -Dm1BS5rJJ4N13qwostse75D0WDGgxUUARDVwASc0qDqjKF6Z1KkWi99WYR3/jiAh -ktJoPKdclot8cIcL0svjNOaK5Yzx06v0tyN3ZyE3U17wPASQWRR23uxvtHNhm08j -GRCCAaU7KmzZuSPlkFUcLLFkBg0qLEqqkliEWzqjAHKDv6i/hPtVT+7DWhXhNcgq -jNg4HKac1zawMkrD/XijA2FTAhpbmFiH1LPyapZVwIqdMDCtPQrP+1eol5gbzB6f -kYtFRfJ0ra5iF6/ZwwkF9hrvK1pu+lYzw4l4W3ZL1izycSVFlJsGpntRZLtBPzRZ -WjkEWVFqlbClr2+F3RajL5+27W1KBsY0Vpyz+pTtYW+Nthtj4HnV3FZhXiQ2oEuJ -7o/yXHrNS09XMdQXqgDLmcaE0u5rtKYbPHAnvTZHHPvph4dmOfR/ko2Y2FVeb94N -H2GrY5RbuDyDqzZxwqiuRZ+v7XcUc3ySBJx8JSf7T9kc4W/4WpzUEhMKRlmIyr1U -olTRjxI3XpZSg//MXPXsRmZv8i21pgpvDjdKmofIlYlBKclOgdXkPCrFP9g8GgbP -41JwtFMCAwEAAaOBrzCBrDAdBgNVHQ4EFgQUfFSHAlUegQxb9mp/onHOG5lHXAIw -HwYDVR0jBBgwFoAUfFSHAlUegQxb9mp/onHOG5lHXAIwDwYDVR0TAQH/BAUwAwEB -/zBZBgNVHREEUjBQgiR0ZWFtc2VydmVyZG93bi5kZXZvcHMuYWV0LmNpdC50dW0u -ZGWCKGFwaS50ZWFtc2VydmVyZG93bi5kZXZvcHMuYWV0LmNpdC50dW0uZGUwDQYJ -KoZIhvcNAQELBQADggIBACSSO0KTKW1InbpIV/ayCraOjsuPEqk0TDIJmhhcdREe -fVnFIoJw0SfuPg3tX0YXiE04Jd+aD++VyrT1VE0NXP0Vw86x4QOsO4Ivs5nAj0uN -QPwkvOCy04HLbM494UCmbqRhPgZFe5SL8IMlth9n1wMG555MuLDMF5JE5QE5I4TL -PKiYia37psEMd/vIZE8zRF55JYSisK7vX/BAC0eiDOxO0rcrC/Mrr2qdNUL2tpf1 -2s0lvZAdmUPaQ2zyLvxPqUFZ44WjDUe6gEP2E7SaF1X19ZdIxr/4ImfL/VOtutu0 -mhqNIjv08zRRpRR/vVWg13r0bIO+5pdBzvf8+Gk/X43GnYE23ZRe8wwQcFT5fnCF -4DpOgLL/QMqluE7N+3e2K4/4JYdPztc70cObhrq2k1BHBcSOJ5tfwrAb3m+IjbMB -YIBcaK5q0jfZ8yOYeGX4w15hZjBDh+v1U1mjNtzwI8w/tYR/9kHmNNop/722RQRf -Tif/9elUinzh0fymqOK4O2+WgvfIHHhkDrDULU3W8dGH6PlhaWSyR+Mz8cT65dg4 -DSeu1Ps9x4dHzNYaPYcpnuXPhYQMKbrfH3R7I2nD8LFMIGDi5lwBk2Q86HBDRzsV -VYBORN8zviJOt1kEQJUYcTKtLp/JfKJJLscUMIiuz9SRoRxmeZKtyjxYkmuL9WcM ------END CERTIFICATE----- diff --git a/docker/nginx/conf.d/web.conf b/docker/nginx/conf.d/web.conf deleted file mode 100644 index f735f3ab..00000000 --- a/docker/nginx/conf.d/web.conf +++ /dev/null @@ -1,27 +0,0 @@ -upstream server { - server server:8080; -} - -server { - listen 80; - listen 443 ssl; - server_name api.teamserverdown.devops.aet.cit.tum.de; - - ssl_certificate /etc/nginx/cert.crt; - ssl_certificate_key /etc/nginx/private.key; - - client_max_body_size 100M; - - proxy_connect_timeout 7d; - proxy_send_timeout 7d; - proxy_read_timeout 7d; - - location / { - proxy_pass http://server; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_set_header Host $host; - proxy_cache_bypass $http_upgrade; - } -} diff --git a/docker/nginx/private.key b/docker/nginx/private.key deleted file mode 100644 index 12fcc047..00000000 --- a/docker/nginx/private.key +++ /dev/null @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJQQIBADANBgkqhkiG9w0BAQEFAASCCSswggknAgEAAoICAQDC+dlyulbz1ib2 -upe3pFTS+Li3X7QqYWa7RopnTjv+JhVbFhkMX4YahET6WEzcYk7geThXRL1A+ASL -jce7W+KYFQ7zpzjyxDpWyY0ObUFLmskng3XerCiy2x7vkPRYMaDFRQBENXABJzSo -OqMoXpnUqRaL31ZhHf+OICGS0mg8p1yWi3xwhwvSy+M05orljPHTq/S3I3dnITdT -XvA8BJBZFHbe7G+0c2GbTyMZEIIBpTsqbNm5I+WQVRwssWQGDSosSqqSWIRbOqMA -coO/qL+E+1VP7sNaFeE1yCqM2DgcppzXNrAySsP9eKMDYVMCGluYWIfUs/JqllXA -ip0wMK09Cs/7V6iXmBvMHp+Ri0VF8nStrmIXr9nDCQX2Gu8rWm76VjPDiXhbdkvW -LPJxJUWUmwame1Fku0E/NFlaOQRZUWqVsKWvb4XdFqMvn7btbUoGxjRWnLP6lO1h -b422G2PgedXcVmFeJDagS4nuj/Jces1LT1cx1BeqAMuZxoTS7mu0phs8cCe9Nkcc -++mHh2Y59H+SjZjYVV5v3g0fYatjlFu4PIOrNnHCqK5Fn6/tdxRzfJIEnHwlJ/tP -2Rzhb/hanNQSEwpGWYjKvVSiVNGPEjdellKD/8xc9exGZm/yLbWmCm8ON0qah8iV -iUEpyU6B1eQ8KsU/2DwaBs/jUnC0UwIDAQABAoICABX+OeWDmGhT2TxOiuuXmUvc -63Uf0gJPz1dDmzwQ6pp98D3Ajd3D7qwSDHfSuwTHAA+5DL5nThPEhjq5qPR85l/y -TCf2zmkv8ka04HjmoQ1mrdcLsKGH+OVH60Lprm4+Vty5rlCDWJmAsyNgzeOxXBfN -ZPItJKC60gdVn4PgwQpKdJq0rDpDOXWzMvNWKAfBsMuII4f+86qJ6CQ62s1iVGf/ -83Ks5+LKexGaSSK8JnYKDdT4x0xnfK8dsppl/hripEWvvHLakj96K0XTJQJbBhN5 -iEOGcrm4Kxm2dPpDL30n3Bw4moJpxZbTY0vvz/22PGvAXL4E+EzGnXClEhsL+pZs -Gl9G/Jey79GawTQyFdUfORHJ/dTujdHcZsd+lfvYLOJDrGyvwfRqoSR3blOk3BA2 -GlrJmFwarhlUQmdaptwnJSffg3OMUjMPIzs1mv8BURkKYkn4ivfJzQ99Q/qXkPFK -BWg6IbjMjahj7lsjtURsQIPgu4HzzeCBJHMRs0RMKVNneiD19CTwr3BOokptlNWO -0O8OtWe9T+x+kpVNEzM132p6/w84vnjdhFQrZ4/KJyEHoZXMydddFbN6yLMijxjh -Vj7sBeWao8070HK22twLXILSAjq9AOElCmz30KcXSXU/17XYT1QKHDomATiEkk0s -LSa0QqijoABcZFtMaW8xAoIBAQDva4unmRk68mtu4GoYWKJoJkWwEmrIkY/zMx9p -ETDpE6zegxuSOdIBW3/Fjwqp3QrclKLuWcsu6tIDm9zA2PHg544fmL1ETb23sXi/ -qV4Mj7jqfVy2gSKC6wfuTpYEmoqkkHehIZKJgOrTBYtrKuyI+rNhiHOQDmB4oYPz -HKQd0uckhC/HKSRwmBPo4Jf2udGYrfRlv7u1Ra8tqE+rMpKoCFYQhz8yG0UO+hFK -lm6OLHhTpTdeO58C/sS4qPQJMG4xfwdATaFjlPX+4e6ayLB1Zx2GSOUQitx3TUPc -MGrNSFIfbqgg9hF54/19Cf89KWa91FMXJMPzhFWl7iOsXAkxAoIBAQDQemVJUZhj -GZo4oI9725x04ZT+GWnP97A/IcfX5Cl/wTPbXXaPa2k2sJ2VKJi0uwp07JtVltZo -k04h6R6d5CSWMhCQgKixR43hPuRVE6+JucolFWqXDfggzSGMn6A8EGrw7vL3JrnP -/TQtgC1layl0iNhU4weiohjtrtSzEYoZ51czYLNHLeeT9a4mzhkJ4rgxNQ+zlIeN -6RMFcQ6ucBRcqPnRet1d4i7aVWHCLNRnL6iB0MEKY6IcSs3pIC2hb4QZf3RmWiui -ZO+c0Pr77rFpxHTZxWi7JsM/AQL7XKNpV67YWthUcyuo/XdmtRimrXOpAsXKxVDs -65LYKAF0A/TDAoIBABxPBgtCEfajVUqd5E2OpV1VMSY5d0DR3UhvQgaFTtgwSF9i -9y6aAZfBfuEYIbWl+jPMq1staNXaVAyzMC3pOOhT2L0prS9XVdhTdqiK2SD5GW84 -dW7q4+7A5YYq6pgOwdflcQ+vTYlOofVjkXGReLhVlEIzBR8CZCu/RT/IoisYldX+ -fzu4RKO/h5MggzdcD0lTQDOLsSEk5Sqr3QesCiUuHycDJtjA2rCDpum/0cCjx3J0 -dZCB0jJjd1UyPPCdNlpJ38ydoTiKE3AYvMK2eg7Xq2kGU3daQ+kjTKPLYcV2CKfI -yL5k+foEmCNhvDk6HPjTEyQIZ9byTcI53yPxGfECggEAQQl94ZKX5IqPJw1H1c7c -D0Z0cZTCAsP1cqx6KKqgG5/NKqkMnI9YolwUvPyOLwuOpo1NpDTLLJKPtFqCV6Vd -mJGDw2Cvv6Zf5530I2phv6h7HHiC6R7NgoYb6j++AB7rDcCtgVbObslB5tPu1Y3J -v/YU7t5oel7xQezho/9bwtr9xCRtqU3zyZ/CNY7kFsZoyckQ3ef/JdFJQtBTUS3b -3FBGpwgaWPh/v1MVjTrpBEvE2MKkBjaw0vyvIgQM2Cju3/l3+Zo1tJKigZxcQA3l -wOYtweYo1wGvtU7+fCYZQHq/K/WjOS04uJ5iCmOCjjTcOSSJScmmhlKzW8WXNncp -ewKCAQAeoiQwiTgi/8DTuhjcL7vuFrJ6PrFDbRhb2T8X4PhJLizks5WV2mBi1czm -hX2VRK4oXeyXfnpSvpRKzPG+UJNnC5IpkgmUUaQqdw/Htuj6g3OfsKDMrbMnoZwm -QcsHCG33A85SlS7DM5f1UM7NHRrM5Z8oUNKe1CqbAOBefqE2cIBSJT9b6f1l9cc+ -wZWAiawIiyFkI5RdDB4XxC9GKtBY4IYdP3a0Y0OdHR+6ENWhdG7hMfH2uZMs/E/s -zhaUS+bvdKDrXnYYaugz18OIkcZoYUhWjD6Vr6Y0acuZHF6CRhhxWMEdCjlx7OY0 -DkLTmnie2YYQMeTi4KraNJzTEZbi ------END PRIVATE KEY----- diff --git a/docker/postgresql/Dockerfile b/docker/postgresql/Dockerfile index fee97f54..30cd4157 100644 --- a/docker/postgresql/Dockerfile +++ b/docker/postgresql/Dockerfile @@ -1,3 +1,7 @@ FROM postgres:16.2-bullseye +COPY ./scripts/db_init.sh /docker-entrypoint-initdb.d/db_init.sh + +ENV POSTGRES_MULTIPLE_DATABASES=main,keycloak + EXPOSE 5432 \ No newline at end of file diff --git a/docker/postgresql/scripts/db_init.sh b/docker/postgresql/scripts/db_init.sh new file mode 100644 index 00000000..f0f87bce --- /dev/null +++ b/docker/postgresql/scripts/db_init.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +set -e +set -u + +function create_user_and_database() { + local database=$1 + echo "Creating user and database '$database'" + psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL + CREATE USER $database; + CREATE DATABASE $database; + GRANT ALL PRIVILEGES ON DATABASE $database TO $database; +EOSQL +} + +if [ -n "$POSTGRES_MULTIPLE_DATABASES" ]; then + echo "Multiple database creation requested: $POSTGRES_MULTIPLE_DATABASES" + for db in $(echo $POSTGRES_MULTIPLE_DATABASES | tr ',' ' '); do + create_user_and_database $db + done + echo "Multiple databases created" +fi \ No newline at end of file diff --git a/docker/prometheus/Dockerfile b/docker/prometheus/Dockerfile new file mode 100644 index 00000000..9c042ad1 --- /dev/null +++ b/docker/prometheus/Dockerfile @@ -0,0 +1 @@ +FROM prom/prometheus:v3.5.0 \ No newline at end of file diff --git a/docker/prometheus/config/alert.rules.yml b/docker/prometheus/config/alert.rules.yml new file mode 100644 index 00000000..e7aa7f87 --- /dev/null +++ b/docker/prometheus/config/alert.rules.yml @@ -0,0 +1,11 @@ +groups: + - name: service-availability + rules: + - alert: Service Down + expr: up{job=~"server_job|genai_job"} == 0 + for: 1m + labels: + severity: critical + annotations: + summary: "Service {{ $labels.job }} is down" + description: "Service {{ $labels.job }} on {{ $labels.instance }} has been down for more than 1 minute" \ No newline at end of file diff --git a/docker/prometheus/config/prometheus.yml b/docker/prometheus/config/prometheus.yml new file mode 100644 index 00000000..ee883ff0 --- /dev/null +++ b/docker/prometheus/config/prometheus.yml @@ -0,0 +1,31 @@ +global: + scrape_interval: 15s + evaluation_interval: 15s + +alerting: + alertmanagers: + - static_configs: + - targets: + - 'alertmanager:9093' + +rule_files: + - "/etc/prometheus/alert.rules.yml" + +scrape_configs: + - job_name: 'server_job' + metrics_path: '/actuator/prometheus' + static_configs: + - targets: + - 'server:9091' + + - job_name: 'genai_job' + metrics_path: '/metrics' + static_configs: + - targets: + - 'genai:8000' + + - job_name: 'realtime_job' + metrics_path: '/metrics' + static_configs: + - targets: + - 'realtime:9090' \ No newline at end of file diff --git a/docker/redis/Dockerfile b/docker/redis/Dockerfile new file mode 100644 index 00000000..d46a1c0e --- /dev/null +++ b/docker/redis/Dockerfile @@ -0,0 +1,3 @@ +FROM redis:8.0.3-alpine + +CMD ["redis-server"] \ No newline at end of file diff --git a/genai/.env.example b/genai/.env.example new file mode 100644 index 00000000..17cbc3ff --- /dev/null +++ b/genai/.env.example @@ -0,0 +1,2 @@ +OPEN_WEB_UI_API_KEY=your-api-key-here +API_URL="https://your-api-url-here" \ No newline at end of file diff --git a/genai/.gitignore b/genai/.gitignore index b694934f..c2eabeca 100644 --- a/genai/.gitignore +++ b/genai/.gitignore @@ -1 +1,2 @@ -.venv \ No newline at end of file +.venv +.env \ No newline at end of file diff --git a/genai/app/config.py b/genai/app/config.py deleted file mode 100644 index 9f6d9cc1..00000000 --- a/genai/app/config.py +++ /dev/null @@ -1,14 +0,0 @@ -import os -from pydantic_settings import BaseSettings -from dotenv import load_dotenv - - -class Settings(BaseSettings): - load_dotenv() - - db_host: str = os.getenv("DB_HOST") or "" - db_port: int = int(os.getenv("DB_PORT") or 0) - db_grpc_port: int = int(os.getenv("DB_GRPC_PORT") or 0) - - -settings = Settings() diff --git a/genai/app/db/client.py b/genai/app/db/client.py deleted file mode 100644 index eaa70d8e..00000000 --- a/genai/app/db/client.py +++ /dev/null @@ -1,16 +0,0 @@ -import weaviate -from app.config import settings - - -class WeaviateClientSingleton: - _instance = None - - @classmethod - def get_instance(cls): - if cls._instance is None: - cls._instance = weaviate.connect_to_local( - host=settings.db_host, - port=settings.db_port, - grpc_port=settings.db_grpc_port, - ) - return cls._instance diff --git a/genai/app/main.py b/genai/app/main.py index ba3f5419..01fbc3db 100644 --- a/genai/app/main.py +++ b/genai/app/main.py @@ -1,16 +1,236 @@ -from contextlib import asynccontextmanager from fastapi import FastAPI -from app.routes import root -from app.db.client import WeaviateClientSingleton +from prometheus_fastapi_instrumentator import Instrumentator +from prometheus_client import Histogram +import os +import requests +from typing import Any, List, Optional +from fastapi import HTTPException, APIRouter +from pydantic import BaseModel +from langchain.llms.base import LLM +from langchain.callbacks.manager import CallbackManagerForLLMRun +import logging +from fastapi.middleware.cors import CORSMiddleware +from fastapi.responses import JSONResponse +from fastapi.openapi.utils import get_openapi +from dotenv import load_dotenv +# Initialize FastAPI app +app = FastAPI( + title="LLM Service", + description="OpenWebUI powered LLM service for text operations", + version="1.0.0", +) -@asynccontextmanager -async def lifespan(app: FastAPI): - WeaviateClientSingleton.get_instance() - print("Weaviate client initialized") - yield +# Setup logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) +load_dotenv() -app = FastAPI(lifespan=lifespan) +router = APIRouter() -app.include_router(root.router) +Instrumentator().instrument(app).expose(app) + +# Environment configuration +OPEN_WEB_UI_API_KEY = os.getenv("OPEN_WEB_UI_API_KEY") +API_URL = os.getenv("API_URL") +SERVER_URL = os.getenv("SERVER_URL") +CLIENT_URL = os.getenv("CLIENT_URL") +GENAI_URL = os.getenv("GENAI_URL") + + +LLM_TOKEN_COUNT = Histogram( + "llm_token_count", + "Number of tokens in requests/responses", + labelnames=["operation", "type"], +) + + +class OpenWebUILLM(LLM): + api_url: str = API_URL + api_key: str = OPEN_WEB_UI_API_KEY + model_name: str = "llama3.3:latest" + + @property + def _llm_type(self) -> str: + return "open_webui" + + def _call( + self, + prompt: str, + stop: Optional[List[str]] = None, + run_manager: Optional[CallbackManagerForLLMRun] = None, + **kwargs: Any, + ) -> str: + if not self.api_key: + raise ValueError("API_KEY environment variable is required") + + headers = { + "Authorization": f"Bearer {self.api_key}", + "Content-Type": "application/json", + } + + # Updated payload format + payload = { + "model": self.model_name, + "messages": [{"role": "user", "content": prompt}], + } + + try: + logger.info(f"Sending request to OpenWebUI API: {payload}") + response = requests.post( + self.api_url, headers=headers, json=payload, timeout=30 + ) + + if response.status_code != 200: + logger.error(f"API error: {response.status_code} - {response.text}") + raise requests.RequestException( + f"API returned {response.status_code}: {response.text}" + ) + + result = response.json() + logger.info(f"Received response: {result}") + + # Extract content from choices array + if "choices" in result and len(result["choices"]) > 0: + content = result["choices"][0]["message"]["content"] + return content.strip() + else: + logger.error(f"Unexpected response format: {result}") + raise ValueError(f"Unexpected response format: {result}") + + except requests.RequestException as e: + logger.error(f"API request failed: {str(e)}") + raise Exception(f"API request failed: {str(e)}") + + +@app.get("/v3/api-docs", include_in_schema=False) +def custom_openapi(): + return JSONResponse( + get_openapi( + title=app.title, + version=app.version, + routes=app.routes, + servers=[{"url": GENAI_URL}], + ) + ) + + +app.add_middleware( + CORSMiddleware, + allow_origins=[CLIENT_URL, SERVER_URL], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +# Initialize LLM +llm = OpenWebUILLM() + + +class TextRequest(BaseModel): + user_text: str + + +class TextResponse(BaseModel): + llm_response: str + + +@router.post("/completion", response_model=TextResponse) +async def complete_text(request: TextRequest): + operation = "completion" + + try: + input_tokens = len(request.user_text.split(" ")) + LLM_TOKEN_COUNT.labels(operation=operation, type="input").observe(input_tokens) + + prompt = f"""Complete the following text with exactly one natural sentence: + {request.user_text} + + Rules: + - ALWAYS start your response with the exact input text + - Add only ONE sentence + - Keep the style consistent + - Make it coherent with the input + """ + logger.info(f"Processing completion request for text: {request.user_text}") + result = llm(prompt) + + output_tokens = len(result.split()) + LLM_TOKEN_COUNT.labels(operation=operation, type="output").observe( + output_tokens + ) + + logger.info(f"Generated completion: {result}") + return TextResponse(llm_response=result) + except Exception as e: + logger.error(f"Completion error: {str(e)}") + raise HTTPException(status_code=500, detail=str(e)) + + +@router.post("/summarization", response_model=TextResponse) +async def summarize_text(request: TextRequest): + operation = "summarization" + + try: + input_tokens = len(request.user_text.split(" ")) + LLM_TOKEN_COUNT.labels(operation=operation, type="input").observe(input_tokens) + + prompt = f"""Summarize the following text concisely: + {request.user_text} + """ + result = llm(prompt) + + output_tokens = len(result.split()) + LLM_TOKEN_COUNT.labels(operation=operation, type="output").observe( + output_tokens + ) + + return TextResponse(llm_response=result) + except Exception as e: + logger.error(f"Summarization error: {str(e)}") + raise HTTPException(status_code=500, detail=str(e)) + + +@router.post("/rephrase", response_model=TextResponse) +async def rephrase_text(request: TextRequest): + operation = "rephrase_text" + logger.info(f"Received rephrase request: {request}") + + try: + input_tokens = len(request.user_text.split(" ")) + LLM_TOKEN_COUNT.labels(operation=operation, type="input").observe(input_tokens) + + word_count = len(request.user_text.split()) + prompt = f"""Rephrase the following text: + {request.user_text} + + Rules: + - Keep EXACTLY {word_count} words + - Maintain the original meaning + - Use similar tone and style + - Make it sound natural + """ + logger.info(f"Received rephrase request: {request.user_text}") + result = llm(prompt) + + output_tokens = len(result.split()) + LLM_TOKEN_COUNT.labels(operation=operation, type="output").observe( + output_tokens + ) + # Ensure exact word count + result_words = result.split() + if len(result_words) > word_count: + result = " ".join(result_words[:word_count]) + return TextResponse(llm_response=result) + except Exception as e: + logger.error(f"Rephrase error: {str(e)}") + raise HTTPException(status_code=500, detail=str(e)) + + +@router.get("/health") +async def health_check(): + return {"status": "healthy", "model": llm.model_name, "api_url": llm.api_url} + + +app.include_router(router) diff --git a/genai/app/test.py b/genai/app/test.py new file mode 100644 index 00000000..02f65146 --- /dev/null +++ b/genai/app/test.py @@ -0,0 +1,31 @@ +from fastapi.testclient import TestClient +from app.main import app + +client = TestClient(app) + + +def test_health_check(): + response = client.get("/health") + assert response.status_code == 200 + assert "status" in response.json() + + +def test_completion(): + payload = {"user_text": "This is a test input."} + response = client.post("/completion", json=payload) + assert response.status_code == 200 + assert "llm_response" in response.json() + + +def test_summarization(): + payload = {"user_text": "This is a long sentence that needs summarizing."} + response = client.post("/summarization", json=payload) + assert response.status_code == 200 + assert "llm_response" in response.json() + + +def test_rephrase(): + payload = {"user_text": "This is a sample sentence."} + response = client.post("/rephrase", json=payload) + assert response.status_code == 200 + assert "llm_response" in response.json() diff --git a/genai/app/utils/__init__.py b/genai/app/utils/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/genai/pyproject.toml b/genai/pyproject.toml index 470be9f6..8869d9aa 100644 --- a/genai/pyproject.toml +++ b/genai/pyproject.toml @@ -67,11 +67,20 @@ line-ending = "auto" # # This is currently disabled by default, but it is planned for this # to be opt-out in the future. -docstring-code-format = false +#docstring-code-format = false # Set the line length limit used when formatting code snippets in # docstrings. # # This only has an effect when the `docstring-code-format` setting is # enabled. -docstring-code-line-length = "dynamic" \ No newline at end of file +#docstring-code-line-length = "dynamic" + +[tool.pytest.ini_options] +asyncio_mode = "auto" +testpaths = ["app"] +python_files = ["test_*.py", "*_test.py", "test.py"] +addopts = "-v" +markers = [ + "asyncio: mark test as an async test", +] \ No newline at end of file diff --git a/genai/requirements.txt b/genai/requirements.txt index a946d4fe..a56d35c1 100644 --- a/genai/requirements.txt +++ b/genai/requirements.txt @@ -3,13 +3,15 @@ anyio==4.9.0 authlib==1.3.1 certifi==2025.4.26 cffi==1.17.1 +charset-normalizer==3.4.2 click==8.1.8 -cryptography==44.0.3 +cryptography==43.0.3 deprecation==2.1.0 dnspython==2.7.0 email-validator==2.2.0 fastapi==0.115.12 fastapi-cli==0.0.7 +greenlet==3.2.3 grpcio==1.71.0 grpcio-health-checking==1.71.0 grpcio-tools==1.71.0 @@ -20,11 +22,21 @@ httpx==0.28.1 idna==3.10 iniconfig==2.1.0 jinja2==3.1.6 +jsonpatch==1.33 +jsonpointer==3.0.0 +langchain==0.3.26 +langchain-core==0.3.69 +langchain-text-splitters==0.3.8 +langsmith==0.4.8 markdown-it-py==3.0.0 markupsafe==3.0.2 mdurl==0.1.2 -packaging==25.0 +orjson==3.11.0 +packaging==23.2 +pip==25.0.1 pluggy==1.5.0 +prometheus-client==0.22.1 +prometheus-fastapi-instrumentator==7.1.0 protobuf==5.29.4 pycparser==2.22 pydantic==2.11.4 @@ -35,19 +47,25 @@ pytest==8.3.5 python-dotenv==1.1.0 python-multipart==0.0.20 pyyaml==6.0.2 +requests==2.31.0 +requests-toolbelt==1.0.0 rich==14.0.0 rich-toolkit==0.14.5 ruff==0.11.8 setuptools==80.4.0 shellingham==1.5.4 sniffio==1.3.1 +sqlalchemy==2.0.41 starlette==0.46.2 +tenacity==9.1.2 typer==0.15.3 typing-extensions==4.13.2 typing-inspection==0.4.0 +urllib3==2.5.0 uvicorn==0.34.2 uvloop==0.21.0 validators==0.34.0 watchfiles==1.0.5 weaviate-client==4.14.1 websockets==15.0.1 +zstandard==0.23.0 diff --git a/genai/tests/__init__.py b/genai/tests/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/genai/tests/routes/__init__.py b/genai/tests/routes/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/genai/tests/routes/test_root.py b/genai/tests/routes/test_root.py deleted file mode 100644 index 6d0ac9c9..00000000 --- a/genai/tests/routes/test_root.py +++ /dev/null @@ -1,10 +0,0 @@ -from fastapi.testclient import TestClient -from app.main import app - -client = TestClient(app) - - -def test_read_main(): - response = client.get("/") - assert response.status_code == 200 - assert response.json() == {"Hello": "World"} diff --git a/infrastructure/whiteboard-app/.helmignore b/infrastructure/whiteboard-app/.helmignore new file mode 100644 index 00000000..0e8a0eb3 --- /dev/null +++ b/infrastructure/whiteboard-app/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/infrastructure/whiteboard-app/Chart.lock b/infrastructure/whiteboard-app/Chart.lock new file mode 100644 index 00000000..00b613af --- /dev/null +++ b/infrastructure/whiteboard-app/Chart.lock @@ -0,0 +1,12 @@ +dependencies: +- name: postgresql + repository: https://charts.bitnami.com/bitnami + version: 16.2.5 +- name: keycloak + repository: https://charts.bitnami.com/bitnami + version: 21.0.1 +- name: redis + repository: https://charts.bitnami.com/bitnami + version: 21.2.12 +digest: sha256:68d94162b9c62e8d173c984b419f1837de111fe9d75c381a1555bc09860e543b +generated: "2025-07-19T17:14:37.691057+02:00" diff --git a/infrastructure/whiteboard-app/Chart.yaml b/infrastructure/whiteboard-app/Chart.yaml new file mode 100644 index 00000000..ab751034 --- /dev/null +++ b/infrastructure/whiteboard-app/Chart.yaml @@ -0,0 +1,15 @@ +apiVersion: v2 +name: whiteboard +description: A Helm chart for Whiteboard Application +version: 0.1.0 +appVersion: "1.0.0" +dependencies: + - name: postgresql + version: 16.2.5 + repository: "https://charts.bitnami.com/bitnami" + - name: keycloak + version: 21.0.1 + repository: "https://charts.bitnami.com/bitnami" + - name: redis + version: 21.2.12 + repository: "https://charts.bitnami.com/bitnami" \ No newline at end of file diff --git a/infrastructure/whiteboard-app/charts/keycloak-21.0.1.tgz b/infrastructure/whiteboard-app/charts/keycloak-21.0.1.tgz new file mode 100644 index 00000000..6a8bd6e7 Binary files /dev/null and b/infrastructure/whiteboard-app/charts/keycloak-21.0.1.tgz differ diff --git a/infrastructure/whiteboard-app/charts/postgresql-16.2.5.tgz b/infrastructure/whiteboard-app/charts/postgresql-16.2.5.tgz new file mode 100644 index 00000000..1ca54214 Binary files /dev/null and b/infrastructure/whiteboard-app/charts/postgresql-16.2.5.tgz differ diff --git a/infrastructure/whiteboard-app/charts/redis-21.2.12.tgz b/infrastructure/whiteboard-app/charts/redis-21.2.12.tgz new file mode 100644 index 00000000..2bac8a9b Binary files /dev/null and b/infrastructure/whiteboard-app/charts/redis-21.2.12.tgz differ diff --git a/infrastructure/whiteboard-app/files/keycloak/realm-export.json b/infrastructure/whiteboard-app/files/keycloak/realm-export.json new file mode 100644 index 00000000..c1298a91 --- /dev/null +++ b/infrastructure/whiteboard-app/files/keycloak/realm-export.json @@ -0,0 +1,2229 @@ +{ + "id": "b36292b9-c4ed-4b3b-84b6-e9274821b709", + "realm": "{{ .Values.namespace }}", + "displayName": "", + "displayNameHtml": "", + "notBefore": 0, + "defaultSignatureAlgorithm": "RS256", + "revokeRefreshToken": false, + "refreshTokenMaxReuse": 0, + "accessTokenLifespan": 300, + "accessTokenLifespanForImplicitFlow": 900, + "ssoSessionIdleTimeout": 1800, + "ssoSessionMaxLifespan": 36000, + "ssoSessionIdleTimeoutRememberMe": 0, + "ssoSessionMaxLifespanRememberMe": 0, + "offlineSessionIdleTimeout": 2592000, + "offlineSessionMaxLifespanEnabled": false, + "offlineSessionMaxLifespan": 5184000, + "clientSessionIdleTimeout": 0, + "clientSessionMaxLifespan": 0, + "clientOfflineSessionIdleTimeout": 0, + "clientOfflineSessionMaxLifespan": 0, + "accessCodeLifespan": 60, + "accessCodeLifespanUserAction": 300, + "accessCodeLifespanLogin": 1800, + "actionTokenGeneratedByAdminLifespan": 43200, + "actionTokenGeneratedByUserLifespan": 300, + "oauth2DeviceCodeLifespan": 600, + "oauth2DevicePollingInterval": 5, + "enabled": true, + "sslRequired": "external", + "registrationAllowed": true, + "registrationEmailAsUsername": false, + "rememberMe": true, + "verifyEmail": false, + "loginWithEmailAllowed": true, + "duplicateEmailsAllowed": false, + "resetPasswordAllowed": true, + "editUsernameAllowed": false, + "bruteForceProtected": false, + "permanentLockout": false, + "maxTemporaryLockouts": 0, + "maxFailureWaitSeconds": 900, + "minimumQuickLoginWaitSeconds": 60, + "waitIncrementSeconds": 60, + "quickLoginCheckMilliSeconds": 1000, + "maxDeltaTimeSeconds": 43200, + "failureFactor": 30, + "roles": { + "realm": [ + { + "id": "500d1053-80ba-47b9-8206-986de9ae2289", + "name": "default-roles-{{ .Values.namespace }}", + "description": "${role_default-roles}", + "composite": true, + "composites": { + "realm": [ + "offline_access", + "uma_authorization" + ], + "client": { + "account": [ + "view-profile", + "manage-account" + ] + } + }, + "clientRole": false, + "containerId": "b36292b9-c4ed-4b3b-84b6-e9274821b709", + "attributes": {} + }, + { + "id": "5fb22171-e348-4a48-b9fa-5b76bb8630fe", + "name": "offline_access", + "description": "${role_offline-access}", + "composite": false, + "clientRole": false, + "containerId": "b36292b9-c4ed-4b3b-84b6-e9274821b709", + "attributes": {} + }, + { + "id": "a26758de-1edf-42b1-b4cb-f047892df01b", + "name": "uma_authorization", + "description": "${role_uma_authorization}", + "composite": false, + "clientRole": false, + "containerId": "b36292b9-c4ed-4b3b-84b6-e9274821b709", + "attributes": {} + } + ], + "client": { + "realm-management": [ + { + "id": "2b522345-da51-466c-a806-1674e9301f56", + "name": "manage-realm", + "description": "${role_manage-realm}", + "composite": false, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "605492c9-b8bd-4e04-aecf-fbe8530cc7e0", + "name": "view-events", + "description": "${role_view-events}", + "composite": false, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "81a6a76e-a98b-4abf-b2b1-c63b08584ed0", + "name": "view-clients", + "description": "${role_view-clients}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-clients" + ] + } + }, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "7ff04bad-a542-416d-868f-dd4900157d67", + "name": "realm-admin", + "description": "${role_realm-admin}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "manage-realm", + "view-events", + "view-clients", + "view-identity-providers", + "view-users", + "create-client", + "manage-authorization", + "view-realm", + "query-users", + "manage-users", + "query-realms", + "manage-clients", + "manage-identity-providers", + "query-clients", + "impersonation", + "view-authorization", + "manage-events", + "query-groups" + ] + } + }, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "0616ad64-c775-4d12-9d41-47307cf35146", + "name": "view-identity-providers", + "description": "${role_view-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "59b677f4-4a97-4582-b329-d0912df25aa0", + "name": "view-users", + "description": "${role_view-users}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-users", + "query-groups" + ] + } + }, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "568b7218-5deb-4d6b-8120-41363b074a53", + "name": "create-client", + "description": "${role_create-client}", + "composite": false, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "5b08223e-7fe8-412b-ab50-146f39d1d551", + "name": "manage-authorization", + "description": "${role_manage-authorization}", + "composite": false, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "a7f8535f-4a8b-4ae1-84ea-e1d0ac3d4863", + "name": "view-realm", + "description": "${role_view-realm}", + "composite": false, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "ac41f160-f85a-4a62-948d-8320d6718d8d", + "name": "query-users", + "description": "${role_query-users}", + "composite": false, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "b20d4d27-464f-40d1-b301-891d142ed6a1", + "name": "manage-users", + "description": "${role_manage-users}", + "composite": false, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "b8410aa6-a4e4-4b47-8c0c-eae9a804bd70", + "name": "query-realms", + "description": "${role_query-realms}", + "composite": false, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "be951022-3b18-4736-ba10-5c2f811d30db", + "name": "manage-clients", + "description": "${role_manage-clients}", + "composite": false, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "5864bf1e-d0a0-4fb4-a0cc-46d4fdee532c", + "name": "manage-identity-providers", + "description": "${role_manage-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "41da20d4-a791-41af-835d-345836333126", + "name": "query-clients", + "description": "${role_query-clients}", + "composite": false, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "58dbca2a-df9a-482c-a1ab-38ec319886c7", + "name": "impersonation", + "description": "${role_impersonation}", + "composite": false, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "bc662f5c-df51-44bb-8c0f-d8b9ed97e06b", + "name": "view-authorization", + "description": "${role_view-authorization}", + "composite": false, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "ef24224a-d3f0-44a7-b08b-122d3985a7b6", + "name": "manage-events", + "description": "${role_manage-events}", + "composite": false, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + }, + { + "id": "62de30b4-b660-4ac3-af5b-64b791a2338f", + "name": "query-groups", + "description": "${role_query-groups}", + "composite": false, + "clientRole": true, + "containerId": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "attributes": {} + } + ], + "webclient": [], + "security-admin-console": [], + "admin-cli": [], + "account-console": [], + "broker": [ + { + "id": "458e2422-0101-4b2a-9667-dfca9e9b738a", + "name": "read-token", + "description": "${role_read-token}", + "composite": false, + "clientRole": true, + "containerId": "ac218318-0c76-4e01-93fa-4c4e1c8dc300", + "attributes": {} + } + ], + "account": [ + { + "id": "37af2be4-a416-4c22-b459-4afdb2a48060", + "name": "manage-consent", + "description": "${role_manage-consent}", + "composite": true, + "composites": { + "client": { + "account": [ + "view-consent" + ] + } + }, + "clientRole": true, + "containerId": "02c640d1-327d-4227-a6e9-0435f9b1760e", + "attributes": {} + }, + { + "id": "b8b2f48f-e41a-46f9-8307-374c65bb1df2", + "name": "manage-account-links", + "description": "${role_manage-account-links}", + "composite": false, + "clientRole": true, + "containerId": "02c640d1-327d-4227-a6e9-0435f9b1760e", + "attributes": {} + }, + { + "id": "d3ef0fb9-8e0c-4324-b487-128621b8e7ce", + "name": "view-groups", + "description": "${role_view-groups}", + "composite": false, + "clientRole": true, + "containerId": "02c640d1-327d-4227-a6e9-0435f9b1760e", + "attributes": {} + }, + { + "id": "3ba1fc71-79b7-48a5-85dc-0dea8a1f5bd0", + "name": "delete-account", + "description": "${role_delete-account}", + "composite": false, + "clientRole": true, + "containerId": "02c640d1-327d-4227-a6e9-0435f9b1760e", + "attributes": {} + }, + { + "id": "98aee1c3-6c94-498f-8477-4e89a7fc03fc", + "name": "view-profile", + "description": "${role_view-profile}", + "composite": false, + "clientRole": true, + "containerId": "02c640d1-327d-4227-a6e9-0435f9b1760e", + "attributes": {} + }, + { + "id": "e9dba9be-7fc6-4845-a93c-9acc3ea2e37b", + "name": "view-consent", + "description": "${role_view-consent}", + "composite": false, + "clientRole": true, + "containerId": "02c640d1-327d-4227-a6e9-0435f9b1760e", + "attributes": {} + }, + { + "id": "c38032c3-73de-4d37-9c4b-24480b3f98b6", + "name": "manage-account", + "description": "${role_manage-account}", + "composite": true, + "composites": { + "client": { + "account": [ + "manage-account-links" + ] + } + }, + "clientRole": true, + "containerId": "02c640d1-327d-4227-a6e9-0435f9b1760e", + "attributes": {} + }, + { + "id": "d43a2e81-cb7b-4932-b3bf-26c05f484305", + "name": "view-applications", + "description": "${role_view-applications}", + "composite": false, + "clientRole": true, + "containerId": "02c640d1-327d-4227-a6e9-0435f9b1760e", + "attributes": {} + } + ] + } + }, + "groups": [], + "defaultRole": { + "id": "500d1053-80ba-47b9-8206-986de9ae2289", + "name": "default-roles-{{ .Values.namespace }}", + "description": "${role_default-roles}", + "composite": true, + "clientRole": false, + "containerId": "b36292b9-c4ed-4b3b-84b6-e9274821b709" + }, + "requiredCredentials": [ + "password" + ], + "otpPolicyType": "totp", + "otpPolicyAlgorithm": "HmacSHA1", + "otpPolicyInitialCounter": 0, + "otpPolicyDigits": 6, + "otpPolicyLookAheadWindow": 1, + "otpPolicyPeriod": 30, + "otpPolicyCodeReusable": false, + "otpSupportedApplications": [ + "totpAppFreeOTPName", + "totpAppGoogleName", + "totpAppMicrosoftAuthenticatorName" + ], + "localizationTexts": {}, + "webAuthnPolicyRpEntityName": "keycloak", + "webAuthnPolicySignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyRpId": "", + "webAuthnPolicyAttestationConveyancePreference": "not specified", + "webAuthnPolicyAuthenticatorAttachment": "not specified", + "webAuthnPolicyRequireResidentKey": "not specified", + "webAuthnPolicyUserVerificationRequirement": "not specified", + "webAuthnPolicyCreateTimeout": 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyAcceptableAaguids": [], + "webAuthnPolicyExtraOrigins": [], + "webAuthnPolicyPasswordlessRpEntityName": "keycloak", + "webAuthnPolicyPasswordlessSignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyPasswordlessRpId": "", + "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified", + "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified", + "webAuthnPolicyPasswordlessRequireResidentKey": "not specified", + "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified", + "webAuthnPolicyPasswordlessCreateTimeout": 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyPasswordlessAcceptableAaguids": [], + "webAuthnPolicyPasswordlessExtraOrigins": [], + "scopeMappings": [ + { + "clientScope": "offline_access", + "roles": [ + "offline_access" + ] + } + ], + "clientScopeMappings": { + "account": [ + { + "client": "account-console", + "roles": [ + "manage-account", + "view-groups" + ] + } + ] + }, + "clients": [ + { + "id": "02c640d1-327d-4227-a6e9-0435f9b1760e", + "clientId": "account", + "name": "${client_account}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/{{ .Values.namespace }}/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/realms/{{ .Values.namespace }}/account/*" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "7762b786-1fa6-4dea-b51c-0af0e38c8348", + "clientId": "account-console", + "name": "${client_account-console}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/{{ .Values.namespace }}/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/realms/{{ .Values.namespace }}/account/*" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+", + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "bba76bcf-bc89-4728-9356-41ca420a0326", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + } + ], + "defaultClientScopes": [ + "web-origins", + "acr", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "ecc207f7-abfc-49a6-8fa6-e1c6e89d84c6", + "clientId": "admin-cli", + "name": "${client_admin-cli}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": false, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "ac218318-0c76-4e01-93fa-4c4e1c8dc300", + "clientId": "broker", + "name": "${client_broker}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "eddc021b-a650-4cdc-ba12-37691a81fbbb", + "clientId": "realm-management", + "name": "${client_realm-management}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "d55ad226-81d8-49c4-b8be-2c62ece0fe2d", + "clientId": "security-admin-console", + "name": "${client_security-admin-console}", + "rootUrl": "${authAdminUrl}", + "baseUrl": "/admin/{{ .Values.namespace }}/console/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/admin/{{ .Values.namespace }}/console/*" + ], + "webOrigins": [ + "+" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+", + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "70c74099-3ccd-4b9d-ad29-2dbe9bf5a0f8", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "acr", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "8036d461-3b22-4e7a-b2f3-a80ab6ffccea", + "clientId": "webclient", + "name": "Web Client", + "description": "", + "rootUrl": "", + "adminUrl": "", + "baseUrl": "", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": true, + "clientAuthenticatorType": "client-secret", + "secret": "SXiMvr1GG10bk2J63ODZC9SOaoAZ4dbe", + "redirectUris": [ + "https://{{ .Values.client.url }}/api/auth/callback/keycloak", + "https://{{ .Values.server.url }}/*" + ], + "webOrigins": [ + "https://{{ .Values.client.url }}", + "https://{{ .Values.server.url }}" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": true, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": true, + "protocol": "openid-connect", + "attributes": { + "oidc.ciba.grant.enabled": "false", + "client.secret.creation.time": "1708880081", + "backchannel.logout.session.required": "true", + "post.logout.redirect.uris": "https://{{ .Values.client.url }}##https://{{ .Values.server.url }}", + "display.on.consent.screen": "false", + "oauth2.device.authorization.grant.enabled": "true", + "backchannel.logout.revoke.offline.tokens": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": [ + "web-origins", + "acr", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + } + ], + "clientScopes": [ + { + "id": "5247110f-f0e2-4a01-a64e-22aa4e1f0551", + "name": "phone", + "description": "OpenID Connect built-in scope: phone", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${phoneScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "261edfee-8d55-4616-8c7a-17ca0509989e", + "name": "phone number verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "phoneNumberVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number_verified", + "jsonType.label": "boolean" + } + }, + { + "id": "eb01d2e6-e1c6-40ca-9da4-f3f1e42dca49", + "name": "phone number", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "phoneNumber", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "e1bae987-e8de-4096-a2bb-dfff8f069022", + "name": "address", + "description": "OpenID Connect built-in scope: address", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${addressScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "aacb899e-0fbd-406f-b537-8bd1cf4e6715", + "name": "address", + "protocol": "openid-connect", + "protocolMapper": "oidc-address-mapper", + "consentRequired": false, + "config": { + "user.attribute.formatted": "formatted", + "user.attribute.country": "country", + "introspection.token.claim": "true", + "user.attribute.postal_code": "postal_code", + "userinfo.token.claim": "true", + "user.attribute.street": "street", + "id.token.claim": "true", + "user.attribute.region": "region", + "access.token.claim": "true", + "user.attribute.locality": "locality" + } + } + ] + }, + { + "id": "beb25224-a81a-40c6-9862-ea01d19ddad7", + "name": "roles", + "description": "OpenID Connect scope for add user roles to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "true", + "consent.screen.text": "${rolesScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "ab24de96-09e8-4d92-b61c-917ff2ef6763", + "name": "realm roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "multivalued": "true", + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "realm_access.roles", + "jsonType.label": "String" + } + }, + { + "id": "1a6f0ed1-6ecb-40d9-af82-3906ddffa7e5", + "name": "client roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-client-role-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "multivalued": "true", + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "resource_access.${client_id}.roles", + "jsonType.label": "String" + } + }, + { + "id": "4ac49eaf-d476-4dc5-a226-77324e5e56b4", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": { + "access.token.claim": "true", + "introspection.token.claim": "true" + } + } + ] + }, + { + "id": "c490baca-b14a-4832-916c-ace1df532810", + "name": "offline_access", + "description": "OpenID Connect built-in scope: offline_access", + "protocol": "openid-connect", + "attributes": { + "consent.screen.text": "${offlineAccessScopeConsentText}", + "display.on.consent.screen": "true" + } + }, + { + "id": "0961b697-1074-4924-8082-b83c541258fe", + "name": "microprofile-jwt", + "description": "Microprofile - JWT built-in scope", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "6edd981a-d225-47c7-8fc5-2f713b971242", + "name": "upn", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "upn", + "jsonType.label": "String" + } + }, + { + "id": "7fc0fc5c-d450-48e3-b285-ed4bb3a29a72", + "name": "groups", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "multivalued": "true", + "userinfo.token.claim": "true", + "user.attribute": "foo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "groups", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "3486255b-1770-4a9f-bd24-4e2dd80f28e6", + "name": "role_list", + "description": "SAML role list", + "protocol": "saml", + "attributes": { + "consent.screen.text": "${samlRoleListScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "e87d4e86-d35f-41c6-b13b-dfefd277bcde", + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", + "consentRequired": false, + "config": { + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + } + ] + }, + { + "id": "7822b2f4-bb70-4108-ab13-88773ac3f397", + "name": "acr", + "description": "OpenID Connect scope for add acr (authentication context class reference) to the token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "5c0b08fd-041d-4f21-a24d-5d20f116ede6", + "name": "acr loa level", + "protocol": "openid-connect", + "protocolMapper": "oidc-acr-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "introspection.token.claim": "true", + "userinfo.token.claim": "true" + } + } + ] + }, + { + "id": "f1640b2e-98eb-4175-8c19-7f0b642f8d62", + "name": "email", + "description": "OpenID Connect built-in scope: email", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${emailScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "a60c53b6-1b6e-4465-9457-27bb099e6921", + "name": "email verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "emailVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email_verified", + "jsonType.label": "boolean" + } + }, + { + "id": "dc526c04-1053-47e4-8f92-d45318c6f600", + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "4601b69d-ea83-4d66-bfb7-d6095a8b8b51", + "name": "web-origins", + "description": "OpenID Connect scope for add allowed web origins to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false", + "consent.screen.text": "" + }, + "protocolMappers": [ + { + "id": "002884fc-7bc8-4583-bffb-e5772413cf14", + "name": "allowed web origins", + "protocol": "openid-connect", + "protocolMapper": "oidc-allowed-origins-mapper", + "consentRequired": false, + "config": { + "access.token.claim": "true", + "introspection.token.claim": "true" + } + } + ] + }, + { + "id": "309d4a2c-208f-4549-a2a5-40f2f0867765", + "name": "profile", + "description": "OpenID Connect built-in scope: profile", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${profileScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "d6153d95-a33a-4c0e-8fa3-a5ea9df103e6", + "name": "gender", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "gender", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "gender", + "jsonType.label": "String" + } + }, + { + "id": "bae8e15c-3a27-4b05-8895-ab073dff8683", + "name": "nickname", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "nickname", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "nickname", + "jsonType.label": "String" + } + }, + { + "id": "913c01af-7409-4e17-b94f-60521ab49a9f", + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "introspection.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "id": "6d6aa12f-1e6b-4d27-9b6d-3e8955881593", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + }, + { + "id": "b527e182-898a-441a-afdf-cc2319aaacc0", + "name": "website", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "website", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "website", + "jsonType.label": "String" + } + }, + { + "id": "c8e7eb48-7055-411c-86a2-36d116454b8b", + "name": "updated at", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "updatedAt", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "updated_at", + "jsonType.label": "long" + } + }, + { + "id": "e014fa8b-a03e-40c4-a174-b2a7a956aa57", + "name": "zoneinfo", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "zoneinfo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "zoneinfo", + "jsonType.label": "String" + } + }, + { + "id": "64e5a635-85a9-42ad-86f2-2d43a72524b2", + "name": "picture", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "picture", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "picture", + "jsonType.label": "String" + } + }, + { + "id": "824d1c93-0d1c-48bc-901a-17d00b74364b", + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + }, + { + "id": "36876d97-38a5-4994-9f13-b92ef4cd7ea9", + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + }, + { + "id": "d998e5f2-7c65-4b68-a8ee-34386fdf404b", + "name": "middle name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "middleName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "middle_name", + "jsonType.label": "String" + } + }, + { + "id": "ebc5d2cd-3442-4cef-8d5b-5146cb5e2824", + "name": "birthdate", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "birthdate", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "birthdate", + "jsonType.label": "String" + } + }, + { + "id": "e6e76b52-b790-44aa-beda-e20e2626cd45", + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "id": "59d693cc-d6e9-452d-96cd-cacf4ead1dc8", + "name": "profile", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "profile", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "profile", + "jsonType.label": "String" + } + } + ] + } + ], + "defaultDefaultClientScopes": [ + "profile", + "role_list", + "web-origins", + "acr", + "roles", + "email" + ], + "defaultOptionalClientScopes": [ + "microprofile-jwt", + "phone", + "offline_access", + "address" + ], + "browserSecurityHeaders": { + "contentSecurityPolicyReportOnly": "", + "xContentTypeOptions": "nosniff", + "referrerPolicy": "no-referrer", + "xRobotsTag": "none", + "xFrameOptions": "SAMEORIGIN", + "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "xXSSProtection": "1; mode=block", + "strictTransportSecurity": "max-age=31536000; includeSubDomains" + }, + "smtpServer": {}, + "eventsEnabled": false, + "eventsListeners": [ + "jboss-logging" + ], + "enabledEventTypes": [], + "adminEventsEnabled": false, + "adminEventsDetailsEnabled": false, + "identityProviders": [], + "identityProviderMappers": [], + "components": { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [ + { + "id": "3526f51d-762e-45dd-b034-7cccbfa7e6c0", + "name": "Consent Required", + "providerId": "consent-required", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "52ce25a4-1a2d-463f-8f88-a46ab1e08995", + "name": "Trusted Hosts", + "providerId": "trusted-hosts", + "subType": "anonymous", + "subComponents": {}, + "config": { + "host-sending-registration-request-must-match": [ + "true" + ], + "client-uris-must-match": [ + "true" + ] + } + }, + { + "id": "78230ed7-c91d-4bf4-993c-d1463105063d", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-usermodel-attribute-mapper", + "oidc-address-mapper", + "oidc-full-name-mapper", + "oidc-usermodel-property-mapper", + "saml-user-attribute-mapper", + "saml-user-property-mapper", + "saml-role-list-mapper", + "oidc-sha256-pairwise-sub-mapper" + ] + } + }, + { + "id": "08f58cfd-4847-4246-bc79-2268dc51305d", + "name": "Max Clients Limit", + "providerId": "max-clients", + "subType": "anonymous", + "subComponents": {}, + "config": { + "max-clients": [ + "200" + ] + } + }, + { + "id": "fb5f3eb5-406f-465c-b3d1-19fd1fb1ffc1", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allow-default-scopes": [ + "true" + ] + } + }, + { + "id": "5ffb837f-8001-42fb-bfa6-c98ae7530b37", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allow-default-scopes": [ + "true" + ] + } + }, + { + "id": "6da6ac13-31e5-4f15-8606-e16d1e834006", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-usermodel-attribute-mapper", + "saml-user-property-mapper", + "oidc-usermodel-property-mapper", + "saml-user-attribute-mapper", + "oidc-address-mapper", + "saml-role-list-mapper", + "oidc-sha256-pairwise-sub-mapper", + "oidc-full-name-mapper" + ] + } + }, + { + "id": "35358f5e-137f-4689-b87f-873f372f2396", + "name": "Full Scope Disabled", + "providerId": "scope", + "subType": "anonymous", + "subComponents": {}, + "config": {} + } + ], + "org.keycloak.userprofile.UserProfileProvider": [ + { + "id": "74e1ebbf-606a-4293-86a2-f9cc0d8e91fb", + "providerId": "declarative-user-profile", + "subComponents": {}, + "config": { + "kc.user.profile.config": [ + "{\"attributes\":[{\"name\":\"username\",\"displayName\":\"${username}\",\"validations\":{\"length\":{\"min\":3,\"max\":255},\"username-prohibited-characters\":{},\"up-username-not-idn-homograph\":{}},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"email\",\"displayName\":\"${email}\",\"validations\":{\"email\":{},\"length\":{\"max\":255}},\"required\":{\"roles\":[\"user\"]},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"firstName\",\"displayName\":\"${firstName}\",\"validations\":{\"length\":{\"max\":255},\"person-name-prohibited-characters\":{}},\"required\":{\"roles\":[\"user\"]},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"lastName\",\"displayName\":\"${lastName}\",\"validations\":{\"length\":{\"max\":255},\"person-name-prohibited-characters\":{}},\"required\":{\"roles\":[\"user\"]},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false}],\"groups\":[{\"name\":\"user-metadata\",\"displayHeader\":\"User metadata\",\"displayDescription\":\"Attributes, which refer to user metadata\"}],\"unmanagedAttributePolicy\":\"ENABLED\"}" + ] + } + } + ], + "org.keycloak.keys.KeyProvider": [ + { + "id": "86b3f7c5-64c9-48ad-9355-fe913ac203c1", + "name": "aes-generated", + "providerId": "aes-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ] + } + }, + { + "id": "1d6fd7c8-76d1-4a9d-9877-60eca8a30336", + "name": "rsa-generated", + "providerId": "rsa-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ] + } + }, + { + "id": "1c57ab36-e4f7-4118-b225-6fdabc10321e", + "name": "rsa-enc-generated", + "providerId": "rsa-enc-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ], + "algorithm": [ + "RSA-OAEP" + ] + } + }, + { + "id": "59724de6-ec76-43de-b8f8-6218085971c9", + "name": "hmac-generated", + "providerId": "hmac-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ], + "algorithm": [ + "HS256" + ] + } + }, + { + "id": "08057a00-49e2-47b3-89c1-ec25f5fdcc2f", + "name": "hmac-generated-hs512", + "providerId": "hmac-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ], + "algorithm": [ + "HS512" + ] + } + } + ] + }, + "internationalizationEnabled": false, + "supportedLocales": [], + "authenticationFlows": [ + { + "id": "b3499093-847b-494f-b0eb-2dcd82c5d9cb", + "alias": "Account verification options", + "description": "Method with which to verity the existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-email-verification", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Verify Existing Account by Re-authentication", + "userSetupAllowed": false + } + ] + }, + { + "id": "dffa8a81-4fde-4b55-ae4f-e002525a2185", + "alias": "Browser - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "dd9679f7-401d-4468-9098-d649fb3f1464", + "alias": "Direct Grant - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "direct-grant-validate-otp", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "112394b4-c201-4faa-8538-c80eeb94746a", + "alias": "First broker login - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "bc5bbc57-1be2-4c9f-9a68-3fe0afda1070", + "alias": "Handle Existing Account", + "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-confirm-link", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Account verification options", + "userSetupAllowed": false + } + ] + }, + { + "id": "682f2aac-0139-4ca0-8d2d-004d658ef2b2", + "alias": "Reset - Conditional OTP", + "description": "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-otp", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "c9b04df1-d208-4a80-8363-cd6023154468", + "alias": "User creation or linking", + "description": "Flow for the existing/non-existing user alternatives", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "create unique user config", + "authenticator": "idp-create-user-if-unique", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Handle Existing Account", + "userSetupAllowed": false + } + ] + }, + { + "id": "313bf938-0fa7-4f8e-a7f9-95ef5f136614", + "alias": "Verify Existing Account by Re-authentication", + "description": "Reauthentication of existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-username-password-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "First broker login - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "a9f14a30-1eb2-46de-b4f0-72a378e82fc5", + "alias": "browser", + "description": "browser based authentication", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-cookie", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-spnego", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "identity-provider-redirector", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 25, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 30, + "autheticatorFlow": true, + "flowAlias": "forms", + "userSetupAllowed": false + } + ] + }, + { + "id": "b0dae209-2a32-44a1-8e44-33fa25b1d7dc", + "alias": "clients", + "description": "Base authentication for clients", + "providerId": "client-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "client-secret", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-jwt", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-secret-jwt", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-x509", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 40, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "b935570a-e80c-433c-a19f-918f9714395e", + "alias": "direct grant", + "description": "OpenID Connect Resource Owner Grant", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "direct-grant-validate-username", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "direct-grant-validate-password", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 30, + "autheticatorFlow": true, + "flowAlias": "Direct Grant - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "aa6af759-eca7-435f-ba9f-b73f3f607b47", + "alias": "docker auth", + "description": "Used by Docker clients to authenticate against the IDP", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "docker-http-basic-authenticator", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "b5572683-b1a9-4c85-935d-dccd1f8aa595", + "alias": "first broker login", + "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "review profile config", + "authenticator": "idp-review-profile", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "User creation or linking", + "userSetupAllowed": false + } + ] + }, + { + "id": "6cc4262d-78c1-4eda-8851-e277c3ac53b4", + "alias": "forms", + "description": "Username, password, otp and other auth forms.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-username-password-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Browser - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "a7c8ab1c-8f6b-4772-b892-242e8cbbe84c", + "alias": "registration", + "description": "registration flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-page-form", + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": true, + "flowAlias": "registration form", + "userSetupAllowed": false + } + ] + }, + { + "id": "a3a22ca3-dc16-4c4d-9bd7-725790fe874c", + "alias": "registration form", + "description": "registration form", + "providerId": "form-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-user-creation", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-password-action", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 50, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-recaptcha-action", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 60, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "0ddae3de-4d23-4325-af3e-ef6058c954e6", + "alias": "reset credentials", + "description": "Reset credentials for a user if they forgot their password or something", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "reset-credentials-choose-user", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-credential-email", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-password", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 40, + "autheticatorFlow": true, + "flowAlias": "Reset - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "d4648fe7-5538-4f23-91cf-6c76e59b641f", + "alias": "saml ecp", + "description": "SAML ECP Profile Authentication Flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "http-basic-authenticator", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + } + ], + "authenticatorConfig": [ + { + "id": "e5b43b21-14cb-44d1-9752-f74729d1a3af", + "alias": "create unique user config", + "config": { + "require.password.update.after.registration": "false" + } + }, + { + "id": "f0a855aa-67e8-4e5d-a65a-d1d5e207ddaa", + "alias": "review profile config", + "config": { + "update.profile.on.first.login": "missing" + } + } + ], + "requiredActions": [ + { + "alias": "CONFIGURE_TOTP", + "name": "Configure OTP", + "providerId": "CONFIGURE_TOTP", + "enabled": true, + "defaultAction": false, + "priority": 10, + "config": {} + }, + { + "alias": "TERMS_AND_CONDITIONS", + "name": "Terms and Conditions", + "providerId": "TERMS_AND_CONDITIONS", + "enabled": false, + "defaultAction": false, + "priority": 20, + "config": {} + }, + { + "alias": "UPDATE_PASSWORD", + "name": "Update Password", + "providerId": "UPDATE_PASSWORD", + "enabled": true, + "defaultAction": false, + "priority": 30, + "config": {} + }, + { + "alias": "UPDATE_PROFILE", + "name": "Update Profile", + "providerId": "UPDATE_PROFILE", + "enabled": true, + "defaultAction": false, + "priority": 40, + "config": {} + }, + { + "alias": "VERIFY_EMAIL", + "name": "Verify Email", + "providerId": "VERIFY_EMAIL", + "enabled": true, + "defaultAction": false, + "priority": 50, + "config": {} + }, + { + "alias": "delete_account", + "name": "Delete Account", + "providerId": "delete_account", + "enabled": false, + "defaultAction": false, + "priority": 60, + "config": {} + }, + { + "alias": "webauthn-register", + "name": "Webauthn Register", + "providerId": "webauthn-register", + "enabled": true, + "defaultAction": false, + "priority": 70, + "config": {} + }, + { + "alias": "webauthn-register-passwordless", + "name": "Webauthn Register Passwordless", + "providerId": "webauthn-register-passwordless", + "enabled": true, + "defaultAction": false, + "priority": 80, + "config": {} + }, + { + "alias": "update_user_locale", + "name": "Update User Locale", + "providerId": "update_user_locale", + "enabled": true, + "defaultAction": false, + "priority": 1000, + "config": {} + } + ], + "browserFlow": "browser", + "registrationFlow": "registration", + "directGrantFlow": "direct grant", + "resetCredentialsFlow": "reset credentials", + "clientAuthenticationFlow": "clients", + "dockerAuthenticationFlow": "docker auth", + "firstBrokerLoginFlow": "first broker login", + "attributes": { + "cibaBackchannelTokenDeliveryMode": "poll", + "cibaAuthRequestedUserHint": "login_hint", + "clientOfflineSessionMaxLifespan": "0", + "oauth2DevicePollingInterval": "5", + "clientSessionIdleTimeout": "0", + "clientOfflineSessionIdleTimeout": "0", + "cibaInterval": "5", + "realmReusableOtpCode": "false", + "cibaExpiresIn": "120", + "oauth2DeviceCodeLifespan": "600", + "parRequestUriLifespan": "60", + "clientSessionMaxLifespan": "0", + "frontendUrl": "", + "acr.loa.map": "{}" + }, + "keycloakVersion": "24.0.2", + "userManagedAccessAllowed": true, + "clientProfiles": { + "profiles": [] + }, + "clientPolicies": { + "policies": [] + }, + "users": [] +} \ No newline at end of file diff --git a/infrastructure/whiteboard-app/files/postgresql/db_init.sh b/infrastructure/whiteboard-app/files/postgresql/db_init.sh new file mode 100644 index 00000000..32886848 --- /dev/null +++ b/infrastructure/whiteboard-app/files/postgresql/db_init.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +set -e +set -u + +function create_user_and_database() { + local database=$1 + echo "Creating user and database '$database'" + + export PGPASSWORD="$POSTGRES_PASSWORD" + psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname=postgres <<-EOSQL + CREATE USER $database; + CREATE DATABASE $database; + GRANT ALL PRIVILEGES ON DATABASE $database TO $database; +EOSQL +} + +if [ -n "$POSTGRES_MULTIPLE_DATABASES" ]; then + echo "Multiple database creation requested: $POSTGRES_MULTIPLE_DATABASES" + for db in $(echo $POSTGRES_MULTIPLE_DATABASES | tr ',' ' '); do + create_user_and_database $db + done + echo "Multiple databases created" +fi \ No newline at end of file diff --git a/infrastructure/whiteboard-app/production.values.yaml b/infrastructure/whiteboard-app/production.values.yaml new file mode 100644 index 00000000..6bfe00a3 --- /dev/null +++ b/infrastructure/whiteboard-app/production.values.yaml @@ -0,0 +1,195 @@ +client: + image: + repository: ghcr.io/aet-devops25/team-server-down/client + tag: latest + pullPolicy: Always + service: + type: ClusterIP + port: 3000 + targetPort: 3000 + replicaCount: 1 + env: + - name: KEYCLOAK_CLIENT_SECRET + value: '{{ .Values.keycloak.clientSecret }}' + - name: NEXTAUTH_URL + value: https://whiteboard.student.k8s.aet.cit.tum.de/api/auth/ + - name: NEXTAUTH_SECRET + value: '{{ .Values.nextauth.secret }}' + - name: KEYCLOAK_CLIENT_ID + value: webclient + - name: KEYCLOAK_ISSUER + value: https://auth.whiteboard.student.k8s.aet.cit.tum.de/realms/production + - name: KEYCLOAK_END_SESSION_ENDPOINT + value: https://auth.whiteboard.student.k8s.aet.cit.tum.de/realms/production/protocol/openid-connect/logout + +server: + image: + repository: ghcr.io/aet-devops25/team-server-down/server + tag: latest + pullPolicy: Always + service: + type: ClusterIP + port: 9091 + targetPort: 9091 + env: + - name: DB_HOST + value: '{{ printf "%s-postgresql" .Release.Name }}' + - name: DB_PORT + value: "5432" + - name: DB_NAME + value: postgres + - name: DB_USER + value: postgres + - name: DB_PASSWORD + value: '{{ .Values.postgresql.auth.postgresPassword }}' + - name: ALLOWED_ORIGIN + value: "https://whiteboard.student.k8s.aet.cit.tum.de" + - name: IDP_INTERNAL_URI + value: "https://auth.whiteboard.student.k8s.aet.cit.tum.de/realms/production" + - name: IDP_EXTERNAL_URI + value: "https://auth.whiteboard.student.k8s.aet.cit.tum.de/realms/production" + - name: SERVER_URL + value: "https://api.whiteboard.student.k8s.aet.cit.tum.de" + replicaCount: 1 + +realtime: + image: + repository: ghcr.io/aet-devops25/team-server-down/realtime + tag: latest + pullPolicy: Always + service: + type: ClusterIP + port: 9090 + targetPort: 9090 + env: + - name: REDIS_HOST + value: '{{ printf "%s-redis-master" .Release.Name }}' + - name: REDIS_PORT + value: "6379" + replicaCount: 1 + +postgresql: + auth: + username: postgres + postgresPassword: "" + primary: + extraEnvVars: + - name: POSTGRES_MULTIPLE_DATABASES + value: "main,keycloak" + extraVolumes: + - name: db-init + configMap: + name: postgresql-configmap + extraVolumeMounts: + - name: db-init + mountPath: /docker-entrypoint-initdb.d + +keycloak: + ingress: + enabled: false + auth: + adminUser: admin + adminPassword: "" + postgresql: + enabled: false + externalDatabase: + host: '{{ printf "%s-postgresql" .Release.Name }}' + user: postgres + password: "" + database: keycloak + port: 5432 + extraEnvVars: + - name: KEYCLOAK_EXTRA_ARGS + value: "--import-realm" + - name: KC_PROXY + value: edge + - name: KC_PROXY_HEADERS + value: xforwarded + - name: KC_HOSTNAME + value: auth.whiteboard.student.k8s.aet.cit.tum.de + extraVolumes: + - name: realm-export + configMap: + name: keycloak-configmap + extraVolumeMounts: + - name: realm-export + mountPath: /opt/bitnami/keycloak/data/import + +genai: + image: + repository: ghcr.io/aet-devops25/team-server-down/genai + tag: latest + pullPolicy: Always + service: + type: ClusterIP + port: 8000 + targetPort: 8000 + replicaCount: 1 + env: + - name: OPEN_WEB_UI_API_KEY + value: '{{ .Values.genai.apiKey }}' + - name: API_URL + value: https://gpu.aet.cit.tum.de/api/chat/completions + - name: SERVER_URL + value: https://api.whiteboard.student.k8s.aet.cit.tum.de + - name: CLIENT_URL + value: https://whiteboard.student.k8s.aet.cit.tum.de + - name: GENAI_URL + value: https://genai.whiteboard.student.k8s.aet.cit.tum + +ingress: + enabled: true + className: "nginx" + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-prod" + nginx.ingress.kubernetes.io/rewrite-target: / + nginx.ingress.kubernetes.io/use-forwarded-headers: "true" + nginx.ingress.kubernetes.io/proxy-buffer-size: "8k" + tls: + hosts: + - "whiteboard.student.k8s.aet.cit.tum.de" + - "api.whiteboard.student.k8s.aet.cit.tum.de" + - "auth.whiteboard.student.k8s.aet.cit.tum.de" + - "genai.whiteboard.student.k8s.aet.cit.tum.de" + secretName: "whiteboard-devops25-tls" + rules: + - host: "whiteboard.student.k8s.aet.cit.tum.de" + paths: + - path: / + pathType: Prefix + service: + name: '{{ printf "%s-client" .Release.Name }}' + port: + number: 3000 + - host: "api.whiteboard.student.k8s.aet.cit.tum.de" + paths: + - path: / + pathType: Prefix + service: + name: '{{ printf "%s-server" .Release.Name }}' + port: + number: 8080 + - host: "auth.whiteboard.student.k8s.aet.cit.tum.de" + paths: + - path: / + pathType: Prefix + service: + name: '{{ printf "%s-keycloak" .Release.Name }}' + port: + number: 80 + - host: "genai.whiteboard.student.k8s.aet.cit.tum.de" + paths: + - path: / + pathType: Prefix + service: + name: '{{ printf "%s-genai" .Release.Name }}' + port: + number: 8000 + - host: 'realtime.whiteboard.student.k8s.aet.cit.tum.de' + paths: + - path: / + pathType: Prefix + service: + name: '{{ printf "%s-realtime" .Release.Name }}' + port: + number: 9090 \ No newline at end of file diff --git a/infrastructure/whiteboard-app/pullrequest.values.yaml b/infrastructure/whiteboard-app/pullrequest.values.yaml new file mode 100644 index 00000000..f27fcb10 --- /dev/null +++ b/infrastructure/whiteboard-app/pullrequest.values.yaml @@ -0,0 +1,199 @@ +client: + image: + repository: ghcr.io/aet-devops25/team-server-down/client + tag: "" + pullPolicy: Always + service: + type: ClusterIP + port: 3000 + targetPort: 3000 + replicaCount: 1 + env: + - name: KEYCLOAK_CLIENT_SECRET + value: '{{ .Values.keycloak.clientSecret }}' + - name: NEXTAUTH_URL + value: 'https://{{ .Values.client.url }}/api/auth/' + - name: NEXTAUTH_SECRET + value: '{{ .Values.nextauth.secret }}' + - name: KEYCLOAK_CLIENT_ID + value: webclient + - name: KEYCLOAK_ISSUER + value: 'https://{{ .Values.auth.url }}/realms/{{ .Values.namespace }}' + - name: KEYCLOAK_END_SESSION_ENDPOINT + value: 'https://{{ .Values.auth.url }}/realms/{{ .Values.namespace }}/protocol/openid-connect/logout' + +server: + image: + repository: ghcr.io/aet-devops25/team-server-down/server + tag: "" + pullPolicy: Always + service: + type: ClusterIP + port: 9091 + targetPort: 9091 + env: + - name: DB_HOST + value: '{{ printf "%s-postgresql" .Release.Name }}' + - name: DB_PORT + value: "5432" + - name: DB_NAME + value: main + - name: DB_USER + value: postgres + - name: DB_PASSWORD + value: '{{ .Values.postgresql.auth.postgresPassword }}' + - name: ALLOWED_ORIGIN + value: 'https://{{ .Values.client.url }}' + - name: IDP_INTERNAL_URI + value: 'https://{{ .Values.auth.url }}/realms/{{ .Values.namespace }}' + - name: IDP_EXTERNAL_URI + value: 'https://{{ .Values.auth.url }}/realms/{{ .Values.namespace }}' + - name: SERVER_URL + value: 'https://{{ .Values.server.url }}' + replicaCount: 1 + +realtime: + image: + repository: ghcr.io/aet-devops25/team-server-down/realtime + tag: "" + pullPolicy: Always + service: + type: ClusterIP + port: 9090 + targetPort: 9090 + env: + - name: REDIS_HOST + value: '{{ printf "%s-redis-master" .Release.Name }}' + - name: REDIS_PORT + value: "6379" + replicaCount: 1 + +postgresql: + auth: + username: postgres + postgresPassword: "" + primary: + extraEnvVars: + - name: POSTGRES_MULTIPLE_DATABASES + value: "main,keycloak" + extraVolumes: + - name: db-init + configMap: + name: postgresql-configmap + extraVolumeMounts: + - name: db-init + mountPath: /docker-entrypoint-initdb.d + +redis: + auth: + enabled: false + +keycloak: + ingress: + enabled: false + auth: + adminUser: admin + adminPassword: "" + postgresql: + enabled: false + externalDatabase: + host: '{{ printf "%s-postgresql" .Release.Name }}' + user: postgres + password: "" + database: keycloak + port: 5432 + extraEnvVars: + - name: KEYCLOAK_EXTRA_ARGS + value: "--import-realm" + - name: KC_PROXY + value: edge + - name: KC_PROXY_HEADERS + value: xforwarded + - name: KC_HOSTNAME + value: '{{ .Values.auth.url }}' + extraVolumes: + - name: realm-export + configMap: + name: keycloak-configmap + extraVolumeMounts: + - name: realm-export + mountPath: /opt/bitnami/keycloak/data/import + +genai: + image: + repository: ghcr.io/aet-devops25/team-server-down/genai + tag: "" + pullPolicy: Always + service: + type: ClusterIP + port: 8000 + targetPort: 8000 + env: + - name: OPEN_WEB_UI_API_KEY + value: '{{ .Values.genai.apiKey }}' + - name: SERVER_URL + value: 'https://{{ .Values.server.url }}' + - name: CLIENT_URL + value: 'https://{{ .Values.client.url }}' + - name: GENAI_URL + value: 'https://{{ .Values.genai.url }}' + - name: API_URL + value: 'https://gpu.aet.cit.tum.de/api/chat/completions' + +ingress: + enabled: true + className: "nginx" + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-prod" + nginx.ingress.kubernetes.io/rewrite-target: / + nginx.ingress.kubernetes.io/use-forwarded-headers: "true" + nginx.ingress.kubernetes.io/proxy-buffer-size: "8k" + tls: + hosts: + - '{{ .Values.client.url }}' + - '{{ .Values.server.url }}' + - '{{ .Values.auth.url }}' + - '{{ .Values.genai.url }}' + - '{{ .Values.realtime.url }}' + secretName: '{{ .Values.namespace }}-whiteboard-devops25-tls' + rules: + - host: '{{ .Values.client.url }}' + paths: + - path: / + pathType: Prefix + service: + name: '{{ printf "%s-client" .Release.Name }}' + port: + number: 3000 + - host: '{{ .Values.server.url }}' + paths: + - path: / + pathType: Prefix + service: + name: '{{ printf "%s-server" .Release.Name }}' + port: + number: 9091 + - host: '{{ .Values.auth.url }}' + paths: + - path: / + pathType: Prefix + service: + name: '{{ printf "%s-keycloak" .Release.Name }}' + port: + number: 80 + - host: '{{ .Values.genai.url }}' + paths: + - path: / + pathType: Prefix + service: + name: '{{ printf "%s-genai" .Release.Name }}' + port: + number: 8000 + - host: '{{ .Values.realtime.url }}' + paths: + - path: / + pathType: Prefix + service: + name: '{{ printf "%s-realtime" .Release.Name }}' + port: + number: 9090 \ No newline at end of file diff --git a/infrastructure/whiteboard-app/staging.values.yaml b/infrastructure/whiteboard-app/staging.values.yaml new file mode 100644 index 00000000..eb98590e --- /dev/null +++ b/infrastructure/whiteboard-app/staging.values.yaml @@ -0,0 +1,200 @@ +client: + image: + repository: ghcr.io/aet-devops25/team-server-down/client + tag: develop + pullPolicy: Always + service: + type: ClusterIP + port: 3000 + targetPort: 3000 + replicaCount: 1 + env: + - name: KEYCLOAK_CLIENT_SECRET + value: '{{ .Values.keycloak.clientSecret }}' + - name: NEXTAUTH_URL + value: https://staging.whiteboard.student.k8s.aet.cit.tum.de/api/auth/ + - name: NEXTAUTH_SECRET + value: '{{ .Values.nextauth.secret }}' + - name: KEYCLOAK_CLIENT_ID + value: webclient + - name: KEYCLOAK_ISSUER + value: https://staging.auth.whiteboard.student.k8s.aet.cit.tum.de/realms/staging + - name: KEYCLOAK_END_SESSION_ENDPOINT + value: https://staging.auth.whiteboard.student.k8s.aet.cit.tum.de/realms/staging/protocol/openid-connect/logout + +server: + image: + repository: ghcr.io/aet-devops25/team-server-down/server + tag: develop + pullPolicy: Always + service: + type: ClusterIP + port: 9091 + targetPort: 9091 + env: + - name: DB_HOST + value: '{{ printf "%s-postgresql" .Release.Name }}' + - name: DB_PORT + value: "5432" + - name: DB_NAME + value: main + - name: DB_USER + value: postgres + - name: DB_PASSWORD + value: '{{ .Values.postgresql.auth.postgresPassword }}' + - name: ALLOWED_ORIGIN + value: "https://staging.whiteboard.student.k8s.aet.cit.tum.de" + - name: IDP_INTERNAL_URI + value: "https://staging.auth.whiteboard.student.k8s.aet.cit.tum.de/realms/staging" + - name: IDP_EXTERNAL_URI + value: "https://staging.auth.whiteboard.student.k8s.aet.cit.tum.de/realms/staging" + - name: SERVER_URL + value: "https://staging.api.whiteboard.student.k8s.aet.cit.tum.de" + replicaCount: 1 + +realtime: + image: + repository: ghcr.io/aet-devops25/team-server-down/realtime + tag: develop + pullPolicy: Always + service: + type: ClusterIP + port: 9090 + targetPort: 9090 + env: + - name: REDIS_HOST + value: '{{ printf "%s-redis-master" .Release.Name }}' + - name: REDIS_PORT + value: "6379" + replicaCount: 1 + +postgresql: + auth: + username: postgres + postgresPassword: "" + primary: + extraEnvVars: + - name: POSTGRES_MULTIPLE_DATABASES + value: "main,keycloak" + extraVolumes: + - name: db-init + configMap: + name: postgresql-configmap + extraVolumeMounts: + - name: db-init + mountPath: /docker-entrypoint-initdb.d + +keycloak: + ingress: + enabled: false + auth: + adminUser: admin + adminPassword: "" + postgresql: + enabled: false + externalDatabase: + host: '{{ printf "%s-postgresql" .Release.Name }}' + user: postgres + password: "" + database: keycloak + port: 5432 + extraEnvVars: + - name: KEYCLOAK_EXTRA_ARGS + value: "--import-realm" + - name: KC_PROXY + value: edge + - name: KC_PROXY_HEADERS + value: xforwarded + - name: KC_HOSTNAME + value: staging.auth.whiteboard.student.k8s.aet.cit.tum.de + extraVolumes: + - name: realm-export + configMap: + name: keycloak-configmap + extraVolumeMounts: + - name: realm-export + mountPath: /opt/bitnami/keycloak/data/import + +redis: + auth: + enabled: false + +genai: + image: + repository: ghcr.io/aet-devops25/team-server-down/genai + tag: develop + pullPolicy: Always + service: + type: ClusterIP + port: 8000 + targetPort: 8000 + replicaCount: 1 + env: + - name: OPEN_WEB_UI_API_KEY + value: '{{ .Values.genai.apiKey }}' + - name: API_URL + value: https://gpu.aet.cit.tum.de/api/chat/completions + - name: SERVER_URL + value: https://staging.api.whiteboard.student.k8s.aet.cit.tum.de + - name: CLIENT_URL + value: https://staging.whiteboard.student.k8s.aet.cit.tum + - name: GENAI_URL + value: https://staging.genai.whiteboard.student.k8s.aet.cit + +ingress: + enabled: true + className: "nginx" + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-prod" + nginx.ingress.kubernetes.io/rewrite-target: / + nginx.ingress.kubernetes.io/use-forwarded-headers: "true" + nginx.ingress.kubernetes.io/proxy-buffer-size: "8k" + tls: + hosts: + - "staging.whiteboard.student.k8s.aet.cit.tum.de" + - "staging.api.whiteboard.student.k8s.aet.cit.tum.de" + - "staging.auth.whiteboard.student.k8s.aet.cit.tum.de" + - "staging.realtime.whiteboard.student.k8s.aet.cit.tum.de" + - "staging.genai.whiteboard.student.k8s.aet.cit.tum.de" + secretName: "staging-whiteboard-devops25-tls" + rules: + - host: "staging.whiteboard.student.k8s.aet.cit.tum.de" + paths: + - path: / + pathType: Prefix + service: + name: '{{ printf "%s-client" .Release.Name }}' + port: + number: 3000 + - host: "staging.api.whiteboard.student.k8s.aet.cit.tum.de" + paths: + - path: / + pathType: Prefix + service: + name: '{{ printf "%s-server" .Release.Name }}' + port: + number: 9091 + - host: "staging.auth.whiteboard.student.k8s.aet.cit.tum.de" + paths: + - path: / + pathType: Prefix + service: + name: '{{ printf "%s-keycloak" .Release.Name }}' + port: + number: 80 + - host: "staging.genai.whiteboard.student.k8s.aet.cit.tum.de" + paths: + - path: / + pathType: Prefix + service: + name: '{{ printf "%s-genai" .Release.Name }}' + port: + number: 8000 + - host: 'staging.realtime.whiteboard.student.k8s.aet.cit.tum.de' + paths: + - path: / + pathType: Prefix + service: + name: '{{ printf "%s-realtime" .Release.Name }}' + port: + number: 9090 \ No newline at end of file diff --git a/infrastructure/whiteboard-app/templates/client-deployment.yaml b/infrastructure/whiteboard-app/templates/client-deployment.yaml new file mode 100644 index 00000000..940eb3e4 --- /dev/null +++ b/infrastructure/whiteboard-app/templates/client-deployment.yaml @@ -0,0 +1,34 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: whiteboard-client +spec: + replicas: {{ .Values.client.replicaCount }} + selector: + matchLabels: + app: whiteboard-client-selector + template: + metadata: + annotations: + deploy-timestamp: "{{ now }}" + labels: + app: whiteboard-client-selector + spec: + containers: + - name: client + image: "{{ .Values.client.image.repository }}:{{ .Values.client.image.tag }}" + imagePullPolicy: {{ .Values.client.image.pullPolicy }} + resources: + limits: + cpu: "500m" + memory: "256Mi" + requests: + cpu: "50m" + memory: "50Mi" + ports: + - containerPort: {{ .Values.client.service.targetPort }} + env: + {{- range .Values.client.env }} + - name: {{ .name }} + value: {{ tpl .value $ | quote }} + {{- end }} diff --git a/infrastructure/whiteboard-app/templates/client-service.yaml b/infrastructure/whiteboard-app/templates/client-service.yaml new file mode 100644 index 00000000..a5cd6027 --- /dev/null +++ b/infrastructure/whiteboard-app/templates/client-service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }}-client +spec: + type: {{ .Values.client.service.type }} + selector: + app: whiteboard-client-selector + ports: + - port: {{ .Values.client.service.port }} + targetPort: {{ .Values.client.service.targetPort }} + protocol: TCP + diff --git a/infrastructure/whiteboard-app/templates/genai-deployment.yaml b/infrastructure/whiteboard-app/templates/genai-deployment.yaml new file mode 100644 index 00000000..f1c40e2c --- /dev/null +++ b/infrastructure/whiteboard-app/templates/genai-deployment.yaml @@ -0,0 +1,34 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: whiteboard-genai +spec: + replicas: {{ .Values.genai.replicaCount }} + selector: + matchLabels: + app: whiteboard-genai-selector + template: + metadata: + annotations: + deploy-timestamp: "{{ now }}" + labels: + app: whiteboard-genai-selector + spec: + containers: + - name: genai + image: "{{ .Values.genai.image.repository }}:{{ .Values.genai.image.tag }}" + imagePullPolicy: {{ .Values.genai.image.pullPolicy }} + resources: + limits: + cpu: "500m" + memory: "256Mi" + requests: + cpu: "50m" + memory: "50Mi" + ports: + - containerPort: {{ .Values.genai.service.targetPort }} + env: + {{- range .Values.genai.env }} + - name: {{ .name }} + value: {{ tpl .value $ | quote }} + {{- end }} diff --git a/infrastructure/whiteboard-app/templates/genai-service.yaml b/infrastructure/whiteboard-app/templates/genai-service.yaml new file mode 100644 index 00000000..3ba92fc8 --- /dev/null +++ b/infrastructure/whiteboard-app/templates/genai-service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }}-genai +spec: + type: {{ .Values.genai.service.type }} + selector: + app: whiteboard-genai-selector + ports: + - port: {{ .Values.genai.service.port }} + targetPort: {{ .Values.genai.service.targetPort }} + protocol: TCP diff --git a/infrastructure/whiteboard-app/templates/ingress.yaml b/infrastructure/whiteboard-app/templates/ingress.yaml new file mode 100644 index 00000000..b722793c --- /dev/null +++ b/infrastructure/whiteboard-app/templates/ingress.yaml @@ -0,0 +1,34 @@ +{{- if .Values.ingress.enabled -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: "whiteboard-ingress" + {{- $annotations := .Values.ingress.annotations | default dict }} + {{- if $annotations }} + annotations: + {{- toYaml $annotations | nindent 4 }} + {{- end }} +spec: + tls: + - hosts: + {{- range .Values.ingress.tls.hosts }} + - {{ tpl . $ }} + {{- end }} + secretName: {{ tpl .Values.ingress.tls.secretName $ }} + ingressClassName: nginx + rules: + {{- range .Values.ingress.rules }} + - host: {{ tpl .host $ }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + pathType: {{ .pathType }} + backend: + service: + name: {{ tpl .service.name $ | quote }} + port: + number: {{ .service.port.number }} + {{- end}} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/infrastructure/whiteboard-app/templates/keycloak-configmap.yaml b/infrastructure/whiteboard-app/templates/keycloak-configmap.yaml new file mode 100644 index 00000000..99f73e44 --- /dev/null +++ b/infrastructure/whiteboard-app/templates/keycloak-configmap.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: keycloak-configmap +data: + realm-export.json: |- +{{- $realmJson := .Files.Get "files/keycloak/realm-export.json" }} +{{ tpl $realmJson . | indent 4 }} \ No newline at end of file diff --git a/infrastructure/whiteboard-app/templates/postgresql-configmap.yaml b/infrastructure/whiteboard-app/templates/postgresql-configmap.yaml new file mode 100644 index 00000000..d8ff3f89 --- /dev/null +++ b/infrastructure/whiteboard-app/templates/postgresql-configmap.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: postgresql-configmap +data: + db_init.sh: |- +{{ .Files.Get "files/postgresql/db_init.sh" | indent 4 }} \ No newline at end of file diff --git a/infrastructure/whiteboard-app/templates/realtime-deployment.yaml b/infrastructure/whiteboard-app/templates/realtime-deployment.yaml new file mode 100644 index 00000000..60852c6c --- /dev/null +++ b/infrastructure/whiteboard-app/templates/realtime-deployment.yaml @@ -0,0 +1,34 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: whiteboard-realtime +spec: + replicas: {{ .Values.realtime.replicaCount }} + selector: + matchLabels: + app: whiteboard-realtime-selector + template: + metadata: + annotations: + deploy-timestamp: "{{ now }}" + labels: + app: whiteboard-realtime-selector + spec: + containers: + - name: realtime + image: "{{ .Values.realtime.image.repository }}:{{ .Values.realtime.image.tag }}" + imagePullPolicy: {{ .Values.realtime.image.pullPolicy }} + resources: + limits: + cpu: "500m" + memory: "256Mi" + requests: + cpu: "50m" + memory: "50Mi" + ports: + - containerPort: {{ .Values.realtime.service.targetPort }} + env: + {{- range .Values.realtime.env }} + - name: {{ .name }} + value: {{ tpl .value $ | quote }} + {{- end }} \ No newline at end of file diff --git a/infrastructure/whiteboard-app/templates/realtime-service.yaml b/infrastructure/whiteboard-app/templates/realtime-service.yaml new file mode 100644 index 00000000..2bfbc8e2 --- /dev/null +++ b/infrastructure/whiteboard-app/templates/realtime-service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }}-realtime +spec: + selector: + app: whiteboard-realtime-selector + ports: + - port: 9090 + targetPort: 9090 + protocol: TCP + type: {{ .Values.realtime.service.type }} \ No newline at end of file diff --git a/infrastructure/whiteboard-app/templates/server-deployment.yaml b/infrastructure/whiteboard-app/templates/server-deployment.yaml new file mode 100644 index 00000000..0cc31d77 --- /dev/null +++ b/infrastructure/whiteboard-app/templates/server-deployment.yaml @@ -0,0 +1,34 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: whiteboard-server +spec: + replicas: {{ .Values.server.replicaCount }} + selector: + matchLabels: + app: whiteboard-server-selector + template: + metadata: + annotations: + deploy-timestamp: "{{ now }}" + labels: + app: whiteboard-server-selector + spec: + containers: + - name: server + image: "{{ .Values.server.image.repository }}:{{ .Values.server.image.tag }}" + imagePullPolicy: {{ .Values.server.image.pullPolicy }} + resources: + limits: + cpu: "500m" + memory: "512Mi" + requests: + cpu: "200m" + memory: "256Mi" + ports: + - containerPort: {{ .Values.server.service.targetPort }} + env: + {{- range .Values.server.env }} + - name: {{ .name }} + value: {{ tpl .value $ | quote }} + {{- end }} diff --git a/infrastructure/whiteboard-app/templates/server-service.yaml b/infrastructure/whiteboard-app/templates/server-service.yaml new file mode 100644 index 00000000..9a6f1829 --- /dev/null +++ b/infrastructure/whiteboard-app/templates/server-service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }}-server +spec: + selector: + app: whiteboard-server-selector + ports: + - port: {{ .Values.server.service.port }} + targetPort: {{ .Values.server.service.targetPort }} + protocol: TCP + type: {{ .Values.server.service.type }} \ No newline at end of file diff --git a/infrastructure/whiteboard-observability/.helmignore b/infrastructure/whiteboard-observability/.helmignore new file mode 100644 index 00000000..0e8a0eb3 --- /dev/null +++ b/infrastructure/whiteboard-observability/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/infrastructure/whiteboard-observability/Chart.lock b/infrastructure/whiteboard-observability/Chart.lock new file mode 100644 index 00000000..afdfd431 --- /dev/null +++ b/infrastructure/whiteboard-observability/Chart.lock @@ -0,0 +1,12 @@ +dependencies: +- name: grafana + repository: https://grafana.github.io/helm-charts + version: 9.2.10 +- name: prometheus + repository: https://prometheus-community.github.io/helm-charts + version: 27.28.0 +- name: mailhog + repository: https://codecentric.github.io/helm-charts + version: 5.8.0 +digest: sha256:b1512e01eadc92c03de33e061fdf24f4ba22e910166fbe57fd8805f0ffca1e9d +generated: "2025-07-20T13:30:29.991761+02:00" diff --git a/infrastructure/whiteboard-observability/Chart.yaml b/infrastructure/whiteboard-observability/Chart.yaml new file mode 100644 index 00000000..78717bdf --- /dev/null +++ b/infrastructure/whiteboard-observability/Chart.yaml @@ -0,0 +1,16 @@ +apiVersion: v2 +name: whiteboard-observability +description: A Helm chart for Kubernetes +type: application +version: 0.1.0 +appVersion: "1.16.0" +dependencies: + - name: grafana + version: 9.2.10 + repository: "https://grafana.github.io/helm-charts" + - name: prometheus + version: 27.28.0 + repository: "https://prometheus-community.github.io/helm-charts" + - name: mailhog + version: 5.8.0 + repository: https://codecentric.github.io/helm-charts diff --git a/infrastructure/whiteboard-observability/charts/grafana-9.2.10.tgz b/infrastructure/whiteboard-observability/charts/grafana-9.2.10.tgz new file mode 100644 index 00000000..d9cbb75e Binary files /dev/null and b/infrastructure/whiteboard-observability/charts/grafana-9.2.10.tgz differ diff --git a/infrastructure/whiteboard-observability/charts/mailhog-5.8.0.tgz b/infrastructure/whiteboard-observability/charts/mailhog-5.8.0.tgz new file mode 100644 index 00000000..9cdfd149 Binary files /dev/null and b/infrastructure/whiteboard-observability/charts/mailhog-5.8.0.tgz differ diff --git a/infrastructure/whiteboard-observability/charts/prometheus-27.28.0.tgz b/infrastructure/whiteboard-observability/charts/prometheus-27.28.0.tgz new file mode 100644 index 00000000..bf979b48 Binary files /dev/null and b/infrastructure/whiteboard-observability/charts/prometheus-27.28.0.tgz differ diff --git a/infrastructure/whiteboard-observability/files/grafana/provisioning/dashboards/dashboards.yml b/infrastructure/whiteboard-observability/files/grafana/provisioning/dashboards/dashboards.yml new file mode 100644 index 00000000..0e8f7220 --- /dev/null +++ b/infrastructure/whiteboard-observability/files/grafana/provisioning/dashboards/dashboards.yml @@ -0,0 +1,23 @@ +apiVersion: 1 + +providers: + - name: "Server System Metrics Dashboard" + type: file + editable: true + updateIntervalSeconds: 10 + options: + path: /etc/grafana/provisioning/dashboards/server-dashboard.json + + - name: "GenAi System Metrics Dashboard" + type: file + editable: true + updateIntervalSeconds: 10 + options: + path: /etc/grafana/provisioning/dashboards/genai-dashboard.json + + - name: "Realtime System Metrics Dashboard" + type: file + editable: true + updateIntervalSeconds: 10 + options: + path: /etc/grafana/provisioning/dashboards/realtime-dashboard.json \ No newline at end of file diff --git a/infrastructure/whiteboard-observability/files/grafana/provisioning/dashboards/genai-dashboard.json b/infrastructure/whiteboard-observability/files/grafana/provisioning/dashboards/genai-dashboard.json new file mode 100644 index 00000000..bc3a1fc6 --- /dev/null +++ b/infrastructure/whiteboard-observability/files/grafana/provisioning/dashboards/genai-dashboard.json @@ -0,0 +1,522 @@ +{ + "annotations":{ + "list":[ + { + "builtIn":1, + "datasource":{ + "type":"grafana", + "uid":"-- Grafana --" + }, + "enable":true, + "hide":true, + "iconColor":"rgba(0, 211, 255, 1)", + "name":"Annotations & Alerts", + "type":"dashboard" + }, + { + "datasource":{ + "type":"prometheus", + "uid":"PBFA97CFB590B2093" + }, + "enable":true, + "hide":false, + "iconColor":"orange", + "name":"Client Error Spike", + "target":{ + "expr":"sum(rate(http_requests_total{status=~\"4xx\"}[5m])) > 0.1", + "interval":"", + "refId":"Anno" + }, + "textFormat":"High rate of client errors detected, exceeding 0.1 requests per second", + "titleFormat":"Client Error Spike" + }, + { + "datasource":{ + "type":"prometheus", + "uid":"PBFA97CFB590B2093" + }, + "enable":true, + "hide":false, + "iconColor":"red", + "name":"Server Error Spike", + "tagKeys":"server, error", + "target":{ + "expr":"sum(rate(http_requests_total{status=~\"5xx\"}[5m])) > 0.1", + "interval":"", + "refId":"Anno" + }, + "textFormat":"Server error rate exceeded threshold (>0.1 req/s)", + "titleFormat":"Server Error Spike" + } + ] + }, + "description":"Dashboard showing system metrics including request count, latency, and error rate.", + "editable":true, + "fiscalYearStartMonth":0, + "graphTooltip":0, + "id":3, + "links":[ + + ], + "panels":[ + { + "datasource":{ + "type":"prometheus", + "uid":"PBFA97CFB590B2093" + }, + "fieldConfig":{ + "defaults":{ + "color":{ + "mode":"palette-classic" + }, + "custom":{ + "axisBorderShow":false, + "axisCenteredZero":false, + "axisColorMode":"text", + "axisLabel":"", + "axisPlacement":"auto", + "barAlignment":0, + "barWidthFactor":0.6, + "drawStyle":"line", + "fillOpacity":20, + "gradientMode":"none", + "hideFrom":{ + "legend":false, + "tooltip":false, + "viz":false + }, + "insertNulls":false, + "lineInterpolation":"smooth", + "lineWidth":2, + "pointSize":5, + "scaleDistribution":{ + "type":"linear" + }, + "showPoints":"auto", + "spanNulls":false, + "stacking":{ + "group":"A", + "mode":"none" + }, + "thresholdsStyle":{ + "mode":"area" + } + }, + "mappings":[ + + ], + "thresholds":{ + "mode":"absolute", + "steps":[ + { + "color":"green" + }, + { + "color":"orange", + "value":70 + }, + { + "color":"red", + "value":80 + } + ] + } + }, + "overrides":[ + + ] + }, + "gridPos":{ + "h":17, + "w":12, + "x":0, + "y":0 + }, + "id":1, + "options":{ + "legend":{ + "calcs":[ + "mean", + "max" + ], + "displayMode":"table", + "placement":"bottom", + "showLegend":true + }, + "tooltip":{ + "hideZeros":false, + "mode":"multi", + "sort":"none" + } + }, + "pluginVersion":"12.0.2", + "targets":[ + { + "datasource":{ + "type":"prometheus", + "uid":"PBFA97CFB590B2093" + }, + "editorMode":"code", + "expr":"sum by (handler, method, status) (increase(http_requests_total{job=\"genai_job\"}[5m]))", + "legendFormat":"{{method}} {{status}} {{uri}}", + "range":true, + "refId":"A" + } + ], + "title":"Request Count", + "type":"timeseries" + }, + { + "datasource":{ + "type":"prometheus", + "uid":"PBFA97CFB590B2093" + }, + "fieldConfig":{ + "defaults":{ + "color":{ + "mode":"palette-classic" + }, + "custom":{ + "axisBorderShow":false, + "axisCenteredZero":false, + "axisColorMode":"text", + "axisLabel":"Average Latency (seconds)", + "axisPlacement":"auto", + "barAlignment":0, + "barWidthFactor":0.6, + "drawStyle":"line", + "fillOpacity":20, + "gradientMode":"none", + "hideFrom":{ + "legend":false, + "tooltip":false, + "viz":false + }, + "insertNulls":false, + "lineInterpolation":"smooth", + "lineWidth":2, + "pointSize":5, + "scaleDistribution":{ + "type":"linear" + }, + "showPoints":"auto", + "spanNulls":false, + "stacking":{ + "group":"A", + "mode":"none" + }, + "thresholdsStyle":{ + "mode":"area" + } + }, + "mappings":[ + + ], + "thresholds":{ + "mode":"absolute", + "steps":[ + { + "color":"green" + }, + { + "color":"red", + "value":80 + } + ] + } + }, + "overrides":[ + + ] + }, + "gridPos":{ + "h":17, + "w":12, + "x":12, + "y":0 + }, + "id":2, + "options":{ + "legend":{ + "calcs":[ + "mean", + "max" + ], + "displayMode":"table", + "placement":"bottom", + "showLegend":true + }, + "tooltip":{ + "hideZeros":false, + "mode":"multi", + "sort":"none" + } + }, + "pluginVersion":"12.0.2", + "targets":[ + { + "datasource":{ + "type":"prometheus", + "uid":"PBFA97CFB590B2093" + }, + "editorMode":"code", + "expr":"sum(rate(http_request_duration_seconds_sum{job=\"genai_job\"}[5m])) by (method, handler) /\nsum(rate(http_request_duration_seconds_count{job=\"genai_job\"}[5m])) by (method, handler)", + "legendFormat":"{{method}} {{handler}}", + "range":true, + "refId":"A" + } + ], + "title":"Latency", + "type":"timeseries" + }, + { + "datasource":{ + "type":"prometheus", + "uid":"PBFA97CFB590B2093" + }, + "fieldConfig":{ + "defaults":{ + "color":{ + "mode":"palette-classic" + }, + "custom":{ + "axisBorderShow":false, + "axisCenteredZero":false, + "axisColorMode":"text", + "axisLabel":"", + "axisPlacement":"auto", + "barAlignment":0, + "barWidthFactor":0.6, + "drawStyle":"line", + "fillOpacity":0, + "gradientMode":"none", + "hideFrom":{ + "legend":false, + "tooltip":false, + "viz":false + }, + "insertNulls":false, + "lineInterpolation":"linear", + "lineWidth":1, + "pointSize":5, + "scaleDistribution":{ + "type":"linear" + }, + "showPoints":"auto", + "spanNulls":false, + "stacking":{ + "group":"A", + "mode":"none" + }, + "thresholdsStyle":{ + "mode":"off" + } + }, + "mappings":[ + + ], + "thresholds":{ + "mode":"absolute", + "steps":[ + { + "color":"green" + }, + { + "color":"red", + "value":80 + } + ] + } + }, + "overrides":[ + + ] + }, + "gridPos":{ + "h":12, + "w":12, + "x":0, + "y":17 + }, + "id":4, + "options":{ + "legend":{ + "calcs":[ + + ], + "displayMode":"list", + "placement":"bottom", + "showLegend":true + }, + "tooltip":{ + "hideZeros":false, + "mode":"single", + "sort":"none" + } + }, + "pluginVersion":"12.0.2", + "targets":[ + { + "editorMode":"code", + "expr":"sum by (handler, method) (increase(http_requests_total{status=~\"4..\", job=\"genai_job\"}[5m]))", + "legendFormat":"{{label_name}}", + "range":true, + "refId":"A" + }, + { + "datasource":{ + "type":"prometheus", + "uid":"PBFA97CFB590B2093" + }, + "editorMode":"code", + "expr":"sum by (handler, method) (increase(http_requests_total{status=~\"4..\", job=\"genai_job\"}[5m]))", + "hide":false, + "instant":false, + "legendFormat":"__auto", + "range":true, + "refId":"B" + } + ], + "title":"Errors", + "type":"timeseries" + }, + { + "datasource":{ + "type":"prometheus", + "uid":"PBFA97CFB590B2093" + }, + "description":"This dashboard displays the average number of input and output tokens generated per request for each operation (completion, summarization, rephrase_text) over time. The values represent the mean input and output token count, calculated every 5 minutes, grouped by operation.", + "fieldConfig":{ + "defaults":{ + "color":{ + "mode":"palette-classic" + }, + "custom":{ + "axisBorderShow":false, + "axisCenteredZero":false, + "axisColorMode":"text", + "axisLabel":"", + "axisPlacement":"auto", + "barAlignment":0, + "barWidthFactor":0.6, + "drawStyle":"line", + "fillOpacity":0, + "gradientMode":"none", + "hideFrom":{ + "legend":false, + "tooltip":false, + "viz":false + }, + "insertNulls":false, + "lineInterpolation":"linear", + "lineWidth":1, + "pointSize":5, + "scaleDistribution":{ + "type":"linear" + }, + "showPoints":"auto", + "spanNulls":false, + "stacking":{ + "group":"A", + "mode":"none" + }, + "thresholdsStyle":{ + "mode":"off" + } + }, + "mappings":[ + + ], + "thresholds":{ + "mode":"absolute", + "steps":[ + { + "color":"green" + }, + { + "color":"red", + "value":80 + } + ] + } + }, + "overrides":[ + + ] + }, + "gridPos":{ + "h":12, + "w":12, + "x":12, + "y":17 + }, + "id":5, + "options":{ + "legend":{ + "calcs":[ + + ], + "displayMode":"list", + "placement":"bottom", + "showLegend":true + }, + "tooltip":{ + "hideZeros":false, + "mode":"single", + "sort":"none" + } + }, + "pluginVersion":"12.0.2", + "targets":[ + { + "editorMode":"code", + "exemplar":false, + "expr":"rate(llm_token_count_sum{type=\"output\"}[5m]) \n/\nrate(llm_token_count_count{type=\"output\"}[5m])", + "format":"time_series", + "instant":false, + "legendFormat":"output - {{operation}}", + "range":true, + "refId":"A" + }, + { + "datasource":{ + "type":"prometheus", + "uid":"PBFA97CFB590B2093" + }, + "editorMode":"code", + "expr":"rate(llm_token_count_sum{type=\"input\"}[5m]) \n/\nrate(llm_token_count_count{type=\"input\"}[5m])", + "hide":false, + "instant":false, + "legendFormat":"input - {{operation}}", + "range":true, + "refId":"B" + } + ], + "title":"LLM API Token Analytics", + "type":"timeseries" + } + ], + "preload":false, + "refresh":"10s", + "schemaVersion":41, + "tags":[ + "monitoring", + "alerts" + ], + "templating":{ + "list":[ + + ] + }, + "time":{ + "from":"now-6h", + "to":"now" + }, + "timepicker":{ + + }, + "timezone":"browser", + "title":"GenAi System Metrics Dashboard", + "uid":"genai-metrics-dashboard1111", + "version":2 +} diff --git a/infrastructure/whiteboard-observability/files/grafana/provisioning/dashboards/realtime-dashboard.json b/infrastructure/whiteboard-observability/files/grafana/provisioning/dashboards/realtime-dashboard.json new file mode 100644 index 00000000..fac0edbf --- /dev/null +++ b/infrastructure/whiteboard-observability/files/grafana/provisioning/dashboards/realtime-dashboard.json @@ -0,0 +1,593 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "enable": true, + "hide": false, + "iconColor": "red", + "name": "WebSocket error", + "tagKeys": "websocket_errors", + "target": { + "expr": "(\n rate(websocket_read_errors[5m]) +\n rate(websocket_write_errors[5m]) +\n rate(websocket_upgrade_errors[5m])\n) > 0.1", + "interval": "", + "refId": "Anno" + }, + "textFormat": "High WebSocket error rate detected - exceeding 0.1 errors per second", + "titleFormat": "WebSocket Error Rate" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 10, + "x": 0, + "y": 0 + }, + "id": 5, + "options": { + "minVizHeight": 75, + "minVizWidth": 75, + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "sizing": "auto" + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "editorMode": "code", + "expr": "websocket_connections_active", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Websocket Connections Active", + "type": "gauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 15, + "w": 14, + "x": 10, + "y": 0 + }, + "id": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "editorMode": "code", + "expr": "rate(websocket_connection_duration_sum[5m]) / rate(websocket_connection_duration_count[5m])", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Connection Duration Summary", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": [ + "histogram_quantile(0.95, sum(rate(websocket_connection_duration_bucket[5m])) by (le))" + ], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [] + } + ] + }, + "gridPos": { + "h": 11, + "w": 10, + "x": 0, + "y": 8 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(websocket_sent_messages[1m])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "legendFormat": "Messages Sent", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "rate(websocket_received_messages[1m])", + "hide": false, + "instant": false, + "legendFormat": "Messages Received", + "range": true, + "refId": "B" + } + ], + "title": "Messages Sent/Received Rate", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "description": "Distribution of WebSocket connection durations across different time buckets. Each bar represents a cumulative count of connections that lasted less than or equal to the specified duration (in seconds). For example, '60s' shows connections lasting up to 60 seconds, '120s' shows connections up to 120 seconds, and so on up to '+Inf' (unlimited duration).", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "Value" + }, + "properties": [ + { + "id": "noValue" + } + ] + } + ] + }, + "gridPos": { + "h": 17, + "w": 14, + "x": 10, + "y": 15 + }, + "id": 4, + "options": { + "displayMode": "gradient", + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "maxVizHeight": 300, + "minVizHeight": 16, + "minVizWidth": 8, + "namePlacement": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showUnfilled": true, + "sizing": "auto", + "valueMode": "color" + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "disableTextWrap": false, + "editorMode": "code", + "exemplar": false, + "expr": "websocket_connection_duration_bucket{le=~\".+\"}", + "format": "table", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": true, + "legendFormat": "{{le}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Websocket Connection Duration", + "transformations": [ + { + "id": "filterFieldsByName", + "options": { + "include": { + "names": [ + "Time", + "60.0", + "120.0", + "300.0", + "600.0", + "900.0", + "1200.0", + "1800.0", + "+Inf" + ] + } + } + } + ], + "type": "bargauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [ + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": [ + "websocket_read_errors" + ], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": false, + "tooltip": false, + "viz": true + } + } + ] + } + ] + }, + "gridPos": { + "h": 13, + "w": 10, + "x": 0, + "y": 19 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "editorMode": "code", + "expr": "rate(websocket_read_errors[1m])", + "legendFormat": "websocket_read_errors", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "rate(websocket_write_errors[1m])", + "hide": false, + "instant": false, + "legendFormat": "websocket_write_errors", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "rate(websocket_upgrade_errors[1m])", + "hide": false, + "instant": false, + "legendFormat": "websocket_upgrade_errors", + "range": true, + "refId": "C" + } + ], + "title": "Websocket Error Rates", + "type": "timeseries" + } + ], + "preload": false, + "schemaVersion": 41, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-3h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Realtime System Metrics", + "uid": "275dd5ce-c9b5-4ed9-a6d0-cbac478716a1", + "version": 4 +} \ No newline at end of file diff --git a/infrastructure/whiteboard-observability/files/grafana/provisioning/dashboards/server-dashboard.json b/infrastructure/whiteboard-observability/files/grafana/provisioning/dashboards/server-dashboard.json new file mode 100644 index 00000000..caa6f15f --- /dev/null +++ b/infrastructure/whiteboard-observability/files/grafana/provisioning/dashboards/server-dashboard.json @@ -0,0 +1,394 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "enable": true, + "hide": false, + "iconColor": "orange", + "name": "Client Error Spike", + "tagKeys": "client-error, http-4xx", + "target": { + "expr": "sum(rate(http_server_requests_seconds_count{status=~\"4..\"}[5m])) > 0.1", + "interval": "", + "refId": "Anno" + }, + "textFormat": "High rate of client errors detected, exceeding 0.1 requests per second", + "titleFormat": "Client Error Spike" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "enable": true, + "hide": false, + "iconColor": "red", + "name": "Server Error Spike", + "tagKeys": "server, error", + "target": { + "expr": "sum(rate(http_server_requests_seconds_count{status=~\"5..\"}[5m])) > 0.1", + "interval": "", + "refId": "Anno" + }, + "textFormat": "Server error rate exceeded threshold (>0.1 req/s)", + "titleFormat": "Server Error Spike" + } + ] + }, + "description": "Dashboard showing system metrics including request count, latency, and error rate.", + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "smooth", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "area" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "orange", + "value": 70 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 17, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 1, + "options": { + "legend": { + "calcs": [ + "mean", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "sum(rate(http_server_requests_seconds_count[5m])) by (method, uri)", + "legendFormat": "{{method}} {{status}} {{uri}}", + "range": true, + "refId": "A" + } + ], + "title": "Request Count", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "Average Latency (seconds)", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "smooth", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "area" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 17, + "w": 12, + "x": 12, + "y": 0 + }, + "id": 2, + "options": { + "legend": { + "calcs": [ + "mean", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": " sum(rate(http_server_requests_seconds_sum[5m])) by (method, uri) /\nsum(rate(http_server_requests_seconds_count[5m])) by (method, uri)", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Latency", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 12, + "w": 12, + "x": 0, + "y": 17 + }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.0.2", + "targets": [ + { + "editorMode": "code", + "expr": "sum(rate(http_server_requests_seconds_count{outcome=\"CLIENT_ERROR\"}[5m])) by (method, uri, status)", + "legendFormat": "__auto", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "PBFA97CFB590B2093" + }, + "editorMode": "code", + "expr": "sum(rate(http_server_requests_seconds_count{status=~\"5..\"}[5m])) by (method, uri, status)", + "hide": false, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "B" + } + ], + "title": "Errors", + "type": "timeseries" + } + ], + "preload": false, + "refresh": "10s", + "schemaVersion": 41, + "tags": [ + "monitoring", + "alerts" + ], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Server System Metrics Dashboard", + "uid": "system-metrics-dashboard111112", + "version": 1 +} \ No newline at end of file diff --git a/infrastructure/whiteboard-observability/files/grafana/provisioning/datasources/prometheus.yml b/infrastructure/whiteboard-observability/files/grafana/provisioning/datasources/prometheus.yml new file mode 100644 index 00000000..20ab3d19 --- /dev/null +++ b/infrastructure/whiteboard-observability/files/grafana/provisioning/datasources/prometheus.yml @@ -0,0 +1,8 @@ +apiVersion: 1 + +datasources: + - name: Prometheus + type: prometheus + access: proxy + url: '{{ printf "http://%s-prometheus-server" .Release.Name }}' + isDefault: true \ No newline at end of file diff --git a/infrastructure/whiteboard-observability/files/prometheus/alert.rules.yml b/infrastructure/whiteboard-observability/files/prometheus/alert.rules.yml new file mode 100644 index 00000000..502c3530 --- /dev/null +++ b/infrastructure/whiteboard-observability/files/prometheus/alert.rules.yml @@ -0,0 +1,11 @@ +groups: + - name: service-availability + rules: + - alert: Service Down + expr: up{job=~"server_job|genai_job|realtime_job"} == 0 + for: 1m + labels: + severity: critical + annotations: + summary: "Service {{ $labels.job }} is down" + description: "Service {{ $labels.job }} on {{ $labels.instance }} has been down for more than 1 minute" \ No newline at end of file diff --git a/infrastructure/whiteboard-observability/files/prometheus/prometheus.yml b/infrastructure/whiteboard-observability/files/prometheus/prometheus.yml new file mode 100644 index 00000000..c6f4c4d5 --- /dev/null +++ b/infrastructure/whiteboard-observability/files/prometheus/prometheus.yml @@ -0,0 +1,32 @@ +global: + scrape_interval: 15s + evaluation_interval: 15s + +alerting: + alertmanagers: + - static_configs: + - targets: + - '{{ printf "%s-alertmanager" .Release.Name }}:9093' + +rule_files: + - "/etc/prometheus/alert.rules.yml" + +scrape_configs: + - job_name: 'server_job' + metrics_path: '/actuator/prometheus' + scheme: https + static_configs: + - targets: + - '{{ .Values.server.url }}' + + - job_name: 'genai_job' + metrics_path: '/metrics' + static_configs: + - targets: + - '{{ .Values.genai.url }}' + + - job_name: 'realtime_job' + metrics_path: '/metrics' + static_configs: + - targets: + - '{{ .Values.realtime.url }}' \ No newline at end of file diff --git a/infrastructure/whiteboard-observability/production.values.yaml b/infrastructure/whiteboard-observability/production.values.yaml new file mode 100644 index 00000000..f3ab5686 --- /dev/null +++ b/infrastructure/whiteboard-observability/production.values.yaml @@ -0,0 +1,92 @@ +grafana: + service: + port: 3000 + rbac: + create: false + namespaced: true + adminUser: admin + adminPassword: admin + extraVolumes: + - name: grafana-datasources-config + configMap: + name: grafana-datasources-configmap + - name: grafana-dashboards-config + configMap: + name: grafana-dashboards-configmap + extraVolumeMounts: + - name: grafana-datasources-config + mountPath: /etc/grafana/provisioning/datasources + - name: grafana-dashboards-config + mountPath: /etc/grafana/provisioning/dashboards + +prometheus: + rbac: + create: false + kube-state-metrics: + enabled: false + prometheus-node-exporter: + enabled: false + server: + extraVolumes: + - name: prometheus-config + configMap: + name: prometheus-configmap + extraVolumeMounts: + - name: prometheus-config + mountPath: /etc/prometheus + defaultFlagsOverride: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--storage.tsdb.path=/prometheus' + - '--web.console.libraries=/etc/prometheus/console_libraries' + - '--web.console.templates=/etc/prometheus/consoles' + - '--web.enable-lifecycle' + alertmanager: + config: + global: + smtp_smarthost: 'whiteboard-observability-production-mailhog:1025' + smtp_from: 'alertmanager@whiteboard.student.k8s.aet.cit.tum.de' + smtp_require_tls: false + route: + receiver: 'mailhog-alerts' + group_by: [ 'alertname' ] + group_wait: 10s + group_interval: 1m + repeat_interval: 30m + receivers: + - name: 'mailhog-alerts' + email_configs: + - to: 'teamserverdown@whiteboard.student.k8s.aet.cit.tum.de' + from: 'alertmanager@whiteboard.student.k8s.aet.cit.tum.de' + smarthost: 'whiteboard-observability-production-mailhog:1025' + send_resolved: true + +ingress: + enabled: true + className: "nginx" + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-prod" + nginx.ingress.kubernetes.io/rewrite-target: / + nginx.ingress.kubernetes.io/use-forwarded-headers: "true" + nginx.ingress.kubernetes.io/proxy-buffer-size: "8k" + tls: + hosts: + - '{{ .Values.metrics.url }}' + - "mailhog.whiteboard.student.k8s.aet.cit.tum.de" + secretName: '{{ .Values.namespace }}-whiteboard-observability-devops25-tls' + rules: + - host: '{{ .Values.metrics.url }}' + paths: + - path: / + pathType: Prefix + service: + name: '{{ printf "%s-grafana" .Release.Name }}' + port: + number: 3000 + - host: "mailhog.whiteboard.student.k8s.aet.cit.tum.de" + paths: + - path: / + pathType: Prefix + service: + name: '{{ printf "%s-mailhog" .Release.Name }}' + port: + number: 8025 \ No newline at end of file diff --git a/infrastructure/whiteboard-observability/staging.values.yaml b/infrastructure/whiteboard-observability/staging.values.yaml new file mode 100644 index 00000000..e772c1bb --- /dev/null +++ b/infrastructure/whiteboard-observability/staging.values.yaml @@ -0,0 +1,92 @@ +grafana: + service: + port: 3000 + rbac: + create: false + namespaced: true + adminUser: admin + adminPassword: admin + extraVolumes: + - name: grafana-datasources-config + configMap: + name: grafana-datasources-configmap + - name: grafana-dashboards-config + configMap: + name: grafana-dashboards-configmap + extraVolumeMounts: + - name: grafana-datasources-config + mountPath: /etc/grafana/provisioning/datasources + - name: grafana-dashboards-config + mountPath: /etc/grafana/provisioning/dashboards + +prometheus: + rbac: + create: false + kube-state-metrics: + enabled: false + prometheus-node-exporter: + enabled: false + server: + extraVolumes: + - name: prometheus-config + configMap: + name: prometheus-configmap + extraVolumeMounts: + - name: prometheus-config + mountPath: /etc/prometheus + defaultFlagsOverride: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--storage.tsdb.path=/prometheus' + - '--web.console.libraries=/etc/prometheus/console_libraries' + - '--web.console.templates=/etc/prometheus/consoles' + - '--web.enable-lifecycle' + alertmanager: + config: + global: + smtp_smarthost: 'whiteboard-observability-staging-mailhog:1025' + smtp_from: 'alertmanager@staging.whiteboard.student.k8s.aet.cit.tum.de' + smtp_require_tls: false + route: + receiver: 'mailhog-alerts' + group_by: [ 'alertname' ] + group_wait: 10s + group_interval: 1m + repeat_interval: 30m + receivers: + - name: 'mailhog-alerts' + email_configs: + - to: 'teamserverdown@staging.whiteboard.student.k8s.aet.cit.tum.de' + from: 'alertmanager@staging.whiteboard.student.k8s.aet.cit.tum.de' + smarthost: 'whiteboard-observability-staging-mailhog:1025' + send_resolved: true + +ingress: + enabled: true + className: "nginx" + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-prod" + nginx.ingress.kubernetes.io/rewrite-target: / + nginx.ingress.kubernetes.io/use-forwarded-headers: "true" + nginx.ingress.kubernetes.io/proxy-buffer-size: "8k" + tls: + hosts: + - '{{ .Values.metrics.url }}' + - "staging.mailhog.whiteboard.student.k8s.aet.cit.tum.de" + secretName: '{{ .Values.namespace }}-whiteboard-observability-devops25-tls' + rules: + - host: '{{ .Values.metrics.url }}' + paths: + - path: / + pathType: Prefix + service: + name: '{{ printf "%s-grafana" .Release.Name }}' + port: + number: 3000 + - host: "staging.mailhog.whiteboard.student.k8s.aet.cit.tum.de" + paths: + - path: / + pathType: Prefix + service: + name: '{{ printf "%s-mailhog" .Release.Name }}' + port: + number: 8025 \ No newline at end of file diff --git a/infrastructure/whiteboard-observability/templates/grafana-configmap.yaml b/infrastructure/whiteboard-observability/templates/grafana-configmap.yaml new file mode 100644 index 00000000..3ce9f9b0 --- /dev/null +++ b/infrastructure/whiteboard-observability/templates/grafana-configmap.yaml @@ -0,0 +1,24 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: grafana-datasources-configmap +data: + prometheus.yml: |- +{{- $dataSourceConfig := .Files.Get "files/grafana/provisioning/datasources/prometheus.yml" }} +{{ tpl $dataSourceConfig . | indent 4 }} + +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: grafana-dashboards-configmap +data: + dashboards.yml: |- +{{- $dashboardsConfig := .Files.Get "files/grafana/provisioning/dashboards/dashboards.yml" }} +{{ tpl $dashboardsConfig . | indent 4 }} + server-dashboard.json: |- +{{ .Files.Get "files/grafana/provisioning/dashboards/server-dashboard.json" | indent 4 }} + genai-dashboard.json: |- +{{ .Files.Get "files/grafana/provisioning/dashboards/genai-dashboard.json" | indent 4 }} + realtime-dashboard.json: |- +{{ .Files.Get "files/grafana/provisioning/dashboards/realtime-dashboard.json" | indent 4 }} \ No newline at end of file diff --git a/infrastructure/whiteboard-observability/templates/ingress.yaml b/infrastructure/whiteboard-observability/templates/ingress.yaml new file mode 100644 index 00000000..f7616cd6 --- /dev/null +++ b/infrastructure/whiteboard-observability/templates/ingress.yaml @@ -0,0 +1,34 @@ +{{- if .Values.ingress.enabled -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: "whiteboard-observability-ingress" + {{- $annotations := .Values.ingress.annotations | default dict }} + {{- if $annotations }} + annotations: + {{- toYaml $annotations | nindent 4 }} + {{- end }} +spec: + tls: + - hosts: + {{- range .Values.ingress.tls.hosts }} + - {{ tpl . $ }} + {{- end }} + secretName: {{ tpl .Values.ingress.tls.secretName $ }} + ingressClassName: nginx + rules: + {{- range .Values.ingress.rules }} + - host: {{ tpl .host $ }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + pathType: {{ .pathType }} + backend: + service: + name: {{ tpl .service.name $ | quote }} + port: + number: {{ .service.port.number }} + {{- end}} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/infrastructure/whiteboard-observability/templates/prometheus-configmap.yaml b/infrastructure/whiteboard-observability/templates/prometheus-configmap.yaml new file mode 100644 index 00000000..bd09c3ca --- /dev/null +++ b/infrastructure/whiteboard-observability/templates/prometheus-configmap.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: prometheus-configmap +data: + prometheus.yml: |- +{{- $config := .Files.Get "files/prometheus/prometheus.yml" }} +{{ tpl $config . | indent 4 }} + alert.rules.yml: |- +{{ .Files.Get "files/prometheus/alert.rules.yml" | indent 4 }} \ No newline at end of file diff --git a/realtime/Dockerfile b/realtime/Dockerfile new file mode 100644 index 00000000..424c8167 --- /dev/null +++ b/realtime/Dockerfile @@ -0,0 +1,18 @@ +FROM golang:1.24-alpine AS builder + +WORKDIR /app + +COPY go.mod go.sum ./ +RUN go mod download + +COPY . . +ENV CGO_ENABLED=0 +RUN go build -o realtime ./cmd/api/main.go + +FROM scratch AS runner + +WORKDIR /app + +COPY --from=builder /app/realtime . + +ENTRYPOINT [ "./realtime" ] \ No newline at end of file diff --git a/realtime/Dockerfile.local b/realtime/Dockerfile.local new file mode 100644 index 00000000..c039ba88 --- /dev/null +++ b/realtime/Dockerfile.local @@ -0,0 +1,12 @@ +FROM golang:1.24-alpine as development + +WORKDIR /app + +RUN go install github.com/air-verse/air@latest + +COPY go.mod go.sum ./ +RUN go mod download + +COPY . . + +ENTRYPOINT ["air", "--build.cmd", "go build -o build/realtime ./cmd/api/main.go", "--build.bin", "./build/realtime"] \ No newline at end of file diff --git a/realtime/Makefile b/realtime/Makefile new file mode 100644 index 00000000..d53220ed --- /dev/null +++ b/realtime/Makefile @@ -0,0 +1,43 @@ +SHELL := /bin/bash + +.PHONY: all build test deps deps-cleancache + +GOCMD=go +BUILD_DIR=build +BINARY_DIR=$(BUILD_DIR)/bin +CODE_COVERAGE=code-coverage + +all: test build + +${BINARY_DIR}: + mkdir -p $(BINARY_DIR) + +build: ${BINARY_DIR} ## Compile the code, build Executable File + $(GOCMD) build -o $(BINARY_DIR) -v ./cmd/api + +run: ## Start application + $(GOCMD) run ./cmd/api + +test: ## Run tests + $(GOCMD) test ./... -cover + +test-coverage: ## Run tests and generate coverage file + $(GOCMD) test ./... -coverprofile=$(CODE_COVERAGE).out + $(GOCMD) tool cover -html=$(CODE_COVERAGE).out + +deps: ## Install dependencies + $(GOCMD) get -u -t -d -v ./... + $(GOCMD) mod tidy + $(GOCMD) mod vendor + +deps-cleancache: ## Clear cache in Go module + $(GOCMD) clean -modcache + +wire: ## Generate wire_gen.go + cd pkg/di && wire + +swag: ## Generate swagger docs + swag init -g ./cmd/api/main.go -o ./cmd/api/docs + +help: ## Display this help screen + @grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' \ No newline at end of file diff --git a/genai/app/services/__init__.py b/realtime/README.md similarity index 100% rename from genai/app/services/__init__.py rename to realtime/README.md diff --git a/realtime/cmd/api/docs/docs.go b/realtime/cmd/api/docs/docs.go new file mode 100644 index 00000000..569d1c6a --- /dev/null +++ b/realtime/cmd/api/docs/docs.go @@ -0,0 +1,64 @@ +// Package docs Code generated by swaggo/swag. DO NOT EDIT +package docs + +import "github.com/swaggo/swag" + +const docTemplate = `{ + "schemes": {{ marshal .Schemes }}, + "swagger": "2.0", + "info": { + "description": "{{escape .Description}}", + "title": "{{.Title}}", + "contact": {}, + "version": "{{.Version}}" + }, + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", + "paths": { + "/": { + "get": { + "tags": [ + "root" + ], + "summary": "Get Root", + "operationId": "GetRoot", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/handler.RootResponse" + } + } + } + } + } + }, + "definitions": { + "handler.RootResponse": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + } + } +}` + +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = &swag.Spec{ + Version: "", + Host: "", + BasePath: "", + Schemes: []string{}, + Title: "", + Description: "", + InfoInstanceName: "swagger", + SwaggerTemplate: docTemplate, + LeftDelim: "{{", + RightDelim: "}}", +} + +func init() { + swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) +} diff --git a/realtime/cmd/api/docs/swagger.json b/realtime/cmd/api/docs/swagger.json new file mode 100644 index 00000000..ecdb8a1b --- /dev/null +++ b/realtime/cmd/api/docs/swagger.json @@ -0,0 +1,35 @@ +{ + "swagger": "2.0", + "info": { + "contact": {} + }, + "paths": { + "/": { + "get": { + "tags": [ + "root" + ], + "summary": "Get Root", + "operationId": "GetRoot", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/handler.RootResponse" + } + } + } + } + } + }, + "definitions": { + "handler.RootResponse": { + "type": "object", + "properties": { + "message": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/realtime/cmd/api/docs/swagger.yaml b/realtime/cmd/api/docs/swagger.yaml new file mode 100644 index 00000000..a2d3446f --- /dev/null +++ b/realtime/cmd/api/docs/swagger.yaml @@ -0,0 +1,21 @@ +definitions: + handler.RootResponse: + properties: + message: + type: string + type: object +info: + contact: {} +paths: + /: + get: + operationId: GetRoot + responses: + "200": + description: OK + schema: + $ref: '#/definitions/handler.RootResponse' + summary: Get Root + tags: + - root +swagger: "2.0" diff --git a/realtime/cmd/api/main.go b/realtime/cmd/api/main.go new file mode 100644 index 00000000..d6c1ecea --- /dev/null +++ b/realtime/cmd/api/main.go @@ -0,0 +1,21 @@ +package main + +import ( + "github.com/AET-DevOps25/team-server-down/pkg/config" + "github.com/AET-DevOps25/team-server-down/pkg/di" + "log" +) + +func main() { + cfg, configErr := config.LoadConfig() + if configErr != nil { + log.Fatal("cannot load config: ", configErr) + } + + server, diErr := di.InitializeAPI(cfg) + if diErr != nil { + log.Fatal("cannot start server: ", diErr) + } + + server.Start() +} diff --git a/realtime/go.mod b/realtime/go.mod new file mode 100644 index 00000000..dc18154a --- /dev/null +++ b/realtime/go.mod @@ -0,0 +1,72 @@ +module github.com/AET-DevOps25/team-server-down + +go 1.24.0 + +require ( + github.com/gin-gonic/gin v1.10.1 + github.com/go-playground/validator/v10 v10.27.0 + github.com/google/wire v0.6.0 + github.com/gorilla/websocket v1.5.3 + github.com/redis/go-redis/v9 v9.11.0 + github.com/spf13/viper v1.20.1 + github.com/stretchr/testify v1.10.0 + github.com/swaggo/files v1.0.1 + github.com/swaggo/gin-swagger v1.6.0 + github.com/swaggo/swag v1.16.4 +) + +require ( + github.com/KyleBanks/depth v1.2.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/bytedance/sonic v1.13.3 // indirect + github.com/bytedance/sonic/loader v0.2.4 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cloudwego/base64x v0.1.5 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.9 // indirect + github.com/gin-contrib/sse v1.1.0 // indirect + github.com/go-openapi/jsonpointer v0.21.1 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/spec v0.21.0 // indirect + github.com/go-openapi/swag v0.23.1 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-viper/mapstructure/v2 v2.2.1 // indirect + github.com/goccy/go-json v0.10.5 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.11 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mailru/easyjson v0.9.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_golang v1.22.0 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.62.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect + github.com/sagikazarmark/locafero v0.7.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.14.0 // indirect + github.com/spf13/cast v1.8.0 // indirect + github.com/spf13/pflag v1.0.6 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.3.0 // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.9.0 // indirect + golang.org/x/arch v0.18.0 // indirect + golang.org/x/crypto v0.40.0 // indirect + golang.org/x/net v0.42.0 // indirect + golang.org/x/sys v0.34.0 // indirect + golang.org/x/text v0.27.0 // indirect + golang.org/x/tools v0.35.0 // indirect + google.golang.org/protobuf v1.36.6 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/realtime/go.sum b/realtime/go.sum new file mode 100644 index 00000000..5c224bc6 --- /dev/null +++ b/realtime/go.sum @@ -0,0 +1,220 @@ +github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= +github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/bytedance/sonic v1.13.3 h1:MS8gmaH16Gtirygw7jV91pDCN33NyMrPbN7qiYhEsF0= +github.com/bytedance/sonic v1.13.3/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= +github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= +github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY= +github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok= +github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4= +github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk= +github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= +github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= +github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ= +github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic= +github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY= +github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= +github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= +github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4= +github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= +github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= +github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= +github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= +github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI= +github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.11 h1:0OwqZRYI2rFrjS4kvkDnqJkKHdHaRnCm68/DY4OxRzU= +github.com/klauspost/cpuid/v2 v2.2.11/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= +github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/redis/go-redis/v9 v9.11.0 h1:E3S08Gl/nJNn5vkxd2i78wZxWAPNZgUNTp8WIJUAiIs= +github.com/redis/go-redis/v9 v9.11.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= +github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA= +github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo= +github.com/spf13/cast v1.8.0 h1:gEN9K4b8Xws4EX0+a0reLmhq8moKn7ntRlQYgjPeCDk= +github.com/spf13/cast v1.8.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= +github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE= +github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg= +github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M= +github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo= +github.com/swaggo/swag v1.16.4 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A= +github.com/swaggo/swag v1.16.4/go.mod h1:VBsHJRsDvfYvqoiMKnsdwhNV9LEMHgEDZcyVYX0sxPg= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA= +github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +golang.org/x/arch v0.18.0 h1:WN9poc33zL4AzGxqf8VtpKUnGvMi8O9lhNyBMF/85qc= +golang.org/x/arch v0.18.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= +golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg= +golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= +golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= +golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= +golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= +golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= +golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= diff --git a/realtime/pkg/api/handler/root.go b/realtime/pkg/api/handler/root.go new file mode 100644 index 00000000..83bb369b --- /dev/null +++ b/realtime/pkg/api/handler/root.go @@ -0,0 +1,29 @@ +package handler + +import ( + "net/http" + + "github.com/gin-gonic/gin" +) + +type RootHandler struct{} + +type RootResponse struct { + Message string `json:"message"` +} + +func NewRootHandler() *RootHandler { + return &RootHandler{} +} + +// GetRoot FindAll godoc +// @Summary Get Root +// @Tags root +// @ID GetRoot +// @Product JSON +// @Success 200 {object} RootResponse +// @Router / [get] +func (rh *RootHandler) GetRoot(c *gin.Context) { + response := RootResponse{Message: "Hello World"} + c.JSON(http.StatusOK, response) +} diff --git a/realtime/pkg/api/handler/root_test.go b/realtime/pkg/api/handler/root_test.go new file mode 100644 index 00000000..95631561 --- /dev/null +++ b/realtime/pkg/api/handler/root_test.go @@ -0,0 +1,30 @@ +package handler_test + +import ( + "github.com/AET-DevOps25/team-server-down/pkg/api/handler" + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/assert" + "net/http" + "net/http/httptest" + "testing" +) + +func TestRootHandler(t *testing.T) { + gin.SetMode(gin.TestMode) + + rh := handler.NewRootHandler() + router := gin.New() + router.GET("/", rh.GetRoot) + + req, err := http.NewRequest(http.MethodGet, "/", nil) + assert.NoError(t, err) + + rec := httptest.NewRecorder() + + router.ServeHTTP(rec, req) + + assert.Equal(t, http.StatusOK, rec.Code) + + expectedJSON := `{"message":"Hello World"}` + assert.JSONEq(t, expectedJSON, rec.Body.String()) +} diff --git a/realtime/pkg/api/handler/whiteboard.go b/realtime/pkg/api/handler/whiteboard.go new file mode 100644 index 00000000..ac5fafb1 --- /dev/null +++ b/realtime/pkg/api/handler/whiteboard.go @@ -0,0 +1,138 @@ +package handler + +import ( + "context" + "github.com/AET-DevOps25/team-server-down/pkg/api/metrics" + "github.com/AET-DevOps25/team-server-down/pkg/mq" + "github.com/gin-gonic/gin" + "github.com/gorilla/websocket" + "log" + "net/http" + "time" +) + +type WhiteboardHandler struct { + mq *mq.RedisMQ + metrics *metrics.Metrics +} + +func NewWhiteboardHandler(redisMQ *mq.RedisMQ, metrics *metrics.Metrics) *WhiteboardHandler { + return &WhiteboardHandler{ + redisMQ, + metrics, + } +} + +var upgrader = websocket.Upgrader{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, + CheckOrigin: func(r *http.Request) bool { + return true + }, +} + +func (wh *WhiteboardHandler) GetWhiteboardEvents(c *gin.Context) { + whiteboardId := c.Param("whiteboardId") + + conn, err := upgrader.Upgrade(c.Writer, c.Request, nil) + if err != nil { + log.Printf("WebSocket upgrade failed: %v", err) + wh.metrics.WebsocketUpgradeErrors.Inc() + return + } + defer conn.Close() + + wh.metrics.WebsocketConnectionsActive.Inc() + defer wh.metrics.WebsocketConnectionsActive.Dec() + + start := time.Now() + defer func() { + duration := time.Since(start).Seconds() + wh.metrics.WebsocketConnectionDuration.Observe(duration) + }() + + ctx, cancel := context.WithCancel(c.Request.Context()) + defer cancel() + + // Close the connection when client disconnects + go func() { + for { + if _, _, err := conn.NextReader(); err != nil { + cancel() + return + } + } + }() + + msgCh := make(chan []byte, 100) + + // Subscribe to whiteboard events + go func() { + err := wh.mq.Subscribe(whiteboardId, func(key, value string) { + if key != whiteboardId { + return + } + select { + case msgCh <- []byte(value): + case <-ctx.Done(): + return + } + }) + if err != nil && ctx.Err() == nil { + cancel() + } + }() + + // Stream messages to the WebSocket client + for { + select { + case <-ctx.Done(): + return + case msg, ok := <-msgCh: + if !ok { + return + } + if err := conn.WriteMessage(websocket.TextMessage, msg); err != nil { + wh.metrics.WebsocketWriteErrors.Inc() + cancel() + return + } + wh.metrics.WebsocketSentMessages.Inc() + } + } +} + +func (wh *WhiteboardHandler) PublishWhiteboardEvents(c *gin.Context) { + whiteboardId := c.Param("whiteboardId") + + conn, err := upgrader.Upgrade(c.Writer, c.Request, nil) + if err != nil { + log.Printf("WebSocket upgrade failed: %v", err) + wh.metrics.WebsocketUpgradeErrors.Inc() + return + } + defer conn.Close() + + wh.metrics.WebsocketConnectionsActive.Inc() + defer wh.metrics.WebsocketConnectionsActive.Dec() + + start := time.Now() + defer func() { + duration := time.Since(start).Seconds() + wh.metrics.WebsocketConnectionDuration.Observe(duration) + }() + + for { + _, message, err := conn.ReadMessage() + if err != nil { + wh.metrics.WebsocketReadErrors.Inc() + break + } + wh.metrics.WebsocketReceivedMessages.Inc() + + err = wh.mq.Publish(whiteboardId, string(message)) + if err != nil { + log.Printf("Failed to publish message: %v", err) + } + } +} diff --git a/realtime/pkg/api/metrics/metrics.go b/realtime/pkg/api/metrics/metrics.go new file mode 100644 index 00000000..1b6c197d --- /dev/null +++ b/realtime/pkg/api/metrics/metrics.go @@ -0,0 +1,66 @@ +package metrics + +import "github.com/prometheus/client_golang/prometheus" + +type Metrics struct { + WebsocketConnectionsActive prometheus.Gauge + WebsocketConnectionDuration prometheus.Histogram + WebsocketUpgradeErrors prometheus.Counter + WebsocketReadErrors prometheus.Counter + WebsocketWriteErrors prometheus.Counter + WebsocketSentMessages prometheus.Counter + WebsocketReceivedMessages prometheus.Counter +} + +func NewMetrics(reg *prometheus.Registry) *Metrics { + m := &Metrics{ + WebsocketConnectionsActive: prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "websocket_connections_active", + Help: "Number of active websocket connections", + }), + WebsocketConnectionDuration: prometheus.NewHistogram(prometheus.HistogramOpts{ + Name: "websocket_connection_duration", + Help: "Duration of websocket connections", + Buckets: []float64{ + 60, // 1 min + 120, // 2 min + 300, // 5 min + 600, // 10 min + 900, // 15 min + 1200, // 20 min + 1800, // 30 min + }, + }), + WebsocketUpgradeErrors: prometheus.NewCounter(prometheus.CounterOpts{ + Name: "websocket_upgrade_errors", + Help: "Number of websocket upgrade errors", + }), + WebsocketReadErrors: prometheus.NewCounter(prometheus.CounterOpts{ + Name: "websocket_read_errors", + Help: "Number of websocket read errors", + }), + WebsocketWriteErrors: prometheus.NewCounter(prometheus.CounterOpts{ + Name: "websocket_write_errors", + Help: "Number of websocket write errors", + }), + WebsocketSentMessages: prometheus.NewCounter(prometheus.CounterOpts{ + Name: "websocket_sent_messages", + Help: "Number of sent websocket messages", + }), + WebsocketReceivedMessages: prometheus.NewCounter(prometheus.CounterOpts{ + Name: "websocket_received_messages", + Help: "Number of received websocket messages", + }), + } + + reg.MustRegister( + m.WebsocketConnectionsActive, + m.WebsocketConnectionDuration, + m.WebsocketUpgradeErrors, + m.WebsocketReadErrors, + m.WebsocketWriteErrors, + m.WebsocketSentMessages, + m.WebsocketReceivedMessages, + ) + return m +} diff --git a/realtime/pkg/api/metrics/provider.go b/realtime/pkg/api/metrics/provider.go new file mode 100644 index 00000000..a9839105 --- /dev/null +++ b/realtime/pkg/api/metrics/provider.go @@ -0,0 +1,11 @@ +package metrics + +import "github.com/prometheus/client_golang/prometheus" + +func ProvideRegistry() *prometheus.Registry { + return prometheus.NewRegistry() +} + +func ProvideMetrics(reg *prometheus.Registry) *Metrics { + return NewMetrics(reg) +} diff --git a/realtime/pkg/api/server.go b/realtime/pkg/api/server.go new file mode 100644 index 00000000..a78ad579 --- /dev/null +++ b/realtime/pkg/api/server.go @@ -0,0 +1,38 @@ +package http + +import ( + _ "github.com/AET-DevOps25/team-server-down/cmd/api/docs" + "github.com/AET-DevOps25/team-server-down/pkg/api/handler" + "github.com/gin-gonic/gin" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" + swaggerFiles "github.com/swaggo/files" + ginSwagger "github.com/swaggo/gin-swagger" +) + +type Server struct { + engine *gin.Engine +} + +func NewServer( + rootHandler *handler.RootHandler, + whiteboardHandler *handler.WhiteboardHandler, + reg *prometheus.Registry, +) *Server { + engine := gin.New() + + engine.Use(gin.Logger(), gin.Recovery()) + + engine.GET("/", rootHandler.GetRoot) + engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) + + engine.GET("/metrics", gin.WrapH(promhttp.HandlerFor(reg, promhttp.HandlerOpts{}))) + + engine.GET("/ws/whiteboard/:whiteboardId/subscribe", whiteboardHandler.GetWhiteboardEvents) + engine.GET("/ws/whiteboard/:whiteboardId/publish", whiteboardHandler.PublishWhiteboardEvents) + return &Server{engine: engine} +} + +func (s *Server) Start() { + s.engine.Run(":9090") +} diff --git a/realtime/pkg/config/config.go b/realtime/pkg/config/config.go new file mode 100644 index 00000000..7ba4c036 --- /dev/null +++ b/realtime/pkg/config/config.go @@ -0,0 +1,39 @@ +package config + +import ( + "github.com/go-playground/validator/v10" + "github.com/spf13/viper" +) + +type Config struct { + REDIS_HOST string `mapstructure:"REDIS_HOST"` + REDIS_PORT string `mapstructure:"REDIS_PORT"` +} + +var envs = []string{ + "REDIS_HOST", "REDIS_PORT", +} + +func LoadConfig() (Config, error) { + var config Config + + viper.AddConfigPath("./") + viper.SetConfigFile(".env") + viper.ReadInConfig() + + for _, env := range envs { + if err := viper.BindEnv(env); err != nil { + return config, err + } + } + + if err := viper.Unmarshal(&config); err != nil { + return config, err + } + + if err := validator.New().Struct(&config); err != nil { + return config, err + } + + return config, nil +} diff --git a/realtime/pkg/di/wire.go b/realtime/pkg/di/wire.go new file mode 100644 index 00000000..684ba831 --- /dev/null +++ b/realtime/pkg/di/wire.go @@ -0,0 +1,26 @@ +//go:build wireinject +// +build wireinject + +package di + +import ( + http "github.com/AET-DevOps25/team-server-down/pkg/api" + "github.com/AET-DevOps25/team-server-down/pkg/api/handler" + "github.com/AET-DevOps25/team-server-down/pkg/api/metrics" + "github.com/AET-DevOps25/team-server-down/pkg/config" + "github.com/AET-DevOps25/team-server-down/pkg/mq" + "github.com/google/wire" +) + +func InitializeAPI(cfg config.Config) (*http.Server, error) { + wire.Build( + http.NewServer, + handler.NewRootHandler, + handler.NewWhiteboardHandler, + mq.NewRedisMQ, + metrics.ProvideRegistry, + metrics.ProvideMetrics, + ) + + return &http.Server{}, nil +} diff --git a/realtime/pkg/di/wire_gen.go b/realtime/pkg/di/wire_gen.go new file mode 100644 index 00000000..736234d1 --- /dev/null +++ b/realtime/pkg/di/wire_gen.go @@ -0,0 +1,27 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate go run -mod=mod github.com/google/wire/cmd/wire +//go:build !wireinject +// +build !wireinject + +package di + +import ( + "github.com/AET-DevOps25/team-server-down/pkg/api" + "github.com/AET-DevOps25/team-server-down/pkg/api/handler" + "github.com/AET-DevOps25/team-server-down/pkg/api/metrics" + "github.com/AET-DevOps25/team-server-down/pkg/config" + "github.com/AET-DevOps25/team-server-down/pkg/mq" +) + +// Injectors from wire.go: + +func InitializeAPI(cfg config.Config) (*http.Server, error) { + rootHandler := handler.NewRootHandler() + redisMQ := mq.NewRedisMQ(cfg) + registry := metrics.ProvideRegistry() + metricsMetrics := metrics.ProvideMetrics(registry) + whiteboardHandler := handler.NewWhiteboardHandler(redisMQ, metricsMetrics) + server := http.NewServer(rootHandler, whiteboardHandler, registry) + return server, nil +} diff --git a/realtime/pkg/mq/redis.go b/realtime/pkg/mq/redis.go new file mode 100644 index 00000000..c8888b4a --- /dev/null +++ b/realtime/pkg/mq/redis.go @@ -0,0 +1,38 @@ +package mq + +import ( + "context" + "github.com/AET-DevOps25/team-server-down/pkg/config" + "github.com/redis/go-redis/v9" +) + +type RedisMQ struct { + client *redis.Client +} +type RedisMessageHandler func(channel, payload string) + +func NewRedisMQ(cfg config.Config) *RedisMQ { + return &RedisMQ{ + client: redis.NewClient(&redis.Options{ + Addr: cfg.REDIS_HOST + ":" + cfg.REDIS_PORT, + }), + } +} + +func (mq *RedisMQ) Subscribe(channel string, handler RedisMessageHandler) error { + ctx := context.Background() + pubsub := mq.client.Subscribe(ctx, channel) + ch := pubsub.Channel() + + go func() { + for msg := range ch { + handler(msg.Channel, msg.Payload) + } + }() + + return nil +} + +func (mq *RedisMQ) Publish(channel, payload string) error { + return mq.client.Publish(context.Background(), channel, payload).Err() +} diff --git a/server/Dockerfile b/server/Dockerfile index af37499f..e821ab2a 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -2,12 +2,12 @@ FROM gradle:8.13.0-jdk21 AS development WORKDIR /app -COPY build.gradle build.gradle +COPY build.gradle settings.gradle ./ RUN gradle dependencies COPY src/ src/ -EXPOSE 8080 +EXPOSE 9091 EXPOSE 5005 CMD gradle build --continuous & gradle bootRun @@ -16,7 +16,7 @@ FROM gradle:8.13.0-jdk21 AS builder WORKDIR /app -COPY build.gradle build.gradle +COPY build.gradle settings.gradle ./ RUN gradle dependencies COPY src/ src/ @@ -28,6 +28,6 @@ WORKDIR /app COPY --from=builder app/build/libs/*.jar server.jar -EXPOSE 8080 +EXPOSE 9091 ENTRYPOINT ["java", "-jar", "server.jar"] diff --git a/server/README.md b/server/README.md index ec0c48be..62e73de1 100644 --- a/server/README.md +++ b/server/README.md @@ -35,3 +35,4 @@ https://api.teamserverdown.devops.aet.cit.tum.de/swagger-ui/index.html ## Run tests `gradle test` + diff --git a/server/build.gradle b/server/build.gradle index 4174e37d..0209c94f 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -3,7 +3,6 @@ plugins { id 'org.springframework.boot' version '3.4.4' id 'org.flywaydb.flyway' version '9.22.3' id 'com.diffplug.spotless' version '5.0.0' - id 'name.remal.sonarlint' version '6.0.0-rc-2' } apply plugin: 'io.spring.dependency-management' @@ -30,6 +29,18 @@ dependencies { implementation 'org.postgresql:postgresql' implementation 'org.flywaydb:flyway-core' implementation 'org.flywaydb:flyway-database-postgresql' + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.hibernate.validator:hibernate-validator' + implementation 'com.auth0:java-jwt:4.5.0' + implementation 'org.springframework.boot:spring-boot-starter-actuator' + implementation 'io.micrometer:micrometer-core' + implementation 'io.micrometer:micrometer-registry-prometheus' + testImplementation "org.mockito:mockito-core" + testImplementation "org.mockito:mockito-junit-jupiter" + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.springframework.boot:spring-boot-test' + testImplementation 'org.springframework.boot:spring-boot-test-autoconfigure' + testImplementation 'org.springframework:spring-test' testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0' developmentOnly 'org.springframework.boot:spring-boot-devtools' } @@ -44,6 +55,6 @@ spotless { } } -sonarLint { - isGeneratedCodeIgnored = false +tasks.withType(JavaCompile) { + options.compilerArgs << "-parameters" } \ No newline at end of file diff --git a/server/gradlew b/server/gradlew new file mode 100755 index 00000000..f3b75f3b --- /dev/null +++ b/server/gradlew @@ -0,0 +1,251 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/server/gradlew.bat b/server/gradlew.bat new file mode 100644 index 00000000..9d21a218 --- /dev/null +++ b/server/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/server/settings.gradle b/server/settings.gradle new file mode 100644 index 00000000..2b7ac29b --- /dev/null +++ b/server/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'teamserverdown' \ No newline at end of file diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/config/AppConfig.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/config/AppConfig.java new file mode 100644 index 00000000..7c62b56b --- /dev/null +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/config/AppConfig.java @@ -0,0 +1,14 @@ +package de.tum.cit.aet.devops.teamserverdown.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class AppConfig { + + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } +} diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/config/OpenAPIConfiguration.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/config/OpenAPIConfiguration.java index 653aebf3..858cb08c 100644 --- a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/config/OpenAPIConfiguration.java +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/config/OpenAPIConfiguration.java @@ -1,25 +1,95 @@ package de.tum.cit.aet.devops.teamserverdown.config; +import de.tum.cit.aet.devops.teamserverdown.security.CurrentUser; +import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.ExternalDocumentation; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.security.*; +import io.swagger.v3.oas.models.servers.Server; +import java.util.*; +import org.springdoc.core.customizers.OperationCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.MethodParameter; @Configuration public class OpenAPIConfiguration { @Bean public OpenAPI openAPI() { + final String securitySchemeName = "keycloak"; + + Scopes scopes = new Scopes(); + + SecurityScheme securityScheme = + new SecurityScheme() + .type(SecurityScheme.Type.OAUTH2) + .description("Keycloak OAuth2 login") + .flows( + new OAuthFlows() + .authorizationCode( + new OAuthFlow() + .authorizationUrl( + System.getenv("IDP_EXTERNAL_URI") + "/protocol/openid-connect/auth") + .tokenUrl( + System.getenv("IDP_EXTERNAL_URI") + + "/protocol/openid-connect/token") + .scopes(scopes))); + return new OpenAPI() .info( new Info() .title("Team Server Down") .description("DevOps Application") .version("v0.0.1")) + .servers(List.of(new Server().url(System.getenv("SERVER_URL")))) .externalDocs( new ExternalDocumentation() .description("README.md") - .url("https://github.com/AET-DevOps25/team-server-down")); + .url("https://github.com/AET-DevOps25/team-server-down")) + .components(new Components().addSecuritySchemes(securitySchemeName, securityScheme)) + .addSecurityItem(new SecurityRequirement().addList(securitySchemeName)); + } + + @Bean + public OperationCustomizer hideCurrentUser() { + return (operation, handlerMethod) -> { + if (operation.getParameters() == null) { + return operation; + } + + MethodParameter[] methodParameters = handlerMethod.getMethodParameters(); + List openApiParams = operation.getParameters(); + + Iterator paramIterator = openApiParams.iterator(); + while (paramIterator.hasNext()) { + Parameter openApiParam = paramIterator.next(); + + for (MethodParameter methodParam : methodParameters) { + if (methodParam.hasParameterAnnotation(CurrentUser.class)) { + Class paramType = methodParam.getParameterType(); + + String paramTypeName = paramType.getSimpleName(); + + if (openApiParam.getName().equalsIgnoreCase(paramTypeName) + || (openApiParam.getSchema() != null + && paramTypeName.equalsIgnoreCase(openApiParam.getSchema().getType()))) { + paramIterator.remove(); + break; + } + + if (openApiParam.getSchema() != null + && openApiParam.getSchema().get$ref() != null + && openApiParam.getSchema().get$ref().contains(paramType.getSimpleName())) { + paramIterator.remove(); + break; + } + } + } + } + return operation; + }; } } diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/config/SecurityConfiguration.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/config/SecurityConfiguration.java new file mode 100644 index 00000000..6dd14268 --- /dev/null +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/config/SecurityConfiguration.java @@ -0,0 +1,54 @@ +package de.tum.cit.aet.devops.teamserverdown.config; + +import de.tum.cit.aet.devops.teamserverdown.security.JWTAuthenticationFilter; +import java.util.List; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; + +@Configuration +@EnableWebSecurity +public class SecurityConfiguration { + + private final JWTAuthenticationFilter jwtAuthenticationFilter; + + public SecurityConfiguration(JWTAuthenticationFilter jwtAuthenticationFilter) { + this.jwtAuthenticationFilter = jwtAuthenticationFilter; + } + + @Bean + public SecurityFilterChain securityFilterChain( + org.springframework.security.config.annotation.web.builders.HttpSecurity http) + throws Exception { + http.cors(cors -> {}) + .httpBasic(AbstractHttpConfigurer::disable) + .formLogin(AbstractHttpConfigurer::disable) + .csrf(AbstractHttpConfigurer::disable) + .sessionManagement( + session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); + + return http.build(); + } + + @Bean + public CorsFilter corsFilter() { + CorsConfiguration config = new CorsConfiguration(); + config.setAllowedOrigins(List.of(System.getenv("ALLOWED_ORIGIN"))); + config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH")); + config.setAllowedHeaders(List.of("*")); + config.setExposedHeaders(List.of("Authorization")); + config.setAllowCredentials(true); + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", config); + return new CorsFilter(source); + } +} diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/config/WebConfiguration.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/config/WebConfiguration.java new file mode 100644 index 00000000..e5b83f0a --- /dev/null +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/config/WebConfiguration.java @@ -0,0 +1,21 @@ +package de.tum.cit.aet.devops.teamserverdown.config; + +import de.tum.cit.aet.devops.teamserverdown.security.CurrentUserArgumentResolver; +import java.util.List; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfiguration implements WebMvcConfigurer { + private final CurrentUserArgumentResolver currentUserArgumentResolver; + + public WebConfiguration(CurrentUserArgumentResolver currentUserArgumentResolver) { + this.currentUserArgumentResolver = currentUserArgumentResolver; + } + + @Override + public void addArgumentResolvers(List resolvers) { + resolvers.add(currentUserArgumentResolver); + } +} diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/AccountController.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/AccountController.java new file mode 100644 index 00000000..e1f14633 --- /dev/null +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/AccountController.java @@ -0,0 +1,19 @@ +package de.tum.cit.aet.devops.teamserverdown.controller; + +import de.tum.cit.aet.devops.teamserverdown.controller.dtos.UserResponse; +import de.tum.cit.aet.devops.teamserverdown.model.User; +import de.tum.cit.aet.devops.teamserverdown.security.CurrentUser; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@Tag(name = "Account", description = "User Accounts") +public class AccountController { + @GetMapping("/me") + public ResponseEntity getCurrentUser(@CurrentUser User user) { + UserResponse response = UserResponse.fromEntity(user); + return ResponseEntity.ok(response); + } +} diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/EdgeController.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/EdgeController.java new file mode 100644 index 00000000..d97cb23d --- /dev/null +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/EdgeController.java @@ -0,0 +1,43 @@ +package de.tum.cit.aet.devops.teamserverdown.controller; + +import de.tum.cit.aet.devops.teamserverdown.model.Edge; +import de.tum.cit.aet.devops.teamserverdown.repository.EdgeRepository; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/edge") +public class EdgeController { + + private final EdgeRepository edgeRepository; + + private static final Logger logger = LoggerFactory.getLogger(EdgeController.class); + + public EdgeController(EdgeRepository edgeRepository) { + this.edgeRepository = edgeRepository; + } + + @GetMapping("/whiteboard/{whiteboardId}") + public ResponseEntity> getEdgesByWhiteboard(@PathVariable long whiteboardId) { + List edges = edgeRepository.findAllByWhiteboardId(whiteboardId); + return ResponseEntity.ok(edges); + } + + @PostMapping + public ResponseEntity addEdge(@RequestBody Edge edge) { + Edge savedEdge = edgeRepository.save(edge); + return ResponseEntity.status(201).body(savedEdge); + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteEdge(@PathVariable String id) { + if (!edgeRepository.existsById(id)) { + return ResponseEntity.notFound().build(); + } + edgeRepository.deleteById(id); + return ResponseEntity.noContent().build(); + } +} diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/NodeController.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/NodeController.java new file mode 100644 index 00000000..03858d6f --- /dev/null +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/NodeController.java @@ -0,0 +1,76 @@ +package de.tum.cit.aet.devops.teamserverdown.controller; + +import de.tum.cit.aet.devops.teamserverdown.controller.dtos.UpdateNodeRequest; +import de.tum.cit.aet.devops.teamserverdown.model.Node; +import de.tum.cit.aet.devops.teamserverdown.repository.NodeRepository; +import java.util.List; +import java.util.Optional; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/nodes") +public class NodeController { + + private final NodeRepository nodeRepository; + + public NodeController(NodeRepository nodeRepository) { + this.nodeRepository = nodeRepository; + } + + // Get all nodes for a whiteboard + @GetMapping("/whiteboard/{whiteboardId}") + public List getAllByWhiteboardId(@PathVariable long whiteboardId) { + return nodeRepository.findByWhiteboardId(whiteboardId); + } + + // Create a node + @PostMapping + public Node createNode(@RequestBody Node node) { + return nodeRepository.save(node); + } + + @PatchMapping("/nodes/{id}") + public ResponseEntity patchNode( + @PathVariable String id, @RequestBody UpdateNodeRequest updateDTO) { + Optional existingNodeOpt = nodeRepository.findById(id); + + if (existingNodeOpt.isEmpty()) { + return ResponseEntity.notFound().build(); + } + + Node node = existingNodeOpt.get(); + + if (updateDTO.getType() != null) node.setType(updateDTO.getType()); + if (updateDTO.getPositionX() != null) node.setPositionX(updateDTO.getPositionX()); + if (updateDTO.getPositionY() != null) node.setPositionY(updateDTO.getPositionY()); + if (updateDTO.getLabel() != null) node.setLabel(updateDTO.getLabel()); + if (updateDTO.getWidth() != null) node.setWidth(updateDTO.getWidth()); + if (updateDTO.getHeight() != null) node.setHeight(updateDTO.getHeight()); + if (updateDTO.getColor() != null) node.setColor(updateDTO.getColor()); + if (updateDTO.getBorderColor() != null) node.setBorderColor(updateDTO.getBorderColor()); + if (updateDTO.getBorderWidth() != null) node.setBorderWidth(updateDTO.getBorderWidth()); + if (updateDTO.getBorderOpacity() != null) node.setBorderOpacity(updateDTO.getBorderOpacity()); + if (updateDTO.getOpacity() != null) node.setOpacity(updateDTO.getOpacity()); + if (updateDTO.getTextColor() != null) node.setTextColor(updateDTO.getTextColor()); + if (updateDTO.getFontSize() != null) node.setFontSize(updateDTO.getFontSize()); + if (updateDTO.getFontFamily() != null) node.setFontFamily(updateDTO.getFontFamily()); + if (updateDTO.getIsBold() != null) node.setBold(updateDTO.getIsBold()); + if (updateDTO.getIsItalic() != null) node.setItalic(updateDTO.getIsItalic()); + if (updateDTO.getIsStrikethrough() != null) + node.setStrikethrough(updateDTO.getIsStrikethrough()); + if (updateDTO.getIsUnderline() != null) node.setUnderline(updateDTO.getIsUnderline()); + + nodeRepository.save(node); + return ResponseEntity.ok(node); + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteNode(@PathVariable String id) { + if (!nodeRepository.existsById(id)) { + return ResponseEntity.notFound().build(); + } + nodeRepository.deleteById(id); + return ResponseEntity.noContent().build(); + } +} diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/ViewportController.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/ViewportController.java new file mode 100644 index 00000000..39849975 --- /dev/null +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/ViewportController.java @@ -0,0 +1,34 @@ +package de.tum.cit.aet.devops.teamserverdown.controller; + +import de.tum.cit.aet.devops.teamserverdown.model.Viewport; +import de.tum.cit.aet.devops.teamserverdown.repository.ViewportRepository; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/viewports") +public class ViewportController { + + private final ViewportRepository viewportRepository; + + public ViewportController(ViewportRepository viewportRepository) { + this.viewportRepository = viewportRepository; + } + + @GetMapping("/whiteboard/{whiteboardId}") + public ResponseEntity getViewportByWhiteboardId(@PathVariable Long whiteboardId) { + return viewportRepository + .findByWhiteboardId(whiteboardId) + .map(ResponseEntity::ok) + .orElse(ResponseEntity.notFound().build()); + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteViewport(@PathVariable Long id) { + if (!viewportRepository.existsById(id)) { + return ResponseEntity.notFound().build(); + } + viewportRepository.deleteById(id); + return ResponseEntity.noContent().build(); + } +} diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/WhiteboardController.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/WhiteboardController.java new file mode 100644 index 00000000..590dede0 --- /dev/null +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/WhiteboardController.java @@ -0,0 +1,290 @@ +package de.tum.cit.aet.devops.teamserverdown.controller; + +import de.tum.cit.aet.devops.teamserverdown.controller.dtos.*; +import de.tum.cit.aet.devops.teamserverdown.model.User; +import de.tum.cit.aet.devops.teamserverdown.model.Viewport; +import de.tum.cit.aet.devops.teamserverdown.model.Whiteboard; +import de.tum.cit.aet.devops.teamserverdown.repository.EdgeRepository; +import de.tum.cit.aet.devops.teamserverdown.repository.NodeRepository; +import de.tum.cit.aet.devops.teamserverdown.repository.UserWhiteboardAccessRepository; +import de.tum.cit.aet.devops.teamserverdown.repository.ViewportRepository; +import de.tum.cit.aet.devops.teamserverdown.repository.WhiteboardRepository; +import de.tum.cit.aet.devops.teamserverdown.security.CurrentUser; +import de.tum.cit.aet.devops.teamserverdown.services.UserWhiteboardAccessService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import java.util.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/whiteboards") +@Tag(name = "Whiteboard", description = "Endpoints for managing whiteboards") +public class WhiteboardController { + + private static final Logger logger = LoggerFactory.getLogger(WhiteboardController.class); + + private final WhiteboardRepository whiteboardRepository; + private final NodeRepository nodeRepository; + private final EdgeRepository edgeRepository; + private final ViewportRepository viewportRepository; + private final UserWhiteboardAccessRepository userWhiteboardAccessRepository; + private final UserWhiteboardAccessService userWhiteboardAccessService; + + public WhiteboardController( + WhiteboardRepository whiteboardRepository, + NodeRepository nodeRepository, + EdgeRepository edgeRepository, + ViewportRepository viewportRepository, + UserWhiteboardAccessRepository userWhiteboardAccessRepository, + UserWhiteboardAccessService userWhiteboardAccessService) { + this.whiteboardRepository = whiteboardRepository; + this.nodeRepository = nodeRepository; + this.edgeRepository = edgeRepository; + this.viewportRepository = viewportRepository; + this.userWhiteboardAccessRepository = userWhiteboardAccessRepository; + this.userWhiteboardAccessService = userWhiteboardAccessService; + } + + @PostMapping + @Operation(summary = "Create whiteboard", description = "Creates a new whiteboard for a user.") + public ResponseEntity createWhiteboard( + @CurrentUser User user, @RequestParam String title) { + logger.info("Creating whiteboard for userId={} with title='{}'", user.getId(), title); + + Whiteboard whiteboard = new Whiteboard(title, user); + Whiteboard saved = whiteboardRepository.save(whiteboard); + logger.info("Whiteboard created with id={}", saved.getId()); + + WhiteboardResponse response = WhiteboardResponse.fromEntity(saved); + return ResponseEntity.ok(response); + } + + @GetMapping("/{id}") + public ResponseEntity getWhiteboardById( + @Parameter(description = "ID of the whiteboard", required = true) @PathVariable Long id, + @CurrentUser User user) { + + logger.info("Fetching whiteboard with id={} for userId={}", id, user.getId()); + Optional whiteboardOpt = + userWhiteboardAccessService.getUserWhiteboardById(user.getId(), id); + + if (whiteboardOpt.isEmpty()) { + logger.warn( + "Whiteboard not found or unauthorized access for id={} and userId={}", id, user.getId()); + return ResponseEntity.status(404).build(); + } + + logger.info("Whiteboard found: id={}", id); + WhiteboardResponse response = WhiteboardResponse.fromEntity(whiteboardOpt.get()); + return ResponseEntity.ok(response); + } + + @GetMapping + @Operation( + summary = "Get whiteboards by user ID", + description = "Returns a list of whiteboards for the current user.") + public ResponseEntity> getUserWhiteboards(@CurrentUser User user) { + logger.info("Fetching all whiteboards for userId={}", user.getId()); + + Set allAccessible = userWhiteboardAccessService.getUserWhiteboards(user.getId()); + + List whiteboardResponseList = new ArrayList<>(); + for (Whiteboard whiteboard : allAccessible) { + whiteboardResponseList.add(WhiteboardResponse.fromEntity(whiteboard)); + } + + return ResponseEntity.ok(whiteboardResponseList); + } + + @GetMapping("/{id}/title") + @Operation( + summary = "Get whiteboard title", + description = "Returns the title of a whiteboard by its ID") + public ResponseEntity getWhiteboardTitle( + @Parameter(description = "ID of the whiteboard", required = true) @PathVariable Long id, + @CurrentUser User user) { + + logger.info("Fetching title for whiteboard with id={} for userId={}", id, user.getId()); + Optional whiteboardOpt = + userWhiteboardAccessService.getUserWhiteboardById(user.getId(), id); + + if (whiteboardOpt.isPresent()) { + Whiteboard whiteboard = whiteboardOpt.get(); + logger.info("Whiteboard found: id={}, title='{}'", id, whiteboard.getTitle()); + return ResponseEntity.ok(whiteboard.getTitle()); + } else { + logger.warn( + "Whiteboard not found or unauthorized access for id={} and userId={}", id, user.getId()); + return ResponseEntity.status(404).build(); + } + } + + @PutMapping("/{id}/title") + @Operation(summary = "Update title", description = "Updates the title of an existing whiteboard.") + public ResponseEntity updateTitle( + @Parameter(description = "ID of the whiteboard", required = true) @PathVariable Long id, + @RequestParam String title, + @CurrentUser User user) { + logger.info("Updating title for whiteboard id={} to '{}'", id, title); + Optional whiteboardOpt = whiteboardRepository.findByIdAndUserId(id, user.getId()); + if (whiteboardOpt.isPresent()) { + Whiteboard w = whiteboardOpt.get(); + w.setTitle(title); + Whiteboard updated = whiteboardRepository.save(w); + logger.info("Whiteboard title updated successfully for id={}", id); + return ResponseEntity.ok(updated.getTitle()); + } + logger.warn("Whiteboard not found or unauthorized access: id={}, userId={}", id, user.getId()); + return ResponseEntity.status(403).build(); + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteWhiteboard(@PathVariable Long id, @CurrentUser User user) { + logger.info("Attempting to delete whiteboard with id={} by userId={}", id, user.getId()); + + Optional whiteboardOpt = whiteboardRepository.findByIdAndUserId(id, user.getId()); + + if (whiteboardOpt.isEmpty()) { + logger.warn( + "Whiteboard not found or unauthorized access: id={}, userId={}", id, user.getId()); + return ResponseEntity.status(403).build(); + } + + whiteboardRepository.delete(whiteboardOpt.get()); + logger.info("Whiteboard deleted: id={}, userId={}", id, user.getId()); + + return ResponseEntity.noContent().build(); + } + + @GetMapping("/{id}/collaborators") + public ResponseEntity> getCollaborators( + @Parameter(description = "ID of the whiteboard", required = true) @PathVariable Long id, + @CurrentUser User user) { + Optional whiteboardOpt = whiteboardRepository.findById(id); + if (whiteboardOpt.isEmpty()) { + logger.warn("Whiteboard not found: id={}", id); + return ResponseEntity.status(404).build(); + } + + List collaborators = userWhiteboardAccessRepository.findUsersByWhiteboardId(id); + collaborators.add(whiteboardOpt.get().getUser()); + + List whiteboardUserListResponse = new ArrayList<>(); + for (User collaborator : collaborators) { + whiteboardUserListResponse.add(UserResponse.fromEntity(collaborator)); + } + + return ResponseEntity.ok(whiteboardUserListResponse); + } + + @PostMapping("/{id}/invitations") + @Operation(summary = "Invite users to collaborate on the whiteboard") + public ResponseEntity inviteCollaborators( + @Parameter(description = "ID of the whiteboard", required = true) @PathVariable Long id, + @Valid @RequestBody InviteCollaboratorsRequest inviteCollaboratorsRequest, + @CurrentUser User user) { + Optional whiteboardOpt = whiteboardRepository.findByIdAndUserId(id, user.getId()); + if (whiteboardOpt.isEmpty()) { + logger.warn( + "[Invitations] Whiteboard not found or unauthorized access: id={}, userId={}", + id, + user.getId()); + return ResponseEntity.status(403).build(); + } + + List emails = inviteCollaboratorsRequest.getEmails(); + this.userWhiteboardAccessService.inviteUsersToWhiteboard(emails, whiteboardOpt.get().getId()); + + return ResponseEntity.noContent().build(); + } + + @DeleteMapping("/{id}/invitations") + @Operation(summary = "Remove collaborators from the whiteboard") + public ResponseEntity removeCollaborators( + @Parameter(description = "ID of the whiteboard", required = true) @PathVariable Long id, + @Valid @RequestBody RemoveCollaboratorsRequest removeCollaboratorsRequest, + @CurrentUser User user) { + + logger.info( + "[Remove Invitations] Request received - WhiteboardId={}, RequestingUserId={}, CollaboratorsToRemove={}", + id, + user.getId(), + removeCollaboratorsRequest.getUserIds()); + + Optional whiteboardOpt = whiteboardRepository.findByIdAndUserId(id, user.getId()); + + if (whiteboardOpt.isEmpty()) { + logger.warn( + "[Remove Invitations] Access denied - Whiteboard not found or unauthorized access: WhiteboardId={}, UserId={}", + id, + user.getId()); + return ResponseEntity.status(403).build(); + } + + List userIds = removeCollaboratorsRequest.getUserIds(); + logger.info( + "[Remove Invitations] Processing removal of {} collaborators from whiteboard {}", + userIds.size(), + id); + + try { + this.userWhiteboardAccessService.removeUsersFromWhiteboard( + userIds, whiteboardOpt.get().getId()); + logger.info( + "[Remove Invitations] Successfully removed {} collaborators from whiteboard {}", + userIds.size(), + id); + } catch (Exception e) { + logger.error( + "[Remove Invitations] Error removing collaborators from whiteboard {} - Error: {}", + id, + e.getMessage(), + e); + throw e; + } + + return ResponseEntity.noContent().build(); + } + + @PostMapping("/{whiteboardId}/save") + public ResponseEntity saveWhiteboardState( + @PathVariable Long whiteboardId, + @RequestBody SaveWhiteboardStateRequest saveWhiteboardStateRequest) { + + nodeRepository.deleteByWhiteboardId(whiteboardId); + edgeRepository.deleteByWhiteboardId(whiteboardId); + + saveWhiteboardStateRequest.getNodes().forEach(node -> node.setWhiteboardId(whiteboardId)); + saveWhiteboardStateRequest.getEdges().forEach(edge -> edge.setWhiteboardId(whiteboardId)); + + nodeRepository.saveAll(saveWhiteboardStateRequest.getNodes()); + edgeRepository.saveAll(saveWhiteboardStateRequest.getEdges()); + + ViewportResponse viewportResponse = saveWhiteboardStateRequest.getViewportResponse(); + if (viewportResponse != null) { + Optional existingOpt = viewportRepository.findByWhiteboardId(whiteboardId); + + if (existingOpt.isPresent()) { + Viewport existing = existingOpt.get(); + existing.setX(viewportResponse.getX()); + existing.setY(viewportResponse.getY()); + existing.setZoom(viewportResponse.getZoom()); + viewportRepository.save(existing); + } else { + Viewport newViewport = new Viewport(); + newViewport.setX(viewportResponse.getX()); + newViewport.setY(viewportResponse.getY()); + newViewport.setZoom(viewportResponse.getZoom()); + newViewport.setWhiteboardId(whiteboardId); + viewportRepository.save(newViewport); + } + } + + return ResponseEntity.ok().build(); + } +} diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/dtos/CreateViewportRequest.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/dtos/CreateViewportRequest.java new file mode 100644 index 00000000..42fb3392 --- /dev/null +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/dtos/CreateViewportRequest.java @@ -0,0 +1,40 @@ +package de.tum.cit.aet.devops.teamserverdown.controller.dtos; + +public class CreateViewportRequest { + private double x; + private double y; + private double zoom; + private Long whiteboardId; + + public double getX() { + return x; + } + + public void setX(double x) { + this.x = x; + } + + public double getY() { + return y; + } + + public void setY(double y) { + this.y = y; + } + + public double getZoom() { + return zoom; + } + + public void setZoom(double zoom) { + this.zoom = zoom; + } + + public Long getWhiteboardId() { + return whiteboardId; + } + + public void setWhiteboardId(Long whiteboardId) { + this.whiteboardId = whiteboardId; + } +} diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/dtos/InviteCollaboratorsRequest.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/dtos/InviteCollaboratorsRequest.java new file mode 100644 index 00000000..c7c84f89 --- /dev/null +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/dtos/InviteCollaboratorsRequest.java @@ -0,0 +1,15 @@ +package de.tum.cit.aet.devops.teamserverdown.controller.dtos; + +import java.util.List; + +public class InviteCollaboratorsRequest { + private List emails; + + public List getEmails() { + return emails; + } + + public void setEmails(List emails) { + this.emails = emails; + } +} diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/dtos/RemoveCollaboratorsRequest.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/dtos/RemoveCollaboratorsRequest.java new file mode 100644 index 00000000..942572d6 --- /dev/null +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/dtos/RemoveCollaboratorsRequest.java @@ -0,0 +1,15 @@ +package de.tum.cit.aet.devops.teamserverdown.controller.dtos; + +import java.util.List; + +public class RemoveCollaboratorsRequest { + private List userIds; + + public List getUserIds() { + return userIds; + } + + public void setUserIds(List userIds) { + this.userIds = userIds; + } +} diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/dtos/SaveWhiteboardStateRequest.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/dtos/SaveWhiteboardStateRequest.java new file mode 100644 index 00000000..0e4d3609 --- /dev/null +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/dtos/SaveWhiteboardStateRequest.java @@ -0,0 +1,36 @@ +package de.tum.cit.aet.devops.teamserverdown.controller.dtos; + +import de.tum.cit.aet.devops.teamserverdown.model.Edge; +import de.tum.cit.aet.devops.teamserverdown.model.Node; +import java.util.List; + +public class SaveWhiteboardStateRequest { + + private List nodes; + private List edges; + private ViewportResponse viewport; + + public List getNodes() { + return nodes; + } + + public void setNodes(List nodes) { + this.nodes = nodes; + } + + public List getEdges() { + return edges; + } + + public void setEdges(List edges) { + this.edges = edges; + } + + public ViewportResponse getViewportResponse() { + return viewport; + } + + public void setViewportResponse(ViewportResponse viewport) { + this.viewport = viewport; + } +} diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/dtos/UpdateNodeRequest.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/dtos/UpdateNodeRequest.java new file mode 100644 index 00000000..363f06d7 --- /dev/null +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/dtos/UpdateNodeRequest.java @@ -0,0 +1,166 @@ +package de.tum.cit.aet.devops.teamserverdown.controller.dtos; + +public class UpdateNodeRequest { + private String type; + private Double positionX; + private Double positionY; + private String label; + private Double width; + private Double height; + private String color; + private String borderColor; + private Integer borderWidth; + private Double borderOpacity; + private Double opacity; + private String textColor; + private Integer fontSize; + private String fontFamily; + private Boolean isBold; + private Boolean isItalic; + private Boolean isStrikethrough; + private Boolean isUnderline; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Double getPositionX() { + return positionX; + } + + public void setPositionX(Double positionX) { + this.positionX = positionX; + } + + public Double getPositionY() { + return positionY; + } + + public void setPositionY(Double positionY) { + this.positionY = positionY; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + public Double getWidth() { + return width; + } + + public void setWidth(Double width) { + this.width = width; + } + + public Double getHeight() { + return height; + } + + public void setHeight(Double height) { + this.height = height; + } + + public String getColor() { + return color; + } + + public void setColor(String color) { + this.color = color; + } + + public String getBorderColor() { + return borderColor; + } + + public void setBorderColor(String borderColor) { + this.borderColor = borderColor; + } + + public Integer getBorderWidth() { + return borderWidth; + } + + public void setBorderWidth(Integer borderWidth) { + this.borderWidth = borderWidth; + } + + public Double getBorderOpacity() { + return borderOpacity; + } + + public void setBorderOpacity(Double borderOpacity) { + this.borderOpacity = borderOpacity; + } + + public Double getOpacity() { + return opacity; + } + + public void setOpacity(Double opacity) { + this.opacity = opacity; + } + + public String getTextColor() { + return textColor; + } + + public void setTextColor(String textColor) { + this.textColor = textColor; + } + + public Integer getFontSize() { + return fontSize; + } + + public void setFontSize(Integer fontSize) { + this.fontSize = fontSize; + } + + public String getFontFamily() { + return fontFamily; + } + + public void setFontFamily(String fontFamily) { + this.fontFamily = fontFamily; + } + + public Boolean getIsBold() { + return isBold; + } + + public void setIsBold(Boolean bold) { + isBold = bold; + } + + public Boolean getIsItalic() { + return isItalic; + } + + public void setIsItalic(Boolean italic) { + isItalic = italic; + } + + public Boolean getIsStrikethrough() { + return isStrikethrough; + } + + public void setIsStrikethrough(Boolean strikethrough) { + isStrikethrough = strikethrough; + } + + public Boolean getIsUnderline() { + return isUnderline; + } + + public void setIsUnderline(Boolean underline) { + isUnderline = underline; + } +} diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/dtos/UserResponse.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/dtos/UserResponse.java new file mode 100644 index 00000000..c93ea6cd --- /dev/null +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/dtos/UserResponse.java @@ -0,0 +1,61 @@ +package de.tum.cit.aet.devops.teamserverdown.controller.dtos; + +import de.tum.cit.aet.devops.teamserverdown.model.User; + +public class UserResponse { + private Long id; + private String firstName; + private String lastName; + private String username; + private String email; + + public static UserResponse fromEntity(User user) { + UserResponse dto = new UserResponse(); + dto.id = user.getId(); + dto.firstName = user.getFirstName(); + dto.lastName = user.getLastName(); + dto.username = user.getUsername(); + dto.email = user.getEmail(); + return dto; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } +} diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/dtos/ViewportResponse.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/dtos/ViewportResponse.java new file mode 100644 index 00000000..290b9985 --- /dev/null +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/dtos/ViewportResponse.java @@ -0,0 +1,31 @@ +package de.tum.cit.aet.devops.teamserverdown.controller.dtos; + +public class ViewportResponse { + private Double x; + private Double y; + private Double zoom; + + public Double getX() { + return x; + } + + public void setX(Double x) { + this.x = x; + } + + public Double getY() { + return y; + } + + public void setY(Double y) { + this.y = y; + } + + public Double getZoom() { + return zoom; + } + + public void setZoom(Double zoom) { + this.zoom = zoom; + } +} diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/dtos/WhiteboardResponse.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/dtos/WhiteboardResponse.java new file mode 100644 index 00000000..a6d2fbd2 --- /dev/null +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/controller/dtos/WhiteboardResponse.java @@ -0,0 +1,63 @@ +package de.tum.cit.aet.devops.teamserverdown.controller.dtos; + +import de.tum.cit.aet.devops.teamserverdown.model.User; +import de.tum.cit.aet.devops.teamserverdown.model.Whiteboard; +import java.time.Instant; + +public class WhiteboardResponse { + private Long id; + private String title; + private User user; + private Instant createdAt; + private Instant lastUpdatedAt; + + public static WhiteboardResponse fromEntity(Whiteboard whiteboard) { + WhiteboardResponse dto = new WhiteboardResponse(); + dto.id = whiteboard.getId(); + dto.title = whiteboard.getTitle(); + dto.createdAt = whiteboard.getCreatedAt(); + dto.lastUpdatedAt = whiteboard.getLastUpdatedAt(); + dto.user = whiteboard.getUser(); + return dto; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + + public Instant getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(Instant createdAt) { + this.createdAt = createdAt; + } + + public Instant getLastUpdatedAt() { + return lastUpdatedAt; + } + + public void setLastUpdatedAt(Instant lastUpdatedAt) { + this.lastUpdatedAt = lastUpdatedAt; + } +} diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/model/Edge.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/model/Edge.java new file mode 100644 index 00000000..882ae24c --- /dev/null +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/model/Edge.java @@ -0,0 +1,73 @@ +package de.tum.cit.aet.devops.teamserverdown.model; + +import jakarta.persistence.*; + +@Entity +@Table(name = "edges") +public class Edge { + + @Id private String id; + + @Column(name = "whiteboard_id", nullable = false) + private long whiteboardId; + + @Column(name = "source") + private String source; + + @Column(name = "source_handle") + private String sourceHandle; + + @Column(name = "target") + private String target; + + @Column(name = "target_handle") + private String targetHandle; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getSource() { + return source; + } + + public void setSource(String source) { + this.source = source; + } + + public String getSourceHandle() { + return sourceHandle; + } + + public void setSourceHandle(String sourceHandle) { + this.sourceHandle = sourceHandle; + } + + public String getTarget() { + return target; + } + + public void setTarget(String target) { + this.target = target; + } + + public String getTargetHandle() { + return targetHandle; + } + + public void setTargetHandle(String targetHandle) { + this.targetHandle = targetHandle; + } + + public long getWhiteboardId() { + return whiteboardId; + } + + public void setWhiteboardId(long whiteboardId) { + this.whiteboardId = whiteboardId; + } +} diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/model/Node.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/model/Node.java new file mode 100644 index 00000000..d315658c --- /dev/null +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/model/Node.java @@ -0,0 +1,276 @@ +package de.tum.cit.aet.devops.teamserverdown.model; + +import jakarta.persistence.*; + +@Entity +@Table(name = "nodes") +public class Node { + + @Id private String id; + + @Column(name = "whiteboard_id", nullable = false) + private long whiteboardId; + + @Column(nullable = false) + private String type; + + @Column(name = "position_x", nullable = false) + private double positionX; + + @Column(name = "position_y", nullable = false) + private double positionY; + + @Column(columnDefinition = "TEXT") + private String label; + + @Column(name = "width", nullable = false) + private double width; + + @Column(name = "height", nullable = false) + private double height; + + // properties + + @Column(name = "color") + private String color; + + @Column(name = "border_color") + private String borderColor; + + @Column(name = "border_width") + private int borderWidth; + + @Column(name = "border_opacity") + private double borderOpacity; + + @Column(name = "opacity") + private double opacity; + + @Column(name = "text_color") + private String textColor; + + @Column(name = "font_size") + private int fontSize; + + @Column(name = "font_family") + private String fontFamily; + + @Column(name = "is_bold") + private boolean isBold; + + @Column(name = "is_italic") + private boolean isItalic; + + @Column(name = "is_strikethrough") + private boolean isStrikethrough; + + @Column(name = "is_underline") + private boolean isUnderline; + + public Node() {} + + public Node( + String id, + long whiteboardId, + String type, + double positionX, + double positionY, + String label, + double width, + double height, + String color, + String borderColor, + int borderWidth, + double borderOpacity, + double opacity, + String textColor, + int fontSize, + String fontFamily, + boolean isBold, + boolean isItalic, + boolean isStrikethrough, + boolean isUnderline) { + this.id = id; + this.whiteboardId = whiteboardId; + this.type = type; + this.positionX = positionX; + this.positionY = positionY; + this.label = label; + this.width = width; + this.height = height; + this.color = color; + this.borderColor = borderColor; + this.borderWidth = borderWidth; + this.borderOpacity = borderOpacity; + this.opacity = opacity; + this.textColor = textColor; + this.fontSize = fontSize; + this.fontFamily = fontFamily; + this.isBold = isBold; + this.isItalic = isItalic; + this.isStrikethrough = isStrikethrough; + this.isUnderline = isUnderline; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public long getWhiteboardId() { + return whiteboardId; + } + + public void setWhiteboardId(long whiteboardId) { + this.whiteboardId = whiteboardId; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public double getPositionX() { + return positionX; + } + + public void setPositionX(double positionX) { + this.positionX = positionX; + } + + public double getPositionY() { + return positionY; + } + + public void setPositionY(double positionY) { + this.positionY = positionY; + } + + public String getLabel() { + return label; + } + + public void setLabel(String label) { + this.label = label; + } + + // properties + + public double getWidth() { + return width; + } + + public void setWidth(double width) { + this.width = width; + } + + public double getHeight() { + return height; + } + + public void setHeight(double height) { + this.height = height; + } + + public String getColor() { + return color; + } + + public void setColor(String color) { + this.color = color; + } + + public String getBorderColor() { + return borderColor; + } + + public void setBorderColor(String borderColor) { + this.borderColor = borderColor; + } + + public int getBorderWidth() { + return borderWidth; + } + + public void setBorderWidth(int borderWidth) { + this.borderWidth = borderWidth; + } + + public double getBorderOpacity() { + return borderOpacity; + } + + public void setBorderOpacity(double borderOpacity) { + this.borderOpacity = borderOpacity; + } + + public double getOpacity() { + return opacity; + } + + public void setOpacity(double opacity) { + this.opacity = opacity; + } + + public String getTextColor() { + return textColor; + } + + public void setTextColor(String textColor) { + this.textColor = textColor; + } + + public int getFontSize() { + return fontSize; + } + + public void setFontSize(int fontSize) { + this.fontSize = fontSize; + } + + public String getFontFamily() { + return fontFamily; + } + + public void setFontFamily(String fontFamily) { + this.fontFamily = fontFamily; + } + + public boolean isBold() { + return isBold; + } + + public void setBold(boolean bold) { + isBold = bold; + } + + public boolean isItalic() { + return isItalic; + } + + public void setItalic(boolean italic) { + isItalic = italic; + } + + public boolean isStrikethrough() { + return isStrikethrough; + } + + public void setStrikethrough(boolean strikethrough) { + isStrikethrough = strikethrough; + } + + public boolean isUnderline() { + return isUnderline; + } + + public void setUnderline(boolean underline) { + isUnderline = underline; + } +} diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/model/User.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/model/User.java index c11d0ab0..7c9b3df6 100644 --- a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/model/User.java +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/model/User.java @@ -1,18 +1,38 @@ package de.tum.cit.aet.devops.teamserverdown.model; -import jakarta.persistence.Entity; -import jakarta.persistence.Id; +import jakarta.persistence.*; @Entity +@Table( + name = "\"user\"", + indexes = {@Index(name = "idx_user_email", columnList = "email")}) public class User { - @Id private Long id; - private String name; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String firstName; + private String lastName; + private String username; + + @Column(nullable = false, unique = true) + private String email; public User() {} - public User(Long id, String name) { + public User(Long id, String firstName, String lastName, String username, String email) { this.id = id; - this.name = name; + this.firstName = firstName; + this.lastName = lastName; + this.username = username; + this.email = email; + } + + public User(String firstName, String lastName, String username, String email) { + this.firstName = firstName; + this.lastName = lastName; + this.username = username; + this.email = email; } public Long getId() { @@ -23,11 +43,35 @@ public void setId(Long id) { this.id = id; } - public String getName() { - return name; + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getEmail() { + return email; } - public void setName(String name) { - this.name = name; + public void setEmail(String email) { + this.email = email; } } diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/model/UserWhiteboardAccess.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/model/UserWhiteboardAccess.java new file mode 100644 index 00000000..4a94c82d --- /dev/null +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/model/UserWhiteboardAccess.java @@ -0,0 +1,56 @@ +package de.tum.cit.aet.devops.teamserverdown.model; + +import jakarta.persistence.*; +import org.hibernate.annotations.OnDelete; +import org.hibernate.annotations.OnDeleteAction; + +@Entity +@Table( + name = "user_whiteboard_access", + uniqueConstraints = @UniqueConstraint(columnNames = {"user_id", "whiteboard_id"})) +public class UserWhiteboardAccess { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "user_id", nullable = false) + @OnDelete(action = OnDeleteAction.CASCADE) + private User user; + + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "whiteboard_id", nullable = false) + @OnDelete(action = OnDeleteAction.CASCADE) + private Whiteboard whiteboard; + + public UserWhiteboardAccess() {} + + public UserWhiteboardAccess(User user, Whiteboard whiteboard) { + this.user = user; + this.whiteboard = whiteboard; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + + public Whiteboard getWhiteboard() { + return whiteboard; + } + + public void setWhiteboard(Whiteboard whiteboard) { + this.whiteboard = whiteboard; + } +} diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/model/Viewport.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/model/Viewport.java new file mode 100644 index 00000000..b3fd2af5 --- /dev/null +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/model/Viewport.java @@ -0,0 +1,73 @@ +package de.tum.cit.aet.devops.teamserverdown.model; + +import jakarta.persistence.*; + +@Entity +@Table(name = "viewports") +public class Viewport { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "x", nullable = false) + private double x; + + @Column(name = "y", nullable = false) + private double y; + + @Column(name = "zoom", nullable = false) + private double zoom; + + @Column(name = "whiteboard_id", nullable = false) + private long whiteboardId; + + public Viewport() {} + + public Viewport(double x, double y, double zoom, long whiteboardId) { + this.x = x; + this.y = y; + this.zoom = zoom; + this.whiteboardId = whiteboardId; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public double getX() { + return x; + } + + public void setX(double x) { + this.x = x; + } + + public double getY() { + return y; + } + + public void setY(double y) { + this.y = y; + } + + public double getZoom() { + return zoom; + } + + public void setZoom(double zoom) { + this.zoom = zoom; + } + + public long getWhiteboardId() { + return whiteboardId; + } + + public void setWhiteboardId(long whiteboardId) { + this.whiteboardId = whiteboardId; + } +} diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/model/Whiteboard.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/model/Whiteboard.java new file mode 100644 index 00000000..fe6231ec --- /dev/null +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/model/Whiteboard.java @@ -0,0 +1,75 @@ +package de.tum.cit.aet.devops.teamserverdown.model; + +import static org.hibernate.generator.EventType.INSERT; +import static org.hibernate.generator.EventType.UPDATE; + +import jakarta.persistence.*; +import java.time.Instant; +import org.hibernate.annotations.CurrentTimestamp; + +@Entity +public class Whiteboard { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String title; + + @CurrentTimestamp(event = INSERT) + private Instant createdAt; + + @CurrentTimestamp(event = {INSERT, UPDATE}) + private Instant lastUpdatedAt; + + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "user_id") + private User user; + + public Whiteboard() {} + + public Whiteboard(String title, User user) { + this.title = title; + this.user = user; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public Instant getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(Instant createdAt) { + this.createdAt = createdAt; + } + + public Instant getLastUpdatedAt() { + return lastUpdatedAt; + } + + public void setLastUpdatedAt(Instant lastUpdatedAt) { + this.lastUpdatedAt = lastUpdatedAt; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } +} diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/repository/EdgeRepository.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/repository/EdgeRepository.java new file mode 100644 index 00000000..51cea0f8 --- /dev/null +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/repository/EdgeRepository.java @@ -0,0 +1,20 @@ +package de.tum.cit.aet.devops.teamserverdown.repository; + +import de.tum.cit.aet.devops.teamserverdown.model.Edge; +import jakarta.transaction.Transactional; +import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +@Repository +public interface EdgeRepository extends JpaRepository { + List findAllByWhiteboardId(long whiteboardId); + + @Transactional + @Modifying + @Query("DELETE FROM Edge e WHERE e.whiteboardId = :whiteboardId") + void deleteByWhiteboardId(@Param("whiteboardId") Long whiteboardId); +} diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/repository/NodeRepository.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/repository/NodeRepository.java new file mode 100644 index 00000000..4c3cf7b4 --- /dev/null +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/repository/NodeRepository.java @@ -0,0 +1,20 @@ +package de.tum.cit.aet.devops.teamserverdown.repository; + +import de.tum.cit.aet.devops.teamserverdown.model.Node; +import jakarta.transaction.Transactional; +import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +@Repository +public interface NodeRepository extends JpaRepository { + List findByWhiteboardId(long whiteboardId); + + @Transactional + @Modifying + @Query("DELETE FROM Node n WHERE n.whiteboardId = :whiteboardId") + void deleteByWhiteboardId(@Param("whiteboardId") Long whiteboardId); +} diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/repository/UserRepository.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/repository/UserRepository.java index 9dfa0713..a2d1cf69 100644 --- a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/repository/UserRepository.java +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/repository/UserRepository.java @@ -1,8 +1,11 @@ package de.tum.cit.aet.devops.teamserverdown.repository; import de.tum.cit.aet.devops.teamserverdown.model.User; +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository -public interface UserRepository extends JpaRepository {} +public interface UserRepository extends JpaRepository { + Optional findByEmail(String email); +} diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/repository/UserWhiteboardAccessRepository.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/repository/UserWhiteboardAccessRepository.java new file mode 100644 index 00000000..399ead3a --- /dev/null +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/repository/UserWhiteboardAccessRepository.java @@ -0,0 +1,32 @@ +package de.tum.cit.aet.devops.teamserverdown.repository; + +import de.tum.cit.aet.devops.teamserverdown.model.User; +import de.tum.cit.aet.devops.teamserverdown.model.UserWhiteboardAccess; +import de.tum.cit.aet.devops.teamserverdown.model.Whiteboard; +import java.util.List; +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +@Repository +public interface UserWhiteboardAccessRepository extends JpaRepository { + @Query("SELECT uwa.whiteboard FROM UserWhiteboardAccess uwa WHERE uwa.user.id = :userId") + List findWhiteboardsByUserId(@Param("userId") Long userId); + + @Query("SELECT uwa.user FROM UserWhiteboardAccess uwa WHERE uwa.whiteboard.id = :whiteboardId") + List findUsersByWhiteboardId(@Param("whiteboardId") Long whiteboardId); + + @Query( + "SELECT uwa.whiteboard FROM UserWhiteboardAccess uwa WHERE uwa.whiteboard.id = :whiteboardId AND uwa.user.id = :userId") + Optional findUserWhiteboardById( + @Param("userId") Long userId, @Param("whiteboardId") Long whiteboardId); + + @Modifying + @Query( + "DELETE FROM UserWhiteboardAccess uwa WHERE uwa.user.id = :userId AND uwa.whiteboard.id = :whiteboardId") + void deleteByUserIdAndWhiteboardId( + @Param("userId") Long userId, @Param("whiteboardId") Long whiteboardId); +} diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/repository/ViewportRepository.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/repository/ViewportRepository.java new file mode 100644 index 00000000..452c8de4 --- /dev/null +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/repository/ViewportRepository.java @@ -0,0 +1,9 @@ +package de.tum.cit.aet.devops.teamserverdown.repository; + +import de.tum.cit.aet.devops.teamserverdown.model.Viewport; +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ViewportRepository extends JpaRepository { + Optional findByWhiteboardId(Long whiteboardId); +} diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/repository/WhiteboardRepository.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/repository/WhiteboardRepository.java new file mode 100644 index 00000000..54fc467e --- /dev/null +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/repository/WhiteboardRepository.java @@ -0,0 +1,12 @@ +package de.tum.cit.aet.devops.teamserverdown.repository; + +import de.tum.cit.aet.devops.teamserverdown.model.Whiteboard; +import java.util.List; +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface WhiteboardRepository extends JpaRepository { + Optional findByIdAndUserId(Long id, Long userId); + + List findByUserId(Long userId); +} diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/security/CurrentUser.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/security/CurrentUser.java new file mode 100644 index 00000000..35764a91 --- /dev/null +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/security/CurrentUser.java @@ -0,0 +1,8 @@ +package de.tum.cit.aet.devops.teamserverdown.security; + +import java.lang.annotation.*; + +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface CurrentUser {} diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/security/CurrentUserArgumentResolver.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/security/CurrentUserArgumentResolver.java new file mode 100644 index 00000000..26e0f949 --- /dev/null +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/security/CurrentUserArgumentResolver.java @@ -0,0 +1,28 @@ +package de.tum.cit.aet.devops.teamserverdown.security; + +import de.tum.cit.aet.devops.teamserverdown.model.User; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.core.MethodParameter; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +@Component +public class CurrentUserArgumentResolver implements HandlerMethodArgumentResolver { + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.hasParameterAnnotation(CurrentUser.class) + && parameter.getParameterType().equals(User.class); + } + + @Override + public Object resolveArgument( + MethodParameter parameter, + ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, + org.springframework.web.bind.support.WebDataBinderFactory binderFactory) { + HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest(); + return request.getAttribute("user"); + } +} diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/security/JWTAuthenticationFilter.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/security/JWTAuthenticationFilter.java new file mode 100644 index 00000000..452d6cb8 --- /dev/null +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/security/JWTAuthenticationFilter.java @@ -0,0 +1,58 @@ +package de.tum.cit.aet.devops.teamserverdown.security; + +import com.auth0.jwt.exceptions.JWTVerificationException; +import com.auth0.jwt.interfaces.DecodedJWT; +import de.tum.cit.aet.devops.teamserverdown.model.User; +import de.tum.cit.aet.devops.teamserverdown.services.UserService; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +@Component +public class JWTAuthenticationFilter extends OncePerRequestFilter { + private final JWTValidator jwtValidator; + private final UserService userService; + + public JWTAuthenticationFilter(JWTValidator jwtValidator, UserService userService) { + this.jwtValidator = jwtValidator; + this.userService = userService; + } + + @Override + protected void doFilterInternal( + HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + String authHeader = request.getHeader("Authorization"); + if (authHeader != null && authHeader.startsWith("Bearer ")) { + String token = authHeader.substring(7); + try { + DecodedJWT decoded = jwtValidator.validateToken(token); + User user = userService.getOrCreateUser(decoded); + + request.setAttribute("user", user); + } catch (JWTVerificationException e) { + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + response.getWriter().write("Invalid JWT token"); + return; + } + } else { + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + response.getWriter().write("Missing Authorization header"); + return; + } + + filterChain.doFilter(request, response); + } + + @Override + protected boolean shouldNotFilter(HttpServletRequest request) { + String path = request.getServletPath(); + return path.startsWith("/v3/api-docs") + || path.startsWith("/swagger-ui") + || path.startsWith("/actuator"); + } +} diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/security/JWTValidator.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/security/JWTValidator.java new file mode 100644 index 00000000..57bf89ee --- /dev/null +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/security/JWTValidator.java @@ -0,0 +1,71 @@ +package de.tum.cit.aet.devops.teamserverdown.security; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.JWTVerifier; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.exceptions.JWTVerificationException; +import com.auth0.jwt.interfaces.DecodedJWT; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.InputStream; +import java.math.BigInteger; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.security.KeyFactory; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.RSAPublicKeySpec; +import java.util.Base64; +import org.springframework.stereotype.Service; + +@Service +public class JWTValidator { + private static final String JWKS_URL = + System.getenv("IDP_INTERNAL_URI") + "/protocol/openid-connect/certs"; + + public DecodedJWT validateToken(String token) throws JWTVerificationException { + DecodedJWT decoded = JWT.decode(token); + String kid = decoded.getKeyId(); + + try { + JsonNode jwk = fetchJWKByKid(kid); + RSAPublicKey publicKey = buildPublicKey(jwk); + + Algorithm algorithm = Algorithm.RSA256(publicKey, null); + JWTVerifier verifier = JWT.require(algorithm).build(); + return verifier.verify(token); + } catch (Exception e) { + throw new JWTVerificationException("Invalid token", e); + } + } + + private JsonNode fetchJWKByKid(String kid) throws Exception { + ObjectMapper mapper = new ObjectMapper(); + + HttpClient client = HttpClient.newHttpClient(); + HttpRequest request = HttpRequest.newBuilder().uri(URI.create(JWKS_URL)).GET().build(); + + HttpResponse response = + client.send(request, HttpResponse.BodyHandlers.ofInputStream()); + + JsonNode jwks = mapper.readTree(response.body()).get("keys"); + for (JsonNode key : jwks) { + if (key.get("kid").asText().equals(kid)) { + return key; + } + } + throw new IllegalArgumentException("Public key not found for kid: " + kid); + } + + private RSAPublicKey buildPublicKey(JsonNode jwk) throws Exception { + String nStr = jwk.get("n").asText(); + String eStr = jwk.get("e").asText(); + + BigInteger modulus = new BigInteger(1, Base64.getUrlDecoder().decode(nStr)); + BigInteger exponent = new BigInteger(1, Base64.getUrlDecoder().decode(eStr)); + + RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, exponent); + return (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(keySpec); + } +} diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/services/UserService.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/services/UserService.java new file mode 100644 index 00000000..33647632 --- /dev/null +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/services/UserService.java @@ -0,0 +1,31 @@ +package de.tum.cit.aet.devops.teamserverdown.services; + +import com.auth0.jwt.interfaces.DecodedJWT; +import de.tum.cit.aet.devops.teamserverdown.model.User; +import de.tum.cit.aet.devops.teamserverdown.repository.UserRepository; +import org.springframework.stereotype.Service; + +@Service +public class UserService { + private final UserRepository userRepository; + + public UserService(UserRepository userRepository) { + this.userRepository = userRepository; + } + + public User getOrCreateUser(DecodedJWT decoded) { + String email = decoded.getClaim("email").asString(); + String firstName = decoded.getClaim("given_name").asString(); + String lastName = decoded.getClaim("family_name").asString(); + String userName = decoded.getClaim("preferred_username").asString(); + + return userRepository + .findByEmail(email) + .orElseGet( + () -> { + User newUser = new User(firstName, lastName, userName, email); + + return userRepository.save(newUser); + }); + } +} diff --git a/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/services/UserWhiteboardAccessService.java b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/services/UserWhiteboardAccessService.java new file mode 100644 index 00000000..e1262b1e --- /dev/null +++ b/server/src/main/java/de/tum/cit/aet/devops/teamserverdown/services/UserWhiteboardAccessService.java @@ -0,0 +1,79 @@ +package de.tum.cit.aet.devops.teamserverdown.services; + +import de.tum.cit.aet.devops.teamserverdown.model.User; +import de.tum.cit.aet.devops.teamserverdown.model.UserWhiteboardAccess; +import de.tum.cit.aet.devops.teamserverdown.model.Whiteboard; +import de.tum.cit.aet.devops.teamserverdown.repository.UserRepository; +import de.tum.cit.aet.devops.teamserverdown.repository.UserWhiteboardAccessRepository; +import de.tum.cit.aet.devops.teamserverdown.repository.WhiteboardRepository; +import jakarta.transaction.Transactional; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +@Transactional +public class UserWhiteboardAccessService { + private static final Logger logger = LoggerFactory.getLogger(UserWhiteboardAccessService.class); + + private final UserWhiteboardAccessRepository userWhiteboardAccessRepository; + private final UserRepository userRepository; + private final WhiteboardRepository whiteboardRepository; + + @Autowired + public UserWhiteboardAccessService( + UserWhiteboardAccessRepository userWhiteboardAccessRepository, + UserRepository userRepository, + WhiteboardRepository whiteboardRepository) { + this.userWhiteboardAccessRepository = userWhiteboardAccessRepository; + this.userRepository = userRepository; + this.whiteboardRepository = whiteboardRepository; + } + + public void inviteUsersToWhiteboard(List emails, Long whiteboardId) { + Whiteboard whiteboard = + whiteboardRepository + .findById(whiteboardId) + .orElseThrow(() -> new IllegalArgumentException("Whiteboard not found")); + + for (String email : emails) { + Optional user = userRepository.findByEmail(email); + if (user.isEmpty()) { + logger.warn("User with email {} not found. Skipping invitation.", email); + continue; + } + UserWhiteboardAccess access = new UserWhiteboardAccess(user.get(), whiteboard); + userWhiteboardAccessRepository.save(access); + } + } + + public void removeUsersFromWhiteboard(List userIds, Long whiteboardId) { + for (Long userId : userIds) { + userWhiteboardAccessRepository.deleteByUserIdAndWhiteboardId(userId, whiteboardId); + } + } + + public Set getUserWhiteboards(Long userId) { + List ownedWhiteboards = whiteboardRepository.findByUserId(userId); + List collaborativeWhiteboards = + userWhiteboardAccessRepository.findWhiteboardsByUserId(userId); + + Set allAccessible = new HashSet<>(ownedWhiteboards); + allAccessible.addAll(collaborativeWhiteboards); + return allAccessible; + } + + public Optional getUserWhiteboardById(Long userId, Long whiteboardId) { + Optional whiteboardOpt = + whiteboardRepository.findByIdAndUserId(whiteboardId, userId); + if (whiteboardOpt.isEmpty()) { + whiteboardOpt = userWhiteboardAccessRepository.findUserWhiteboardById(userId, whiteboardId); + } + return whiteboardOpt; + } +} diff --git a/server/src/main/resources/application.yaml b/server/src/main/resources/application.yaml index c6547175..b0e5bd1d 100644 --- a/server/src/main/resources/application.yaml +++ b/server/src/main/resources/application.yaml @@ -1,4 +1,17 @@ +server: + port: 9091 + +springdoc: + swagger-ui: + oauth2-redirect-url: ${SERVER_URL}/swagger-ui/oauth2-redirect.html + spring: + security: + oauth2: + resourceserver: + jwt: + issuer-uri: ${IDP_INTERNAL_URI} + jwk-set-uri: ${IDP_INTERNAL_URI}/protocol/openid-connect/certs datasource: url: jdbc:postgresql://${DB_HOST}:${DB_PORT}/${DB_NAME} username: ${DB_USER} @@ -11,4 +24,13 @@ spring: dialect: org.hibernate.dialect.PostgreSQLDialect flyway: enabled: true - validate-on-migrate: true \ No newline at end of file + validate-on-migrate: true + +management: + endpoints: + web: + exposure: + include: [ "prometheus" ] + endpoint: + prometheus: + access: unrestricted \ No newline at end of file diff --git a/server/src/main/resources/db/migration/V10__add_on_delete_cascade_to_user_whiteboard_access.sql b/server/src/main/resources/db/migration/V10__add_on_delete_cascade_to_user_whiteboard_access.sql new file mode 100644 index 00000000..1fb804c6 --- /dev/null +++ b/server/src/main/resources/db/migration/V10__add_on_delete_cascade_to_user_whiteboard_access.sql @@ -0,0 +1,8 @@ +ALTER TABLE user_whiteboard_access + DROP CONSTRAINT fk_user_whiteboard_access_on_whiteboard; + +ALTER TABLE user_whiteboard_access + ADD CONSTRAINT fk_user_whiteboard_access_on_whiteboard + FOREIGN KEY (whiteboard_id) + REFERENCES whiteboard(id) + ON DELETE CASCADE; \ No newline at end of file diff --git a/server/src/main/resources/db/migration/V202505052245_create_user_table.sql b/server/src/main/resources/db/migration/V1__create_user_table.sql similarity index 96% rename from server/src/main/resources/db/migration/V202505052245_create_user_table.sql rename to server/src/main/resources/db/migration/V1__create_user_table.sql index f417c232..06cc7af2 100644 --- a/server/src/main/resources/db/migration/V202505052245_create_user_table.sql +++ b/server/src/main/resources/db/migration/V1__create_user_table.sql @@ -3,4 +3,5 @@ CREATE TABLE "user" id BIGINT NOT NULL, name VARCHAR(255), CONSTRAINT pk_user PRIMARY KEY (id) -); \ No newline at end of file +); + diff --git a/server/src/main/resources/db/migration/V2__setup_keycloak.sql b/server/src/main/resources/db/migration/V2__setup_keycloak.sql new file mode 100644 index 00000000..5cee0ff3 --- /dev/null +++ b/server/src/main/resources/db/migration/V2__setup_keycloak.sql @@ -0,0 +1,27 @@ +ALTER TABLE "user" + ADD email VARCHAR(255); + +ALTER TABLE "user" + ADD first_name VARCHAR(255); + +ALTER TABLE "user" + ADD last_name VARCHAR(255); + +ALTER TABLE "user" + ADD username VARCHAR(255); + +ALTER TABLE "user" + ALTER COLUMN email SET NOT NULL; + +CREATE INDEX idx_user_email ON "user" (email); + +ALTER TABLE "user" + DROP COLUMN name; + +CREATE SEQUENCE IF NOT EXISTS user_id_seq; +ALTER TABLE "user" + ALTER COLUMN id SET NOT NULL; +ALTER TABLE "user" + ALTER COLUMN id SET DEFAULT nextval('user_id_seq'); + +ALTER SEQUENCE user_id_seq OWNED BY "user".id; \ No newline at end of file diff --git a/server/src/main/resources/db/migration/V3__create_whiteboard_table.sql b/server/src/main/resources/db/migration/V3__create_whiteboard_table.sql new file mode 100644 index 00000000..aa3a0ffb --- /dev/null +++ b/server/src/main/resources/db/migration/V3__create_whiteboard_table.sql @@ -0,0 +1,9 @@ +CREATE TABLE whiteboard +( + id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL, + title VARCHAR(255), + created_at TIMESTAMP WITHOUT TIME ZONE, + last_updated_at TIMESTAMP WITHOUT TIME ZONE, + user_id BIGINT, + CONSTRAINT pk_whiteboard PRIMARY KEY (id) +); \ No newline at end of file diff --git a/server/src/main/resources/db/migration/V4__alter_user_table_email_unique.sql b/server/src/main/resources/db/migration/V4__alter_user_table_email_unique.sql new file mode 100644 index 00000000..7def2a27 --- /dev/null +++ b/server/src/main/resources/db/migration/V4__alter_user_table_email_unique.sql @@ -0,0 +1,2 @@ +ALTER TABLE "user" + ADD CONSTRAINT uc_user_email UNIQUE (email); \ No newline at end of file diff --git a/server/src/main/resources/db/migration/V5__create_node_table.sql b/server/src/main/resources/db/migration/V5__create_node_table.sql new file mode 100644 index 00000000..6a69aaf9 --- /dev/null +++ b/server/src/main/resources/db/migration/V5__create_node_table.sql @@ -0,0 +1,24 @@ +CREATE TABLE nodes +( + id VARCHAR(255) NOT NULL, + whiteboard_id BIGINT NOT NULL, + type VARCHAR(255) NOT NULL, + position_x DOUBLE PRECISION NOT NULL, + position_y DOUBLE PRECISION NOT NULL, + label TEXT, + width DOUBLE PRECISION NOT NULL, + height DOUBLE PRECISION NOT NULL, + color VARCHAR(255), + border_color VARCHAR(255), + border_width INTEGER, + border_opacity DOUBLE PRECISION, + opacity DOUBLE PRECISION, + text_color VARCHAR(255), + font_size INTEGER, + font_family VARCHAR(255), + is_bold BOOLEAN, + is_italic BOOLEAN, + is_strikethrough BOOLEAN, + is_underline BOOLEAN, + CONSTRAINT pk_nodes PRIMARY KEY (id) +); \ No newline at end of file diff --git a/server/src/main/resources/db/migration/V6__create_edge_table.sql b/server/src/main/resources/db/migration/V6__create_edge_table.sql new file mode 100644 index 00000000..a556364c --- /dev/null +++ b/server/src/main/resources/db/migration/V6__create_edge_table.sql @@ -0,0 +1,11 @@ +CREATE TABLE edges +( + id VARCHAR(255) NOT NULL, + whiteboard_id BIGINT NOT NULL, + source VARCHAR(255), + source_handle VARCHAR(255), + target VARCHAR(255), + target_handle VARCHAR(255), + CONSTRAINT pk_edges PRIMARY KEY (id) +); + diff --git a/server/src/main/resources/db/migration/V7__create_viewport_table.sql b/server/src/main/resources/db/migration/V7__create_viewport_table.sql new file mode 100644 index 00000000..c392a005 --- /dev/null +++ b/server/src/main/resources/db/migration/V7__create_viewport_table.sql @@ -0,0 +1,9 @@ +CREATE TABLE viewports +( + id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL, + x DOUBLE PRECISION NOT NULL, + y DOUBLE PRECISION NOT NULL, + zoom DOUBLE PRECISION NOT NULL, + whiteboard_id BIGINT NOT NULL, + CONSTRAINT pk_viewport PRIMARY KEY (id) +); diff --git a/server/src/main/resources/db/migration/V8_on_delete_whiteboard_cascade.sql b/server/src/main/resources/db/migration/V8_on_delete_whiteboard_cascade.sql new file mode 100644 index 00000000..fc0e5429 --- /dev/null +++ b/server/src/main/resources/db/migration/V8_on_delete_whiteboard_cascade.sql @@ -0,0 +1,17 @@ +ALTER TABLE nodes + ADD CONSTRAINT fk_nodes_whiteboard + FOREIGN KEY (whiteboard_id) + REFERENCES whiteboard(id) + ON DELETE CASCADE; + +ALTER TABLE edges + ADD CONSTRAINT fk_edges_whiteboard + FOREIGN KEY (whiteboard_id) + REFERENCES whiteboard(id) + ON DELETE CASCADE; + +ALTER TABLE viewports + ADD CONSTRAINT fk_viewports_whiteboard + FOREIGN KEY (whiteboard_id) + REFERENCES whiteboard(id) + ON DELETE CASCADE; diff --git a/server/src/main/resources/db/migration/V9__create_user_whiteboard_access.sql b/server/src/main/resources/db/migration/V9__create_user_whiteboard_access.sql new file mode 100644 index 00000000..9c6b48fd --- /dev/null +++ b/server/src/main/resources/db/migration/V9__create_user_whiteboard_access.sql @@ -0,0 +1,16 @@ +CREATE TABLE user_whiteboard_access +( + id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL, + user_id BIGINT NOT NULL, + whiteboard_id BIGINT NOT NULL, + CONSTRAINT pk_user_whiteboard_access PRIMARY KEY (id) +); + +ALTER TABLE user_whiteboard_access + ADD CONSTRAINT uc_6e6857928b3cd35a013da0ed2 UNIQUE (user_id, whiteboard_id); + +ALTER TABLE user_whiteboard_access + ADD CONSTRAINT FK_USER_WHITEBOARD_ACCESS_ON_USER FOREIGN KEY (user_id) REFERENCES "user" (id); + +ALTER TABLE user_whiteboard_access + ADD CONSTRAINT FK_USER_WHITEBOARD_ACCESS_ON_WHITEBOARD FOREIGN KEY (whiteboard_id) REFERENCES whiteboard (id); \ No newline at end of file diff --git a/server/src/test/java/de/tum/cit/aet/devops/teamserverdown/services/AccountTest.java b/server/src/test/java/de/tum/cit/aet/devops/teamserverdown/services/AccountTest.java new file mode 100644 index 00000000..11a62ee7 --- /dev/null +++ b/server/src/test/java/de/tum/cit/aet/devops/teamserverdown/services/AccountTest.java @@ -0,0 +1,32 @@ +package de.tum.cit.aet.devops.teamserverdown.services; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import de.tum.cit.aet.devops.teamserverdown.services.config.AuthTestConfig; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; +import org.springframework.test.web.servlet.MockMvc; + +@SpringBootTest +@AutoConfigureMockMvc +@Import(AuthTestConfig.class) +public class AccountTest { + @Autowired private MockMvc mockMvc; + + @Test + void testGetCurrentUser() throws Exception { + mockMvc + .perform(get("/me").header("Authorization", "Bearer Token")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(1)) + .andExpect(jsonPath("$.firstName").value("John")) + .andExpect(jsonPath("$.lastName").value("Doe")) + .andExpect(jsonPath("$.username").value("john.doe")) + .andExpect(jsonPath("$.email").value("john.doe@tum.de")); + ; + } +} diff --git a/server/src/test/java/de/tum/cit/aet/devops/teamserverdown/services/config/AuthTestConfig.java b/server/src/test/java/de/tum/cit/aet/devops/teamserverdown/services/config/AuthTestConfig.java new file mode 100644 index 00000000..cccb8af3 --- /dev/null +++ b/server/src/test/java/de/tum/cit/aet/devops/teamserverdown/services/config/AuthTestConfig.java @@ -0,0 +1,60 @@ +package de.tum.cit.aet.devops.teamserverdown.services.config; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.auth0.jwt.interfaces.Claim; +import com.auth0.jwt.interfaces.DecodedJWT; +import de.tum.cit.aet.devops.teamserverdown.model.User; +import de.tum.cit.aet.devops.teamserverdown.security.JWTValidator; +import de.tum.cit.aet.devops.teamserverdown.services.UserService; +import org.mockito.Mockito; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; + +@TestConfiguration +public class AuthTestConfig { + + @Bean + @Primary + public JWTValidator jwtValidator() { + JWTValidator mockValidator = mock(JWTValidator.class); + DecodedJWT mockJWT = mock(DecodedJWT.class); + + Claim emailClaim = Mockito.mock(Claim.class); + Claim givenNameClaim = Mockito.mock(Claim.class); + Claim familyNameClaim = Mockito.mock(Claim.class); + Claim preferredUsernameClaim = Mockito.mock(Claim.class); + + Mockito.when(mockJWT.getClaim("email")).thenReturn(emailClaim); + Mockito.when(mockJWT.getClaim("given_name")).thenReturn(givenNameClaim); + Mockito.when(mockJWT.getClaim("family_name")).thenReturn(familyNameClaim); + Mockito.when(mockJWT.getClaim("preferred_username")).thenReturn(preferredUsernameClaim); + + Mockito.when(emailClaim.asString()).thenReturn("john.doe@tum.de"); + Mockito.when(givenNameClaim.asString()).thenReturn("John"); + Mockito.when(familyNameClaim.asString()).thenReturn("Doe"); + Mockito.when(preferredUsernameClaim.asString()).thenReturn("john.doe"); + + when(mockValidator.validateToken(Mockito.anyString())).thenReturn(mockJWT); + return mockValidator; + } + + @Bean + @Primary + public UserService userService() { + UserService mockUserService = Mockito.mock(UserService.class); + + User testUser = new User(); + testUser.setEmail("john.doe@tum.de"); + testUser.setFirstName("John"); + testUser.setLastName("Doe"); + testUser.setUsername("john.doe"); + testUser.setId(1L); + + Mockito.when(mockUserService.getOrCreateUser(Mockito.any())).thenReturn(testUser); + + return mockUserService; + } +} diff --git a/server/src/test/resources/application-test.properties b/server/src/test/resources/application-test.properties new file mode 100644 index 00000000..6ac524b7 --- /dev/null +++ b/server/src/test/resources/application-test.properties @@ -0,0 +1,8 @@ +spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE +spring.datasource.driver-class-name=org.h2.Driver +spring.datasource.username=sa +spring.datasource.password= +spring.jpa.hibernate.ddl-auto=create-drop +spring.flyway.enabled=false +logging.level.org.hibernate.SQL=DEBUG +logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE