diff --git a/.github/workflows/deploy_eks.yaml b/.github/workflows/deploy_eks.yaml
new file mode 100644
index 0000000..d953159
--- /dev/null
+++ b/.github/workflows/deploy_eks.yaml
@@ -0,0 +1,300 @@
+name: Deploy to EKS
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+ branches: [main]
+ workflow_dispatch:
+
+concurrency:
+ group: deploy-eks-${{ github.ref }}
+ cancel-in-progress: true
+
+permissions:
+ contents: read
+
+env:
+ AWS_REGION: ${{ vars.AWS_REGION || 'us-east-1' }}
+ EKS_CLUSTER_NAME: ${{ vars.EKS_CLUSTER_NAME || 'stathub-eks' }}
+ K8S_NAMESPACE: stathub
+ ECR_SERVER_REPO: stathub-server
+ ECR_CLIENT_REPO: stathub-client
+
+jobs:
+ # ----------------------------------------------------------------------
+ # Phase 1 - Run unit, integration and e2e tests for client and server.
+ # ----------------------------------------------------------------------
+ test:
+ name: Phase 1 - Tests
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@v6
+
+ - name: Install pnpm
+ uses: pnpm/action-setup@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v6
+ with:
+ node-version: 24.14.0
+ cache: pnpm
+
+ - name: Install server dependencies
+ working-directory: server
+ run: pnpm install --frozen-lockfile
+
+ - name: Run server tests with coverage
+ working-directory: server
+ run: pnpm run test:ci
+
+ - name: Install client dependencies
+ working-directory: client
+ run: pnpm install --frozen-lockfile
+
+ - name: Run client tests with coverage
+ working-directory: client
+ run: pnpm run test:ci
+
+ - name: Upload server test report
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: server-test-report
+ path: |
+ server/junit.xml
+ server/coverage/
+ if-no-files-found: ignore
+
+ - name: Upload client test report
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: client-test-report
+ path: |
+ client/junit.xml
+ client/coverage/
+ if-no-files-found: ignore
+
+ # ----------------------------------------------------------------------
+ # Phase 2 - Provision the S3 artifact bucket and ECR repos with Terraform.
+ # Skipped on pull requests so PRs don't mutate cloud infra.
+ # ----------------------------------------------------------------------
+ terraform:
+ name: Phase 2 - Terraform Apply
+ needs: test
+ if: github.event_name != 'pull_request'
+ runs-on: ubuntu-latest
+ outputs:
+ ecr_server_url: ${{ steps.tf_outputs.outputs.ecr_server_url }}
+ ecr_client_url: ${{ steps.tf_outputs.outputs.ecr_client_url }}
+ bucket_name: ${{ steps.tf_outputs.outputs.bucket_name }}
+ defaults:
+ run:
+ working-directory: infra/terraform
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@v6
+
+ - name: Configure AWS credentials
+ uses: aws-actions/configure-aws-credentials@v4
+ with:
+ aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
+ aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
+ aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }}
+ aws-region: ${{ env.AWS_REGION }}
+
+ - name: Setup Terraform
+ uses: hashicorp/setup-terraform@v3
+ with:
+ terraform_version: 1.10.5
+
+ # Remote backend uses an S3 bucket bootstrapped out of band (see
+ # DEPLOY_EKS.md "State backend bootstrap"). The bucket name is held
+ # in the TF_STATE_BUCKET repo variable so it can be rotated without
+ # editing Terraform source.
+ - name: Terraform Init
+ env:
+ TF_STATE_BUCKET: ${{ vars.TF_STATE_BUCKET }}
+ run: |
+ if [ -z "${TF_STATE_BUCKET}" ]; then
+ echo "::error::Repo variable TF_STATE_BUCKET is not set. See DEPLOY_EKS.md."
+ exit 1
+ fi
+ terraform init -input=false \
+ -backend-config="bucket=${TF_STATE_BUCKET}" \
+ -backend-config="key=stathub/${{ env.AWS_REGION }}/terraform.tfstate" \
+ -backend-config="region=${{ env.AWS_REGION }}"
+
+ - name: Terraform Validate
+ run: terraform validate
+
+ - name: Terraform Plan
+ run: terraform plan -input=false -out=tfplan
+
+ - name: Terraform Apply
+ run: terraform apply -input=false -auto-approve tfplan
+
+ - name: Capture outputs
+ id: tf_outputs
+ run: |
+ echo "ecr_server_url=$(terraform output -raw ecr_server_repository_url)" >> "$GITHUB_OUTPUT"
+ echo "ecr_client_url=$(terraform output -raw ecr_client_repository_url)" >> "$GITHUB_OUTPUT"
+ echo "bucket_name=$(terraform output -raw artifacts_bucket_name)" >> "$GITHUB_OUTPUT"
+
+ # ----------------------------------------------------------------------
+ # Phase 3 - Build server and client images and push them to ECR.
+ # ----------------------------------------------------------------------
+ build_and_push:
+ name: Phase 3 - Build & Push Images
+ needs: terraform
+ if: github.event_name != 'pull_request'
+ runs-on: ubuntu-latest
+ outputs:
+ server_image: ${{ steps.tags.outputs.server_image }}
+ client_image: ${{ steps.tags.outputs.client_image }}
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@v6
+
+ - name: Configure AWS credentials
+ uses: aws-actions/configure-aws-credentials@v4
+ with:
+ aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
+ aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
+ aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }}
+ aws-region: ${{ env.AWS_REGION }}
+
+ - name: Login to Amazon ECR
+ id: ecr
+ uses: aws-actions/amazon-ecr-login@v2
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
+
+ - name: Compute image tags
+ id: tags
+ run: |
+ SHA="${GITHUB_SHA::7}"
+ echo "server_image=${{ needs.terraform.outputs.ecr_server_url }}:${SHA}" >> "$GITHUB_OUTPUT"
+ echo "client_image=${{ needs.terraform.outputs.ecr_client_url }}:${SHA}" >> "$GITHUB_OUTPUT"
+
+ - name: Build and push server image
+ uses: docker/build-push-action@v6
+ with:
+ context: ./server
+ push: true
+ tags: |
+ ${{ steps.tags.outputs.server_image }}
+ ${{ needs.terraform.outputs.ecr_server_url }}:latest
+ cache-from: type=gha,scope=server
+ cache-to: type=gha,mode=max,scope=server
+
+ - name: Build and push client image
+ uses: docker/build-push-action@v6
+ with:
+ context: ./client
+ push: true
+ tags: |
+ ${{ steps.tags.outputs.client_image }}
+ ${{ needs.terraform.outputs.ecr_client_url }}:latest
+ build-args: |
+ VITE_API_BASE_URL=${{ vars.VITE_API_BASE_URL }}
+ VITE_GOOGLE_CLIENT_ID=${{ secrets.VITE_GOOGLE_CLIENT_ID }}
+ cache-from: type=gha,scope=client
+ cache-to: type=gha,mode=max,scope=client
+
+ # ----------------------------------------------------------------------
+ # Phase 4 - Deploy to EKS. Renders manifests with the freshly built image
+ # tags and applies them. The cluster itself is provisioned out of band
+ # (see DEPLOY_EKS.md for the eksctl steps).
+ # ----------------------------------------------------------------------
+ deploy_eks:
+ name: Phase 4 - Deploy to EKS
+ needs: build_and_push
+ if: github.event_name != 'pull_request'
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repo
+ uses: actions/checkout@v6
+
+ - name: Configure AWS credentials
+ uses: aws-actions/configure-aws-credentials@v4
+ with:
+ aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
+ aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
+ aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }}
+ aws-region: ${{ env.AWS_REGION }}
+
+ - name: Install kubectl
+ uses: azure/setup-kubectl@v4
+ with:
+ version: v1.30.0
+
+ - name: Update kubeconfig for EKS cluster
+ run: |
+ aws eks update-kubeconfig \
+ --region "${AWS_REGION}" \
+ --name "${EKS_CLUSTER_NAME}"
+
+ - name: Verify cluster connectivity
+ run: |
+ kubectl version --client
+ kubectl get nodes
+
+ - name: Apply namespace
+ run: kubectl apply -f infra/k8s/namespace.yaml
+
+ - name: Create or update server Secret
+ env:
+ DATABASE_URL: ${{ secrets.SERVER_ENV_DATABASE_URL }}
+ JWT_SECRET: ${{ secrets.SERVER_ENV_JWT_SECRET }}
+ ARCJET_KEY: ${{ secrets.SERVER_ENV_ARCJET_KEY }}
+ ARCJET_MODE: ${{ secrets.SERVER_ENV_ARCJET_MODE }}
+ RESEND_API_KEY: ${{ secrets.SERVER_ENV_RESEND_API_KEY }}
+ GOOGLE_CLIENT_ID: ${{ secrets.SERVER_ENV_GOOGLE_CLIENT_ID }}
+ GOOGLE_CLIENT_SECRET: ${{ secrets.SERVER_ENV_GOOGLE_CLIENT_SECRET }}
+ CLIENT_URL: ${{ vars.SERVER_ENV_CLIENT_URL }}
+ CLIENT_PASSWORD_RESET_URL: ${{ vars.SERVER_ENV_CLIENT_URL }}${{ vars.SERVER_ENV_CLIENT_PASSWORD_RESET_ENDPOINT }}
+ run: |
+ kubectl -n "${K8S_NAMESPACE}" create secret generic stathub-server-secrets \
+ --from-literal=DATABASE_URL="${DATABASE_URL}" \
+ --from-literal=JWT_SECRET="${JWT_SECRET}" \
+ --from-literal=ARCJET_KEY="${ARCJET_KEY}" \
+ --from-literal=ARCJET_MODE="${ARCJET_MODE}" \
+ --from-literal=RESEND_API_KEY="${RESEND_API_KEY}" \
+ --from-literal=GOOGLE_CLIENT_ID="${GOOGLE_CLIENT_ID}" \
+ --from-literal=GOOGLE_CLIENT_SECRET="${GOOGLE_CLIENT_SECRET}" \
+ --from-literal=CLIENT_URL="${CLIENT_URL}" \
+ --from-literal=CLIENT_PASSWORD_RESET_URL="${CLIENT_PASSWORD_RESET_URL}" \
+ --dry-run=client -o yaml | kubectl apply -f -
+
+ - name: Render and apply server manifests
+ env:
+ SERVER_IMAGE: ${{ needs.build_and_push.outputs.server_image }}
+ run: |
+ sed "s|SERVER_IMAGE_PLACEHOLDER|${SERVER_IMAGE}|g" infra/k8s/server-deployment.yaml | kubectl apply -f -
+ kubectl apply -f infra/k8s/server-service.yaml
+ kubectl apply -f infra/k8s/server-service-lb.yaml
+
+ - name: Render and apply client manifests
+ env:
+ CLIENT_IMAGE: ${{ needs.build_and_push.outputs.client_image }}
+ run: |
+ sed "s|CLIENT_IMAGE_PLACEHOLDER|${CLIENT_IMAGE}|g" infra/k8s/client-deployment.yaml | kubectl apply -f -
+ kubectl apply -f infra/k8s/client-service.yaml
+
+ - name: Wait for server rollout
+ run: kubectl -n "${K8S_NAMESPACE}" rollout status deployment/stathub-server --timeout=5m
+
+ - name: Wait for client rollout
+ run: kubectl -n "${K8S_NAMESPACE}" rollout status deployment/stathub-client --timeout=5m
+
+ - name: Print service endpoints
+ run: |
+ echo "Pods:"
+ kubectl -n "${K8S_NAMESPACE}" get pods -o wide
+ echo ""
+ echo "Services:"
+ kubectl -n "${K8S_NAMESPACE}" get svc
diff --git a/.gitignore b/.gitignore
index aca4ebd..16ca97c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,6 +21,7 @@ lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
+junit.xml
# nyc test coverage
.nyc_output
diff --git a/biome.json b/biome.json
index 70574e9..e758105 100644
--- a/biome.json
+++ b/biome.json
@@ -1,5 +1,5 @@
{
- "$schema": "https://biomejs.dev/schemas/2.3.14/schema.json",
+ "$schema": "https://biomejs.dev/schemas/2.4.10/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
diff --git a/client/Dockerfile b/client/Dockerfile
index 45de21e..2ad07a8 100644
--- a/client/Dockerfile
+++ b/client/Dockerfile
@@ -1,36 +1,34 @@
-# Stage 1: Build Step
FROM node:24.14.0-alpine AS build-step
-# Set work directory
WORKDIR /usr/src/app
-# Copy package files and install dependencies
-COPY package.json ./
-COPY pnpm-lock.yaml ./
+COPY package.json pnpm-lock.yaml ./
-# Install dependencies efficiently
-RUN corepack enable
+RUN corepack enable && pnpm install --frozen-lockfile
-RUN pnpm install --frozen-lockfile
-
-# Copy the rest of the application
COPY . .
-# Set environment variables
ARG VITE_API_BASE_URL
ARG VITE_GOOGLE_CLIENT_ID
+ENV VITE_API_BASE_URL=$VITE_API_BASE_URL
+ENV VITE_GOOGLE_CLIENT_ID=$VITE_GOOGLE_CLIENT_ID
-# Build the application
RUN pnpm run build
-# Stage 2: Nginx Server
-FROM nginx:1.28.2-alpine
+# nginx-unprivileged runs as the non-root "nginx" user (uid 101) by default
+# and listens on 8080 instead of 80, which avoids needing CAP_NET_BIND_SERVICE.
+FROM nginxinc/nginx-unprivileged:1.28-alpine
+
+USER root
+RUN apk add --no-cache wget
+USER nginx
+
+COPY --chown=nginx:nginx nginx.conf /etc/nginx/nginx.conf
+COPY --from=build-step --chown=nginx:nginx /usr/src/app/dist /usr/share/nginx/html
-# Copy the Nginx config file
-COPY nginx.conf /etc/nginx/nginx.conf
+EXPOSE 8080
-# Copy built application from build-step
-COPY --from=build-step /usr/src/app/dist /usr/share/nginx/html
+HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
+ CMD wget --quiet --spider http://127.0.0.1:8080/healthz || exit 1
-# Start Nginx
CMD ["nginx", "-g", "daemon off;"]
diff --git a/client/nginx.conf b/client/nginx.conf
index def979a..5720311 100644
--- a/client/nginx.conf
+++ b/client/nginx.conf
@@ -1,37 +1,44 @@
-user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
-pid /var/run/nginx.pid;
+pid /tmp/nginx.pid;
events {
worker_connections 1024;
}
http {
-
include /etc/nginx/mime.types;
default_type application/octet-stream;
+ client_body_temp_path /tmp/client_body;
+ proxy_temp_path /tmp/proxy;
+ fastcgi_temp_path /tmp/fastcgi;
+ uwsgi_temp_path /tmp/uwsgi;
+ scgi_temp_path /tmp/scgi;
+
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
- '$status $body_bytes_sent "$http_referer" '
- '"$http_user_agent" "$http_x_forwarded_for"';
+ '$status $body_bytes_sent "$http_referer" '
+ '"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
- #tcp_nopush on;
-
keepalive_timeout 65;
- #gzip on;
- #include /etc/nginx/conf.d/*.conf;
server {
- listen 80;
+ listen 8080;
+
+ # cheap, dependency-free endpoint for container HEALTHCHECK and k8s probes
+ location = /healthz {
+ access_log off;
+ add_header Content-Type text/plain;
+ return 200 "ok\n";
+ }
+
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
}
-
}
diff --git a/client/package.json b/client/package.json
index 0b21114..a029432 100644
--- a/client/package.json
+++ b/client/package.json
@@ -7,7 +7,10 @@
"scripts": {
"dev": "vite",
"build": "vite build",
- "preview": "vite preview"
+ "preview": "vite preview",
+ "test": "vitest run",
+ "test:watch": "vitest",
+ "test:ci": "vitest run --reporter=default --reporter=junit --outputFile=junit.xml --coverage"
},
"dependencies": {
"@react-oauth/google": "^0.13.5",
@@ -19,11 +22,17 @@
"tailwindcss": "^4.2.0"
},
"devDependencies": {
+ "@testing-library/jest-dom": "^6.9.1",
+ "@testing-library/react": "^16.3.2",
+ "@testing-library/user-event": "^14.6.1",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.1",
+ "@vitest/coverage-v8": "^4.0.7",
"babel-plugin-react-compiler": "^1.0.0",
"globals": "^17.5.0",
- "vite": "^8.0.8"
+ "jsdom": "^28.0.1",
+ "vite": "^8.0.8",
+ "vitest": "^4.0.7"
}
}
diff --git a/client/pnpm-lock.yaml b/client/pnpm-lock.yaml
index 0a2ba3e..9d71fb9 100644
--- a/client/pnpm-lock.yaml
+++ b/client/pnpm-lock.yaml
@@ -13,10 +13,10 @@ importers:
version: 0.13.5(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
'@tailwindcss/vite':
specifier: ^4.2.0
- version: 4.2.2(vite@8.0.8(esbuild@0.27.4)(jiti@2.6.1))
+ version: 4.2.2(vite@8.0.10(esbuild@0.27.4)(jiti@2.6.1))
axios:
specifier: ^1.15.0
- version: 1.15.0
+ version: 1.16.0
react:
specifier: ^19.2.5
version: 19.2.5
@@ -25,11 +25,20 @@ importers:
version: 19.2.5(react@19.2.5)
react-router-dom:
specifier: ^7.14.1
- version: 7.14.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
+ version: 7.14.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
tailwindcss:
specifier: ^4.2.0
version: 4.2.2
devDependencies:
+ '@testing-library/jest-dom':
+ specifier: ^6.9.1
+ version: 6.9.1
+ '@testing-library/react':
+ specifier: ^16.3.2
+ version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
+ '@testing-library/user-event':
+ specifier: ^14.6.1
+ version: 14.6.1(@testing-library/dom@10.4.1)
'@types/react':
specifier: ^19.2.7
version: 19.2.14
@@ -38,19 +47,52 @@ importers:
version: 19.2.3(@types/react@19.2.14)
'@vitejs/plugin-react':
specifier: ^6.0.1
- version: 6.0.1(babel-plugin-react-compiler@1.0.0)(vite@8.0.8(esbuild@0.27.4)(jiti@2.6.1))
+ version: 6.0.1(babel-plugin-react-compiler@1.0.0)(vite@8.0.10(esbuild@0.27.4)(jiti@2.6.1))
+ '@vitest/coverage-v8':
+ specifier: ^4.0.7
+ version: 4.1.5(vitest@4.1.5)
babel-plugin-react-compiler:
specifier: ^1.0.0
version: 1.0.0
globals:
specifier: ^17.5.0
- version: 17.5.0
+ version: 17.6.0
+ jsdom:
+ specifier: ^28.0.1
+ version: 28.1.0
vite:
specifier: ^8.0.8
- version: 8.0.8(esbuild@0.27.4)(jiti@2.6.1)
+ version: 8.0.10(esbuild@0.27.4)(jiti@2.6.1)
+ vitest:
+ specifier: ^4.0.7
+ version: 4.1.5(@vitest/coverage-v8@4.1.5)(jsdom@28.1.0)(vite@8.0.10(esbuild@0.27.4)(jiti@2.6.1))
packages:
+ '@acemir/cssom@0.9.31':
+ resolution: {integrity: sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==}
+
+ '@adobe/css-tools@4.4.4':
+ resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==}
+
+ '@asamuzakjp/css-color@5.1.11':
+ resolution: {integrity: sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
+ '@asamuzakjp/dom-selector@6.8.1':
+ resolution: {integrity: sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==}
+
+ '@asamuzakjp/generational-cache@1.0.1':
+ resolution: {integrity: sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
+ '@asamuzakjp/nwsapi@2.3.9':
+ resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==}
+
+ '@babel/code-frame@7.29.0':
+ resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==}
+ engines: {node: '>=6.9.0'}
+
'@babel/helper-string-parser@7.27.1':
resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
engines: {node: '>=6.9.0'}
@@ -59,15 +101,68 @@ packages:
resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==}
engines: {node: '>=6.9.0'}
+ '@babel/parser@7.29.3':
+ resolution: {integrity: sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/runtime@7.29.2':
+ resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==}
+ engines: {node: '>=6.9.0'}
+
'@babel/types@7.29.0':
resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==}
engines: {node: '>=6.9.0'}
- '@emnapi/core@1.9.2':
- resolution: {integrity: sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==}
+ '@bcoe/v8-coverage@1.0.2':
+ resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==}
+ engines: {node: '>=18'}
+
+ '@bramus/specificity@2.4.2':
+ resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==}
+ hasBin: true
+
+ '@csstools/color-helpers@6.0.2':
+ resolution: {integrity: sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==}
+ engines: {node: '>=20.19.0'}
+
+ '@csstools/css-calc@3.2.0':
+ resolution: {integrity: sha512-bR9e6o2BDB12jzN/gIbjHa5wLJ4UjD1CB9pM7ehlc0ddk6EBz+yYS1EV2MF55/HUxrHcB/hehAyt5vhsA3hx7w==}
+ engines: {node: '>=20.19.0'}
+ peerDependencies:
+ '@csstools/css-parser-algorithms': ^4.0.0
+ '@csstools/css-tokenizer': ^4.0.0
+
+ '@csstools/css-color-parser@4.1.0':
+ resolution: {integrity: sha512-U0KhLYmy2GVj6q4T3WaAe6NPuFYCPQoE3b0dRGxejWDgcPp8TP7S5rVdM5ZrFaqu4N67X8YaPBw14dQSYx3IyQ==}
+ engines: {node: '>=20.19.0'}
+ peerDependencies:
+ '@csstools/css-parser-algorithms': ^4.0.0
+ '@csstools/css-tokenizer': ^4.0.0
+
+ '@csstools/css-parser-algorithms@4.0.0':
+ resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==}
+ engines: {node: '>=20.19.0'}
+ peerDependencies:
+ '@csstools/css-tokenizer': ^4.0.0
+
+ '@csstools/css-syntax-patches-for-csstree@1.1.3':
+ resolution: {integrity: sha512-SH60bMfrRCJF3morcdk57WklujF4Jr/EsQUzqkarfHXEFcAR1gg7fS/chAE922Sehgzc1/+Tz5H3Ypa1HiEKrg==}
+ peerDependencies:
+ css-tree: ^3.2.1
+ peerDependenciesMeta:
+ css-tree:
+ optional: true
- '@emnapi/runtime@1.9.2':
- resolution: {integrity: sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==}
+ '@csstools/css-tokenizer@4.0.0':
+ resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==}
+ engines: {node: '>=20.19.0'}
+
+ '@emnapi/core@1.10.0':
+ resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==}
+
+ '@emnapi/runtime@1.10.0':
+ resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==}
'@emnapi/wasi-threads@1.2.1':
resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==}
@@ -228,6 +323,15 @@ packages:
cpu: [x64]
os: [win32]
+ '@exodus/bytes@1.15.0':
+ resolution: {integrity: sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+ peerDependencies:
+ '@noble/hashes': ^1.8.0 || ^2.0.0
+ peerDependenciesMeta:
+ '@noble/hashes':
+ optional: true
+
'@jridgewell/gen-mapping@0.3.13':
resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
@@ -244,14 +348,14 @@ packages:
'@jridgewell/trace-mapping@0.3.31':
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
- '@napi-rs/wasm-runtime@1.1.3':
- resolution: {integrity: sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ==}
+ '@napi-rs/wasm-runtime@1.1.4':
+ resolution: {integrity: sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==}
peerDependencies:
'@emnapi/core': ^1.7.1
'@emnapi/runtime': ^1.7.1
- '@oxc-project/types@0.124.0':
- resolution: {integrity: sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg==}
+ '@oxc-project/types@0.127.0':
+ resolution: {integrity: sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==}
'@react-oauth/google@0.13.5':
resolution: {integrity: sha512-xQWri2s/3nNekZJ4uuov2aAfQYu83bN3864KcFqw2pK1nNbFurQIjPFDXhWaKH3IjYJ2r/9yyIIpsn5lMqrheQ==}
@@ -259,107 +363,110 @@ packages:
react: '>=16.8.0'
react-dom: '>=16.8.0'
- '@rolldown/binding-android-arm64@1.0.0-rc.15':
- resolution: {integrity: sha512-YYe6aWruPZDtHNpwu7+qAHEMbQ/yRl6atqb/AhznLTnD3UY99Q1jE7ihLSahNWkF4EqRPVC4SiR4O0UkLK02tA==}
+ '@rolldown/binding-android-arm64@1.0.0-rc.17':
+ resolution: {integrity: sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [android]
- '@rolldown/binding-darwin-arm64@1.0.0-rc.15':
- resolution: {integrity: sha512-oArR/ig8wNTPYsXL+Mzhs0oxhxfuHRfG7Ikw7jXsw8mYOtk71W0OkF2VEVh699pdmzjPQsTjlD1JIOoHkLP1Fg==}
+ '@rolldown/binding-darwin-arm64@1.0.0-rc.17':
+ resolution: {integrity: sha512-4ksWc9n0mhlZpZ9PMZgTGjeOPRu8MB1Z3Tz0Mo02eWfWCHMW1zN82Qz/pL/rC+yQa+8ZnutMF0JjJe7PjwasYw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [darwin]
- '@rolldown/binding-darwin-x64@1.0.0-rc.15':
- resolution: {integrity: sha512-YzeVqOqjPYvUbJSWJ4EDL8ahbmsIXQpgL3JVipmN+MX0XnXMeWomLN3Fb+nwCmP/jfyqte5I3XRSm7OfQrbyxw==}
+ '@rolldown/binding-darwin-x64@1.0.0-rc.17':
+ resolution: {integrity: sha512-SUSDOI6WwUVNcWxd02QEBjLdY1VPHvlEkw6T/8nYG322iYWCTxRb1vzk4E+mWWYehTp7ERibq54LSJGjmouOsw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [darwin]
- '@rolldown/binding-freebsd-x64@1.0.0-rc.15':
- resolution: {integrity: sha512-9Erhx956jeQ0nNTyif1+QWAXDRD38ZNjr//bSHrt6wDwB+QkAfl2q6Mn1k6OBPerznjRmbM10lgRb1Pli4xZPw==}
+ '@rolldown/binding-freebsd-x64@1.0.0-rc.17':
+ resolution: {integrity: sha512-hwnz3nw9dbJ05EDO/PvcjaaewqqDy7Y1rn1UO81l8iIK1GjenME75dl16ajbvSSMfv66WXSRCYKIqfgq2KCfxw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [freebsd]
- '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.15':
- resolution: {integrity: sha512-cVwk0w8QbZJGTnP/AHQBs5yNwmpgGYStL88t4UIaqcvYJWBfS0s3oqVLZPwsPU6M0zlW4GqjP0Zq5MnAGwFeGA==}
+ '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.17':
+ resolution: {integrity: sha512-IS+W7epTcwANmFSQFrS1SivEXHtl1JtuQA9wlxrZTcNi6mx+FDOYrakGevvvTwgj2JvWiK8B29/qD9BELZPyXQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm]
os: [linux]
- '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.15':
- resolution: {integrity: sha512-eBZ/u8iAK9SoHGanqe/jrPnY0JvBN6iXbVOsbO38mbz+ZJsaobExAm1Iu+rxa4S1l2FjG0qEZn4Rc6X8n+9M+w==}
+ '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.17':
+ resolution: {integrity: sha512-e6usGaHKW5BMNZOymS1UcEYGowQMWcgZ71Z17Sl/h2+ZziNJ1a9n3Zvcz6LdRyIW5572wBCTH/Z+bKuZouGk9Q==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
libc: [glibc]
- '@rolldown/binding-linux-arm64-musl@1.0.0-rc.15':
- resolution: {integrity: sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ==}
+ '@rolldown/binding-linux-arm64-musl@1.0.0-rc.17':
+ resolution: {integrity: sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
libc: [musl]
- '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.15':
- resolution: {integrity: sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ==}
+ '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.17':
+ resolution: {integrity: sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [ppc64]
os: [linux]
libc: [glibc]
- '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.15':
- resolution: {integrity: sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ==}
+ '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.17':
+ resolution: {integrity: sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [s390x]
os: [linux]
libc: [glibc]
- '@rolldown/binding-linux-x64-gnu@1.0.0-rc.15':
- resolution: {integrity: sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA==}
+ '@rolldown/binding-linux-x64-gnu@1.0.0-rc.17':
+ resolution: {integrity: sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
libc: [glibc]
- '@rolldown/binding-linux-x64-musl@1.0.0-rc.15':
- resolution: {integrity: sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw==}
+ '@rolldown/binding-linux-x64-musl@1.0.0-rc.17':
+ resolution: {integrity: sha512-0phclDw1spsL7dUB37sIARuis2tAgomCJXAHZlpt8PXZ4Ba0dRP1e+66lsRqrfhISeN9bEGNjQs+T/Fbd7oYGw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
libc: [musl]
- '@rolldown/binding-openharmony-arm64@1.0.0-rc.15':
- resolution: {integrity: sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg==}
+ '@rolldown/binding-openharmony-arm64@1.0.0-rc.17':
+ resolution: {integrity: sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [openharmony]
- '@rolldown/binding-wasm32-wasi@1.0.0-rc.15':
- resolution: {integrity: sha512-ApLruZq/ig+nhaE7OJm4lDjayUnOHVUa77zGeqnqZ9pn0ovdVbbNPerVibLXDmWeUZXjIYIT8V3xkT58Rm9u5Q==}
- engines: {node: '>=14.0.0'}
+ '@rolldown/binding-wasm32-wasi@1.0.0-rc.17':
+ resolution: {integrity: sha512-LEXei6vo0E5wTGwpkJ4KoT3OZJRnglwldt5ziLzOlc6qqb55z4tWNq2A+PFqCJuvWWdP53CVhG1Z9NtToDPJrA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
cpu: [wasm32]
- '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.15':
- resolution: {integrity: sha512-KmoUoU7HnN+Si5YWJigfTws1jz1bKBYDQKdbLspz0UaqjjFkddHsqorgiW1mxcAj88lYUE6NC/zJNwT+SloqtA==}
+ '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.17':
+ resolution: {integrity: sha512-gUmyzBl3SPMa6hrqFUth9sVfcLBlYsbMzBx5PlexMroZStgzGqlZ26pYG89rBb45Mnia+oil6YAIFeEWGWhoZA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [win32]
- '@rolldown/binding-win32-x64-msvc@1.0.0-rc.15':
- resolution: {integrity: sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g==}
+ '@rolldown/binding-win32-x64-msvc@1.0.0-rc.17':
+ resolution: {integrity: sha512-3hkiolcUAvPB9FLb3UZdfjVVNWherN1f/skkGWJP/fgSQhYUZpSIRr0/I8ZK9TkF3F7kxvJAk0+IcKvPHk9qQg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [win32]
- '@rolldown/pluginutils@1.0.0-rc.15':
- resolution: {integrity: sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g==}
+ '@rolldown/pluginutils@1.0.0-rc.17':
+ resolution: {integrity: sha512-n8iosDOt6Ig1UhJ2AYqoIhHWh/isz0xpicHTzpKBeotdVsTEcxsSA/i3EVM7gQAj0rU27OLAxCjzlj15IWY7bg==}
'@rolldown/pluginutils@1.0.0-rc.7':
resolution: {integrity: sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==}
+ '@standard-schema/spec@1.1.0':
+ resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
+
'@tailwindcss/node@4.2.2':
resolution: {integrity: sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==}
@@ -454,9 +561,50 @@ packages:
peerDependencies:
vite: ^5.2.0 || ^6 || ^7 || ^8
+ '@testing-library/dom@10.4.1':
+ resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==}
+ engines: {node: '>=18'}
+
+ '@testing-library/jest-dom@6.9.1':
+ resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==}
+ engines: {node: '>=14', npm: '>=6', yarn: '>=1'}
+
+ '@testing-library/react@16.3.2':
+ resolution: {integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@testing-library/dom': ^10.0.0
+ '@types/react': ^18.0.0 || ^19.0.0
+ '@types/react-dom': ^18.0.0 || ^19.0.0
+ react: ^18.0.0 || ^19.0.0
+ react-dom: ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@testing-library/user-event@14.6.1':
+ resolution: {integrity: sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==}
+ engines: {node: '>=12', npm: '>=6'}
+ peerDependencies:
+ '@testing-library/dom': '>=7.21.4'
+
'@tybys/wasm-util@0.10.1':
resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==}
+ '@types/aria-query@5.0.4':
+ resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==}
+
+ '@types/chai@5.2.3':
+ resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==}
+
+ '@types/deep-eql@4.0.2':
+ resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
+
+ '@types/estree@1.0.8':
+ resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
+
'@types/react-dom@19.2.3':
resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==}
peerDependencies:
@@ -478,38 +626,149 @@ packages:
babel-plugin-react-compiler:
optional: true
+ '@vitest/coverage-v8@4.1.5':
+ resolution: {integrity: sha512-38C0/Ddb7HcRG0Z4/DUem8x57d2p9jYgp18mkaYswEOQBGsI1CG4f/hjm0ZCeaJfWhSZ4k7jgs29V1Zom7Ki9A==}
+ peerDependencies:
+ '@vitest/browser': 4.1.5
+ vitest: 4.1.5
+ peerDependenciesMeta:
+ '@vitest/browser':
+ optional: true
+
+ '@vitest/expect@4.1.5':
+ resolution: {integrity: sha512-PWBaRY5JoKuRnHlUHfpV/KohFylaDZTupcXN1H9vYryNLOnitSw60Mw9IAE2r67NbwwzBw/Cc/8q9BK3kIX8Kw==}
+
+ '@vitest/mocker@4.1.5':
+ resolution: {integrity: sha512-/x2EmFC4mT4NNzqvC3fmesuV97w5FC903KPmey4gsnJiMQ3Be1IlDKVaDaG8iqaLFHqJ2FVEkxZk5VmeLjIItw==}
+ peerDependencies:
+ msw: ^2.4.9
+ vite: ^6.0.0 || ^7.0.0 || ^8.0.0
+ peerDependenciesMeta:
+ msw:
+ optional: true
+ vite:
+ optional: true
+
+ '@vitest/pretty-format@4.1.5':
+ resolution: {integrity: sha512-7I3q6l5qr03dVfMX2wCo9FxwSJbPdwKjy2uu/YPpU3wfHvIL4QHwVRp57OfGrDFeUJ8/8QdfBKIV12FTtLn00g==}
+
+ '@vitest/runner@4.1.5':
+ resolution: {integrity: sha512-2D+o7Pr82IEO46YPpoA/YU0neeyr6FTerQb5Ro7BUnBuv6NQtT/kmVnczngiMEBhzgqz2UZYl5gArejsyERDSQ==}
+
+ '@vitest/snapshot@4.1.5':
+ resolution: {integrity: sha512-zypXEt4KH/XgKGPUz4eC2AvErYx0My5hfL8oDb1HzGFpEk1P62bxSohdyOmvz+d9UJwanI68MKwr2EquOaOgMQ==}
+
+ '@vitest/spy@4.1.5':
+ resolution: {integrity: sha512-2lNOsh6+R2Idnf1TCZqSwYlKN2E/iDlD8sgU59kYVl+OMDmvldO1VDk39smRfpUNwYpNRVn3w4YfuC7KfbBnkQ==}
+
+ '@vitest/utils@4.1.5':
+ resolution: {integrity: sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==}
+
+ agent-base@7.1.4:
+ resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
+ engines: {node: '>= 14'}
+
+ ansi-regex@5.0.1:
+ resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+ engines: {node: '>=8'}
+
+ ansi-styles@5.2.0:
+ resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
+ engines: {node: '>=10'}
+
+ aria-query@5.3.0:
+ resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==}
+
+ aria-query@5.3.2:
+ resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
+ engines: {node: '>= 0.4'}
+
+ assertion-error@2.0.1:
+ resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
+ engines: {node: '>=12'}
+
+ ast-v8-to-istanbul@1.0.0:
+ resolution: {integrity: sha512-1fSfIwuDICFA4LKkCzRPO7F0hzFf0B7+Xqrl27ynQaa+Rh0e1Es0v6kWHPott3lU10AyAr7oKHa65OppjLn3Rg==}
+
asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
- axios@1.15.0:
- resolution: {integrity: sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==}
+ axios@1.16.0:
+ resolution: {integrity: sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==}
babel-plugin-react-compiler@1.0.0:
resolution: {integrity: sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw==}
+ bidi-js@1.0.3:
+ resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==}
+
call-bind-apply-helpers@1.0.2:
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
engines: {node: '>= 0.4'}
+ chai@6.2.2:
+ resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==}
+ engines: {node: '>=18'}
+
combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
+ convert-source-map@2.0.0:
+ resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
cookie@1.1.1:
resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==}
engines: {node: '>=18'}
+ css-tree@3.2.1:
+ resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==}
+ engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
+
+ css.escape@1.5.1:
+ resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==}
+
+ cssstyle@6.2.0:
+ resolution: {integrity: sha512-Fm5NvhYathRnXNVndkUsCCuR63DCLVVwGOOwQw782coXFi5HhkXdu289l59HlXZBawsyNccXfWRYvLzcDCdDig==}
+ engines: {node: '>=20'}
+
csstype@3.2.3:
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
+ data-urls@7.0.0:
+ resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
+ debug@4.4.3:
+ resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ decimal.js@10.6.0:
+ resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
+
delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
+ dequal@2.0.3:
+ resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
+ engines: {node: '>=6'}
+
detect-libc@2.1.2:
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
engines: {node: '>=8'}
+ dom-accessibility-api@0.5.16:
+ resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==}
+
+ dom-accessibility-api@0.6.3:
+ resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==}
+
dunder-proto@1.0.1:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'}
@@ -518,6 +777,10 @@ packages:
resolution: {integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==}
engines: {node: '>=10.13.0'}
+ entities@8.0.0:
+ resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==}
+ engines: {node: '>=20.19.0'}
+
es-define-property@1.0.1:
resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
engines: {node: '>= 0.4'}
@@ -526,6 +789,9 @@ packages:
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
engines: {node: '>= 0.4'}
+ es-module-lexer@2.1.0:
+ resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==}
+
es-object-atoms@1.1.1:
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
engines: {node: '>= 0.4'}
@@ -539,6 +805,13 @@ packages:
engines: {node: '>=18'}
hasBin: true
+ estree-walker@3.0.3:
+ resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
+
+ expect-type@1.3.0:
+ resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==}
+ engines: {node: '>=12.0.0'}
+
fdir@6.5.0:
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
engines: {node: '>=12.0.0'}
@@ -577,8 +850,8 @@ packages:
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
engines: {node: '>= 0.4'}
- globals@17.5.0:
- resolution: {integrity: sha512-qoV+HK2yFl/366t2/Cb3+xxPUo5BuMynomoDmiaZBIdbs+0pYbjfZU+twLhGKp4uCZ/+NbtpVepH5bGCxRyy2g==}
+ globals@17.6.0:
+ resolution: {integrity: sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==}
engines: {node: '>=18'}
gopd@1.2.0:
@@ -588,6 +861,10 @@ packages:
graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+ has-flag@4.0.0:
+ resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+ engines: {node: '>=8'}
+
has-symbols@1.1.0:
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
engines: {node: '>= 0.4'}
@@ -600,10 +877,59 @@ packages:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
+ html-encoding-sniffer@6.0.0:
+ resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
+ html-escaper@2.0.2:
+ resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
+
+ http-proxy-agent@7.0.2:
+ resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
+ engines: {node: '>= 14'}
+
+ https-proxy-agent@7.0.6:
+ resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
+ engines: {node: '>= 14'}
+
+ indent-string@4.0.0:
+ resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
+ engines: {node: '>=8'}
+
+ is-potential-custom-element-name@1.0.1:
+ resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
+
+ istanbul-lib-coverage@3.2.2:
+ resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==}
+ engines: {node: '>=8'}
+
+ istanbul-lib-report@3.0.1:
+ resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==}
+ engines: {node: '>=10'}
+
+ istanbul-reports@3.2.0:
+ resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==}
+ engines: {node: '>=8'}
+
jiti@2.6.1:
resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
hasBin: true
+ js-tokens@10.0.0:
+ resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==}
+
+ js-tokens@4.0.0:
+ resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+ jsdom@28.1.0:
+ resolution: {integrity: sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+ peerDependencies:
+ canvas: ^3.0.0
+ peerDependenciesMeta:
+ canvas:
+ optional: true
+
lightningcss-android-arm64@1.32.0:
resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==}
engines: {node: '>= 12.0.0'}
@@ -678,13 +1004,31 @@ packages:
resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==}
engines: {node: '>= 12.0.0'}
+ lru-cache@11.3.5:
+ resolution: {integrity: sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==}
+ engines: {node: 20 || >=22}
+
+ lz-string@1.5.0:
+ resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==}
+ hasBin: true
+
magic-string@0.30.21:
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
+ magicast@0.5.2:
+ resolution: {integrity: sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==}
+
+ make-dir@4.0.0:
+ resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==}
+ engines: {node: '>=10'}
+
math-intrinsics@1.1.0:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
+ mdn-data@2.27.1:
+ resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==}
+
mime-db@1.52.0:
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
engines: {node: '>= 0.6'}
@@ -693,11 +1037,27 @@ packages:
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
engines: {node: '>= 0.6'}
+ min-indent@1.0.1:
+ resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
+ engines: {node: '>=4'}
+
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
nanoid@3.3.11:
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
+ obug@2.1.1:
+ resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==}
+
+ parse5@8.0.1:
+ resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==}
+
+ pathe@2.0.3:
+ resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
+
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
@@ -705,28 +1065,39 @@ packages:
resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==}
engines: {node: '>=12'}
- postcss@8.5.9:
- resolution: {integrity: sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==}
+ postcss@8.5.13:
+ resolution: {integrity: sha512-qif0+jGGZoLWdHey3UFHHWP0H7Gbmsk8T5VEqyYFbWqPr1XqvLGBbk/sl8V5exGmcYJklJOhOQq1pV9IcsiFag==}
engines: {node: ^10 || ^12 || >=14}
+ pretty-format@27.5.1:
+ resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
proxy-from-env@2.1.0:
resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==}
engines: {node: '>=10'}
+ punycode@2.3.1:
+ resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
+ engines: {node: '>=6'}
+
react-dom@19.2.5:
resolution: {integrity: sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==}
peerDependencies:
react: ^19.2.5
- react-router-dom@7.14.1:
- resolution: {integrity: sha512-ZkrQuwwhGibjQLqH1eCdyiZyLWglPxzxdl5tgwgKEyCSGC76vmAjleGocRe3J/MLfzMUIKwaFJWpFVJhK3d2xA==}
+ react-is@17.0.2:
+ resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
+
+ react-router-dom@7.14.2:
+ resolution: {integrity: sha512-YZcM5ES8jJSM+KrJ9BdvHHqlnGTg5tH3sC5ChFRj4inosKctdyzBDhOyyHdGk597q2OT6NTrCA1OvB/YDwfekQ==}
engines: {node: '>=20.0.0'}
peerDependencies:
react: '>=18'
react-dom: '>=18'
- react-router@7.14.1:
- resolution: {integrity: sha512-5BCvFskyAAVumqhEKh/iPhLOIkfxcEUz8WqFIARCkMg8hZZzDYX9CtwxXA0e+qT8zAxmMC0x3Ckb9iMONwc5jg==}
+ react-router@7.14.2:
+ resolution: {integrity: sha512-yCqNne6I8IB6rVCH7XUvlBK7/QKyqypBFGv+8dj4QBFJiiRX+FG7/nkdAvGElyvVZ/HQP5N19wzteuTARXi5Gw==}
engines: {node: '>=20.0.0'}
peerDependencies:
react: '>=18'
@@ -739,21 +1110,58 @@ packages:
resolution: {integrity: sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==}
engines: {node: '>=0.10.0'}
- rolldown@1.0.0-rc.15:
- resolution: {integrity: sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g==}
+ redent@3.0.0:
+ resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==}
+ engines: {node: '>=8'}
+
+ require-from-string@2.0.2:
+ resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
+ engines: {node: '>=0.10.0'}
+
+ rolldown@1.0.0-rc.17:
+ resolution: {integrity: sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
+ saxes@6.0.0:
+ resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
+ engines: {node: '>=v12.22.7'}
+
scheduler@0.27.0:
resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
+ semver@7.7.4:
+ resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==}
+ engines: {node: '>=10'}
+ hasBin: true
+
set-cookie-parser@2.7.2:
resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==}
+ siginfo@2.0.0:
+ resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
+
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
+ stackback@0.0.2:
+ resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
+
+ std-env@4.1.0:
+ resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==}
+
+ strip-indent@3.0.0:
+ resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==}
+ engines: {node: '>=8'}
+
+ supports-color@7.2.0:
+ resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+ engines: {node: '>=8'}
+
+ symbol-tree@3.2.4:
+ resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
+
tailwindcss@4.2.2:
resolution: {integrity: sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==}
@@ -761,15 +1169,45 @@ packages:
resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==}
engines: {node: '>=6'}
+ tinybench@2.9.0:
+ resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
+
+ tinyexec@1.1.2:
+ resolution: {integrity: sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==}
+ engines: {node: '>=18'}
+
tinyglobby@0.2.16:
resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==}
engines: {node: '>=12.0.0'}
+ tinyrainbow@3.1.0:
+ resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==}
+ engines: {node: '>=14.0.0'}
+
+ tldts-core@7.0.30:
+ resolution: {integrity: sha512-uiHN8PIB1VmWyS98eZYja4xzlYqeFZVjb4OuYlJQnZAuJhMw4PbKQOKgHKhBdJR3FE/t5mUQ1Kd80++B+qhD1Q==}
+
+ tldts@7.0.30:
+ resolution: {integrity: sha512-ELrFxuqsDdHUwoh0XxDbxuLD3Wnz49Z57IFvTtvWy1hJdcMZjXLIuonjilCiWHlT2GbE4Wlv1wKVTzDFnXH1aw==}
+ hasBin: true
+
+ tough-cookie@6.0.1:
+ resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==}
+ engines: {node: '>=16'}
+
+ tr46@6.0.0:
+ resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==}
+ engines: {node: '>=20'}
+
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
- vite@8.0.8:
- resolution: {integrity: sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw==}
+ undici@7.25.0:
+ resolution: {integrity: sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==}
+ engines: {node: '>=20.18.1'}
+
+ vite@8.0.10:
+ resolution: {integrity: sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
@@ -811,24 +1249,159 @@ packages:
yaml:
optional: true
+ vitest@4.1.5:
+ resolution: {integrity: sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg==}
+ engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0}
+ hasBin: true
+ peerDependencies:
+ '@edge-runtime/vm': '*'
+ '@opentelemetry/api': ^1.9.0
+ '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0
+ '@vitest/browser-playwright': 4.1.5
+ '@vitest/browser-preview': 4.1.5
+ '@vitest/browser-webdriverio': 4.1.5
+ '@vitest/coverage-istanbul': 4.1.5
+ '@vitest/coverage-v8': 4.1.5
+ '@vitest/ui': 4.1.5
+ happy-dom: '*'
+ jsdom: '*'
+ vite: ^6.0.0 || ^7.0.0 || ^8.0.0
+ peerDependenciesMeta:
+ '@edge-runtime/vm':
+ optional: true
+ '@opentelemetry/api':
+ optional: true
+ '@types/node':
+ optional: true
+ '@vitest/browser-playwright':
+ optional: true
+ '@vitest/browser-preview':
+ optional: true
+ '@vitest/browser-webdriverio':
+ optional: true
+ '@vitest/coverage-istanbul':
+ optional: true
+ '@vitest/coverage-v8':
+ optional: true
+ '@vitest/ui':
+ optional: true
+ happy-dom:
+ optional: true
+ jsdom:
+ optional: true
+
+ w3c-xmlserializer@5.0.0:
+ resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
+ engines: {node: '>=18'}
+
+ webidl-conversions@8.0.1:
+ resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==}
+ engines: {node: '>=20'}
+
+ whatwg-mimetype@5.0.0:
+ resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==}
+ engines: {node: '>=20'}
+
+ whatwg-url@16.0.1:
+ resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==}
+ engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
+
+ why-is-node-running@2.3.0:
+ resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
+ engines: {node: '>=8'}
+ hasBin: true
+
+ xml-name-validator@5.0.0:
+ resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
+ engines: {node: '>=18'}
+
+ xmlchars@2.2.0:
+ resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
+
snapshots:
+ '@acemir/cssom@0.9.31': {}
+
+ '@adobe/css-tools@4.4.4': {}
+
+ '@asamuzakjp/css-color@5.1.11':
+ dependencies:
+ '@asamuzakjp/generational-cache': 1.0.1
+ '@csstools/css-calc': 3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-tokenizer': 4.0.0
+
+ '@asamuzakjp/dom-selector@6.8.1':
+ dependencies:
+ '@asamuzakjp/nwsapi': 2.3.9
+ bidi-js: 1.0.3
+ css-tree: 3.2.1
+ is-potential-custom-element-name: 1.0.1
+ lru-cache: 11.3.5
+
+ '@asamuzakjp/generational-cache@1.0.1': {}
+
+ '@asamuzakjp/nwsapi@2.3.9': {}
+
+ '@babel/code-frame@7.29.0':
+ dependencies:
+ '@babel/helper-validator-identifier': 7.28.5
+ js-tokens: 4.0.0
+ picocolors: 1.1.1
+
'@babel/helper-string-parser@7.27.1': {}
'@babel/helper-validator-identifier@7.28.5': {}
+ '@babel/parser@7.29.3':
+ dependencies:
+ '@babel/types': 7.29.0
+
+ '@babel/runtime@7.29.2': {}
+
'@babel/types@7.29.0':
dependencies:
'@babel/helper-string-parser': 7.27.1
'@babel/helper-validator-identifier': 7.28.5
- '@emnapi/core@1.9.2':
+ '@bcoe/v8-coverage@1.0.2': {}
+
+ '@bramus/specificity@2.4.2':
+ dependencies:
+ css-tree: 3.2.1
+
+ '@csstools/color-helpers@6.0.2': {}
+
+ '@csstools/css-calc@3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)':
+ dependencies:
+ '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-tokenizer': 4.0.0
+
+ '@csstools/css-color-parser@4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)':
+ dependencies:
+ '@csstools/color-helpers': 6.0.2
+ '@csstools/css-calc': 3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0)
+ '@csstools/css-tokenizer': 4.0.0
+
+ '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)':
+ dependencies:
+ '@csstools/css-tokenizer': 4.0.0
+
+ '@csstools/css-syntax-patches-for-csstree@1.1.3(css-tree@3.2.1)':
+ optionalDependencies:
+ css-tree: 3.2.1
+
+ '@csstools/css-tokenizer@4.0.0': {}
+
+ '@emnapi/core@1.10.0':
dependencies:
'@emnapi/wasi-threads': 1.2.1
tslib: 2.8.1
optional: true
- '@emnapi/runtime@1.9.2':
+ '@emnapi/runtime@1.10.0':
dependencies:
tslib: 2.8.1
optional: true
@@ -916,6 +1489,8 @@ snapshots:
'@esbuild/win32-x64@0.27.4':
optional: true
+ '@exodus/bytes@1.15.0': {}
+
'@jridgewell/gen-mapping@0.3.13':
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
@@ -935,73 +1510,75 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.5
- '@napi-rs/wasm-runtime@1.1.3(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)':
+ '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)':
dependencies:
- '@emnapi/core': 1.9.2
- '@emnapi/runtime': 1.9.2
+ '@emnapi/core': 1.10.0
+ '@emnapi/runtime': 1.10.0
'@tybys/wasm-util': 0.10.1
optional: true
- '@oxc-project/types@0.124.0': {}
+ '@oxc-project/types@0.127.0': {}
'@react-oauth/google@0.13.5(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
dependencies:
react: 19.2.5
react-dom: 19.2.5(react@19.2.5)
- '@rolldown/binding-android-arm64@1.0.0-rc.15':
+ '@rolldown/binding-android-arm64@1.0.0-rc.17':
optional: true
- '@rolldown/binding-darwin-arm64@1.0.0-rc.15':
+ '@rolldown/binding-darwin-arm64@1.0.0-rc.17':
optional: true
- '@rolldown/binding-darwin-x64@1.0.0-rc.15':
+ '@rolldown/binding-darwin-x64@1.0.0-rc.17':
optional: true
- '@rolldown/binding-freebsd-x64@1.0.0-rc.15':
+ '@rolldown/binding-freebsd-x64@1.0.0-rc.17':
optional: true
- '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.15':
+ '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.17':
optional: true
- '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.15':
+ '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.17':
optional: true
- '@rolldown/binding-linux-arm64-musl@1.0.0-rc.15':
+ '@rolldown/binding-linux-arm64-musl@1.0.0-rc.17':
optional: true
- '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.15':
+ '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.17':
optional: true
- '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.15':
+ '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.17':
optional: true
- '@rolldown/binding-linux-x64-gnu@1.0.0-rc.15':
+ '@rolldown/binding-linux-x64-gnu@1.0.0-rc.17':
optional: true
- '@rolldown/binding-linux-x64-musl@1.0.0-rc.15':
+ '@rolldown/binding-linux-x64-musl@1.0.0-rc.17':
optional: true
- '@rolldown/binding-openharmony-arm64@1.0.0-rc.15':
+ '@rolldown/binding-openharmony-arm64@1.0.0-rc.17':
optional: true
- '@rolldown/binding-wasm32-wasi@1.0.0-rc.15':
+ '@rolldown/binding-wasm32-wasi@1.0.0-rc.17':
dependencies:
- '@emnapi/core': 1.9.2
- '@emnapi/runtime': 1.9.2
- '@napi-rs/wasm-runtime': 1.1.3(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)
+ '@emnapi/core': 1.10.0
+ '@emnapi/runtime': 1.10.0
+ '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)
optional: true
- '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.15':
+ '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.17':
optional: true
- '@rolldown/binding-win32-x64-msvc@1.0.0-rc.15':
+ '@rolldown/binding-win32-x64-msvc@1.0.0-rc.17':
optional: true
- '@rolldown/pluginutils@1.0.0-rc.15': {}
+ '@rolldown/pluginutils@1.0.0-rc.17': {}
'@rolldown/pluginutils@1.0.0-rc.7': {}
+ '@standard-schema/spec@1.1.0': {}
+
'@tailwindcss/node@4.2.2':
dependencies:
'@jridgewell/remapping': 2.3.5
@@ -1063,18 +1640,63 @@ snapshots:
'@tailwindcss/oxide-win32-arm64-msvc': 4.2.2
'@tailwindcss/oxide-win32-x64-msvc': 4.2.2
- '@tailwindcss/vite@4.2.2(vite@8.0.8(esbuild@0.27.4)(jiti@2.6.1))':
+ '@tailwindcss/vite@4.2.2(vite@8.0.10(esbuild@0.27.4)(jiti@2.6.1))':
dependencies:
'@tailwindcss/node': 4.2.2
'@tailwindcss/oxide': 4.2.2
tailwindcss: 4.2.2
- vite: 8.0.8(esbuild@0.27.4)(jiti@2.6.1)
+ vite: 8.0.10(esbuild@0.27.4)(jiti@2.6.1)
+
+ '@testing-library/dom@10.4.1':
+ dependencies:
+ '@babel/code-frame': 7.29.0
+ '@babel/runtime': 7.29.2
+ '@types/aria-query': 5.0.4
+ aria-query: 5.3.0
+ dom-accessibility-api: 0.5.16
+ lz-string: 1.5.0
+ picocolors: 1.1.1
+ pretty-format: 27.5.1
+
+ '@testing-library/jest-dom@6.9.1':
+ dependencies:
+ '@adobe/css-tools': 4.4.4
+ aria-query: 5.3.2
+ css.escape: 1.5.1
+ dom-accessibility-api: 0.6.3
+ picocolors: 1.1.1
+ redent: 3.0.0
+
+ '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
+ dependencies:
+ '@babel/runtime': 7.29.2
+ '@testing-library/dom': 10.4.1
+ react: 19.2.5
+ react-dom: 19.2.5(react@19.2.5)
+ optionalDependencies:
+ '@types/react': 19.2.14
+ '@types/react-dom': 19.2.3(@types/react@19.2.14)
+
+ '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.1)':
+ dependencies:
+ '@testing-library/dom': 10.4.1
'@tybys/wasm-util@0.10.1':
dependencies:
tslib: 2.8.1
optional: true
+ '@types/aria-query@5.0.4': {}
+
+ '@types/chai@5.2.3':
+ dependencies:
+ '@types/deep-eql': 4.0.2
+ assertion-error: 2.0.1
+
+ '@types/deep-eql@4.0.2': {}
+
+ '@types/estree@1.0.8': {}
+
'@types/react-dom@19.2.3(@types/react@19.2.14)':
dependencies:
'@types/react': 19.2.14
@@ -1083,16 +1705,91 @@ snapshots:
dependencies:
csstype: 3.2.3
- '@vitejs/plugin-react@6.0.1(babel-plugin-react-compiler@1.0.0)(vite@8.0.8(esbuild@0.27.4)(jiti@2.6.1))':
+ '@vitejs/plugin-react@6.0.1(babel-plugin-react-compiler@1.0.0)(vite@8.0.10(esbuild@0.27.4)(jiti@2.6.1))':
dependencies:
'@rolldown/pluginutils': 1.0.0-rc.7
- vite: 8.0.8(esbuild@0.27.4)(jiti@2.6.1)
+ vite: 8.0.10(esbuild@0.27.4)(jiti@2.6.1)
optionalDependencies:
babel-plugin-react-compiler: 1.0.0
+ '@vitest/coverage-v8@4.1.5(vitest@4.1.5)':
+ dependencies:
+ '@bcoe/v8-coverage': 1.0.2
+ '@vitest/utils': 4.1.5
+ ast-v8-to-istanbul: 1.0.0
+ istanbul-lib-coverage: 3.2.2
+ istanbul-lib-report: 3.0.1
+ istanbul-reports: 3.2.0
+ magicast: 0.5.2
+ obug: 2.1.1
+ std-env: 4.1.0
+ tinyrainbow: 3.1.0
+ vitest: 4.1.5(@vitest/coverage-v8@4.1.5)(jsdom@28.1.0)(vite@8.0.10(esbuild@0.27.4)(jiti@2.6.1))
+
+ '@vitest/expect@4.1.5':
+ dependencies:
+ '@standard-schema/spec': 1.1.0
+ '@types/chai': 5.2.3
+ '@vitest/spy': 4.1.5
+ '@vitest/utils': 4.1.5
+ chai: 6.2.2
+ tinyrainbow: 3.1.0
+
+ '@vitest/mocker@4.1.5(vite@8.0.10(esbuild@0.27.4)(jiti@2.6.1))':
+ dependencies:
+ '@vitest/spy': 4.1.5
+ estree-walker: 3.0.3
+ magic-string: 0.30.21
+ optionalDependencies:
+ vite: 8.0.10(esbuild@0.27.4)(jiti@2.6.1)
+
+ '@vitest/pretty-format@4.1.5':
+ dependencies:
+ tinyrainbow: 3.1.0
+
+ '@vitest/runner@4.1.5':
+ dependencies:
+ '@vitest/utils': 4.1.5
+ pathe: 2.0.3
+
+ '@vitest/snapshot@4.1.5':
+ dependencies:
+ '@vitest/pretty-format': 4.1.5
+ '@vitest/utils': 4.1.5
+ magic-string: 0.30.21
+ pathe: 2.0.3
+
+ '@vitest/spy@4.1.5': {}
+
+ '@vitest/utils@4.1.5':
+ dependencies:
+ '@vitest/pretty-format': 4.1.5
+ convert-source-map: 2.0.0
+ tinyrainbow: 3.1.0
+
+ agent-base@7.1.4: {}
+
+ ansi-regex@5.0.1: {}
+
+ ansi-styles@5.2.0: {}
+
+ aria-query@5.3.0:
+ dependencies:
+ dequal: 2.0.3
+
+ aria-query@5.3.2: {}
+
+ assertion-error@2.0.1: {}
+
+ ast-v8-to-istanbul@1.0.0:
+ dependencies:
+ '@jridgewell/trace-mapping': 0.3.31
+ estree-walker: 3.0.3
+ js-tokens: 10.0.0
+
asynckit@0.4.0: {}
- axios@1.15.0:
+ axios@1.16.0:
dependencies:
follow-redirects: 1.16.0
form-data: 4.0.5
@@ -1104,23 +1801,64 @@ snapshots:
dependencies:
'@babel/types': 7.29.0
+ bidi-js@1.0.3:
+ dependencies:
+ require-from-string: 2.0.2
+
call-bind-apply-helpers@1.0.2:
dependencies:
es-errors: 1.3.0
function-bind: 1.1.2
+ chai@6.2.2: {}
+
combined-stream@1.0.8:
dependencies:
delayed-stream: 1.0.0
+ convert-source-map@2.0.0: {}
+
cookie@1.1.1: {}
+ css-tree@3.2.1:
+ dependencies:
+ mdn-data: 2.27.1
+ source-map-js: 1.2.1
+
+ css.escape@1.5.1: {}
+
+ cssstyle@6.2.0:
+ dependencies:
+ '@asamuzakjp/css-color': 5.1.11
+ '@csstools/css-syntax-patches-for-csstree': 1.1.3(css-tree@3.2.1)
+ css-tree: 3.2.1
+ lru-cache: 11.3.5
+
csstype@3.2.3: {}
+ data-urls@7.0.0:
+ dependencies:
+ whatwg-mimetype: 5.0.0
+ whatwg-url: 16.0.1
+ transitivePeerDependencies:
+ - '@noble/hashes'
+
+ debug@4.4.3:
+ dependencies:
+ ms: 2.1.3
+
+ decimal.js@10.6.0: {}
+
delayed-stream@1.0.0: {}
+ dequal@2.0.3: {}
+
detect-libc@2.1.2: {}
+ dom-accessibility-api@0.5.16: {}
+
+ dom-accessibility-api@0.6.3: {}
+
dunder-proto@1.0.1:
dependencies:
call-bind-apply-helpers: 1.0.2
@@ -1132,10 +1870,14 @@ snapshots:
graceful-fs: 4.2.11
tapable: 2.3.0
+ entities@8.0.0: {}
+
es-define-property@1.0.1: {}
es-errors@1.3.0: {}
+ es-module-lexer@2.1.0: {}
+
es-object-atoms@1.1.1:
dependencies:
es-errors: 1.3.0
@@ -1177,6 +1919,12 @@ snapshots:
'@esbuild/win32-x64': 0.27.4
optional: true
+ estree-walker@3.0.3:
+ dependencies:
+ '@types/estree': 1.0.8
+
+ expect-type@1.3.0: {}
+
fdir@6.5.0(picomatch@4.0.4):
optionalDependencies:
picomatch: 4.0.4
@@ -1214,12 +1962,14 @@ snapshots:
dunder-proto: 1.0.1
es-object-atoms: 1.1.1
- globals@17.5.0: {}
+ globals@17.6.0: {}
gopd@1.2.0: {}
graceful-fs@4.2.11: {}
+ has-flag@4.0.0: {}
+
has-symbols@1.1.0: {}
has-tostringtag@1.0.2:
@@ -1230,8 +1980,78 @@ snapshots:
dependencies:
function-bind: 1.1.2
+ html-encoding-sniffer@6.0.0:
+ dependencies:
+ '@exodus/bytes': 1.15.0
+ transitivePeerDependencies:
+ - '@noble/hashes'
+
+ html-escaper@2.0.2: {}
+
+ http-proxy-agent@7.0.2:
+ dependencies:
+ agent-base: 7.1.4
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ https-proxy-agent@7.0.6:
+ dependencies:
+ agent-base: 7.1.4
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ indent-string@4.0.0: {}
+
+ is-potential-custom-element-name@1.0.1: {}
+
+ istanbul-lib-coverage@3.2.2: {}
+
+ istanbul-lib-report@3.0.1:
+ dependencies:
+ istanbul-lib-coverage: 3.2.2
+ make-dir: 4.0.0
+ supports-color: 7.2.0
+
+ istanbul-reports@3.2.0:
+ dependencies:
+ html-escaper: 2.0.2
+ istanbul-lib-report: 3.0.1
+
jiti@2.6.1: {}
+ js-tokens@10.0.0: {}
+
+ js-tokens@4.0.0: {}
+
+ jsdom@28.1.0:
+ dependencies:
+ '@acemir/cssom': 0.9.31
+ '@asamuzakjp/dom-selector': 6.8.1
+ '@bramus/specificity': 2.4.2
+ '@exodus/bytes': 1.15.0
+ cssstyle: 6.2.0
+ data-urls: 7.0.0
+ decimal.js: 10.6.0
+ html-encoding-sniffer: 6.0.0
+ http-proxy-agent: 7.0.2
+ https-proxy-agent: 7.0.6
+ is-potential-custom-element-name: 1.0.1
+ parse5: 8.0.1
+ saxes: 6.0.0
+ symbol-tree: 3.2.4
+ tough-cookie: 6.0.1
+ undici: 7.25.0
+ w3c-xmlserializer: 5.0.0
+ webidl-conversions: 8.0.1
+ whatwg-mimetype: 5.0.0
+ whatwg-url: 16.0.1
+ xml-name-validator: 5.0.0
+ transitivePeerDependencies:
+ - '@noble/hashes'
+ - supports-color
+
lightningcss-android-arm64@1.32.0:
optional: true
@@ -1281,44 +2101,82 @@ snapshots:
lightningcss-win32-arm64-msvc: 1.32.0
lightningcss-win32-x64-msvc: 1.32.0
+ lru-cache@11.3.5: {}
+
+ lz-string@1.5.0: {}
+
magic-string@0.30.21:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
+ magicast@0.5.2:
+ dependencies:
+ '@babel/parser': 7.29.3
+ '@babel/types': 7.29.0
+ source-map-js: 1.2.1
+
+ make-dir@4.0.0:
+ dependencies:
+ semver: 7.7.4
+
math-intrinsics@1.1.0: {}
+ mdn-data@2.27.1: {}
+
mime-db@1.52.0: {}
mime-types@2.1.35:
dependencies:
mime-db: 1.52.0
+ min-indent@1.0.1: {}
+
+ ms@2.1.3: {}
+
nanoid@3.3.11: {}
+ obug@2.1.1: {}
+
+ parse5@8.0.1:
+ dependencies:
+ entities: 8.0.0
+
+ pathe@2.0.3: {}
+
picocolors@1.1.1: {}
picomatch@4.0.4: {}
- postcss@8.5.9:
+ postcss@8.5.13:
dependencies:
nanoid: 3.3.11
picocolors: 1.1.1
source-map-js: 1.2.1
+ pretty-format@27.5.1:
+ dependencies:
+ ansi-regex: 5.0.1
+ ansi-styles: 5.2.0
+ react-is: 17.0.2
+
proxy-from-env@2.1.0: {}
+ punycode@2.3.1: {}
+
react-dom@19.2.5(react@19.2.5):
dependencies:
react: 19.2.5
scheduler: 0.27.0
- react-router-dom@7.14.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5):
+ react-is@17.0.2: {}
+
+ react-router-dom@7.14.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5):
dependencies:
react: 19.2.5
react-dom: 19.2.5(react@19.2.5)
- react-router: 7.14.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
+ react-router: 7.14.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
- react-router@7.14.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5):
+ react-router@7.14.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5):
dependencies:
cookie: 1.1.1
react: 19.2.5
@@ -1328,53 +2186,157 @@ snapshots:
react@19.2.5: {}
- rolldown@1.0.0-rc.15:
+ redent@3.0.0:
+ dependencies:
+ indent-string: 4.0.0
+ strip-indent: 3.0.0
+
+ require-from-string@2.0.2: {}
+
+ rolldown@1.0.0-rc.17:
dependencies:
- '@oxc-project/types': 0.124.0
- '@rolldown/pluginutils': 1.0.0-rc.15
+ '@oxc-project/types': 0.127.0
+ '@rolldown/pluginutils': 1.0.0-rc.17
optionalDependencies:
- '@rolldown/binding-android-arm64': 1.0.0-rc.15
- '@rolldown/binding-darwin-arm64': 1.0.0-rc.15
- '@rolldown/binding-darwin-x64': 1.0.0-rc.15
- '@rolldown/binding-freebsd-x64': 1.0.0-rc.15
- '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.15
- '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.15
- '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.15
- '@rolldown/binding-linux-ppc64-gnu': 1.0.0-rc.15
- '@rolldown/binding-linux-s390x-gnu': 1.0.0-rc.15
- '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.15
- '@rolldown/binding-linux-x64-musl': 1.0.0-rc.15
- '@rolldown/binding-openharmony-arm64': 1.0.0-rc.15
- '@rolldown/binding-wasm32-wasi': 1.0.0-rc.15
- '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.15
- '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.15
+ '@rolldown/binding-android-arm64': 1.0.0-rc.17
+ '@rolldown/binding-darwin-arm64': 1.0.0-rc.17
+ '@rolldown/binding-darwin-x64': 1.0.0-rc.17
+ '@rolldown/binding-freebsd-x64': 1.0.0-rc.17
+ '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.17
+ '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.17
+ '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.17
+ '@rolldown/binding-linux-ppc64-gnu': 1.0.0-rc.17
+ '@rolldown/binding-linux-s390x-gnu': 1.0.0-rc.17
+ '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.17
+ '@rolldown/binding-linux-x64-musl': 1.0.0-rc.17
+ '@rolldown/binding-openharmony-arm64': 1.0.0-rc.17
+ '@rolldown/binding-wasm32-wasi': 1.0.0-rc.17
+ '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.17
+ '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.17
+
+ saxes@6.0.0:
+ dependencies:
+ xmlchars: 2.2.0
scheduler@0.27.0: {}
+ semver@7.7.4: {}
+
set-cookie-parser@2.7.2: {}
+ siginfo@2.0.0: {}
+
source-map-js@1.2.1: {}
+ stackback@0.0.2: {}
+
+ std-env@4.1.0: {}
+
+ strip-indent@3.0.0:
+ dependencies:
+ min-indent: 1.0.1
+
+ supports-color@7.2.0:
+ dependencies:
+ has-flag: 4.0.0
+
+ symbol-tree@3.2.4: {}
+
tailwindcss@4.2.2: {}
tapable@2.3.0: {}
+ tinybench@2.9.0: {}
+
+ tinyexec@1.1.2: {}
+
tinyglobby@0.2.16:
dependencies:
fdir: 6.5.0(picomatch@4.0.4)
picomatch: 4.0.4
+ tinyrainbow@3.1.0: {}
+
+ tldts-core@7.0.30: {}
+
+ tldts@7.0.30:
+ dependencies:
+ tldts-core: 7.0.30
+
+ tough-cookie@6.0.1:
+ dependencies:
+ tldts: 7.0.30
+
+ tr46@6.0.0:
+ dependencies:
+ punycode: 2.3.1
+
tslib@2.8.1:
optional: true
- vite@8.0.8(esbuild@0.27.4)(jiti@2.6.1):
+ undici@7.25.0: {}
+
+ vite@8.0.10(esbuild@0.27.4)(jiti@2.6.1):
dependencies:
lightningcss: 1.32.0
picomatch: 4.0.4
- postcss: 8.5.9
- rolldown: 1.0.0-rc.15
+ postcss: 8.5.13
+ rolldown: 1.0.0-rc.17
tinyglobby: 0.2.16
optionalDependencies:
esbuild: 0.27.4
fsevents: 2.3.3
jiti: 2.6.1
+
+ vitest@4.1.5(@vitest/coverage-v8@4.1.5)(jsdom@28.1.0)(vite@8.0.10(esbuild@0.27.4)(jiti@2.6.1)):
+ dependencies:
+ '@vitest/expect': 4.1.5
+ '@vitest/mocker': 4.1.5(vite@8.0.10(esbuild@0.27.4)(jiti@2.6.1))
+ '@vitest/pretty-format': 4.1.5
+ '@vitest/runner': 4.1.5
+ '@vitest/snapshot': 4.1.5
+ '@vitest/spy': 4.1.5
+ '@vitest/utils': 4.1.5
+ es-module-lexer: 2.1.0
+ expect-type: 1.3.0
+ magic-string: 0.30.21
+ obug: 2.1.1
+ pathe: 2.0.3
+ picomatch: 4.0.4
+ std-env: 4.1.0
+ tinybench: 2.9.0
+ tinyexec: 1.1.2
+ tinyglobby: 0.2.16
+ tinyrainbow: 3.1.0
+ vite: 8.0.10(esbuild@0.27.4)(jiti@2.6.1)
+ why-is-node-running: 2.3.0
+ optionalDependencies:
+ '@vitest/coverage-v8': 4.1.5(vitest@4.1.5)
+ jsdom: 28.1.0
+ transitivePeerDependencies:
+ - msw
+
+ w3c-xmlserializer@5.0.0:
+ dependencies:
+ xml-name-validator: 5.0.0
+
+ webidl-conversions@8.0.1: {}
+
+ whatwg-mimetype@5.0.0: {}
+
+ whatwg-url@16.0.1:
+ dependencies:
+ '@exodus/bytes': 1.15.0
+ tr46: 6.0.0
+ webidl-conversions: 8.0.1
+ transitivePeerDependencies:
+ - '@noble/hashes'
+
+ why-is-node-running@2.3.0:
+ dependencies:
+ siginfo: 2.0.0
+ stackback: 0.0.2
+
+ xml-name-validator@5.0.0: {}
+
+ xmlchars@2.2.0: {}
diff --git a/client/src/api/__tests__/auth.test.js b/client/src/api/__tests__/auth.test.js
new file mode 100644
index 0000000..0320fc8
--- /dev/null
+++ b/client/src/api/__tests__/auth.test.js
@@ -0,0 +1,87 @@
+import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
+
+// Mock axios.create so api/auth.js gets a controllable client.
+const mockPost = vi.fn();
+vi.mock("axios", () => ({
+ default: {
+ create: () => ({ post: mockPost }),
+ },
+}));
+
+const { loginUser, signupUser, googleAuthUser, forgotPasswordUser, resetPasswordUser } =
+ await import("../auth.js");
+
+describe("auth API client", () => {
+ beforeEach(() => {
+ mockPost.mockReset();
+ });
+
+ afterEach(() => {
+ mockPost.mockReset();
+ });
+
+ it("loginUser POSTs credentials and returns response data", async () => {
+ mockPost.mockResolvedValue({ data: { token: "abc", user: { id: 1 } } });
+
+ const result = await loginUser({ email: "a@b.com", password: "x" });
+
+ expect(mockPost).toHaveBeenCalledWith("/api/auth/login", {
+ email: "a@b.com",
+ password: "x",
+ });
+ expect(result).toEqual({ token: "abc", user: { id: 1 } });
+ });
+
+ it("signupUser POSTs the user data", async () => {
+ mockPost.mockResolvedValue({ data: { token: "tok" } });
+
+ await signupUser({ name: "A", email: "a@b.com", password: "secret12" });
+
+ expect(mockPost).toHaveBeenCalledWith("/api/auth/signup", {
+ name: "A",
+ email: "a@b.com",
+ password: "secret12",
+ });
+ });
+
+ it("googleAuthUser POSTs the auth code", async () => {
+ mockPost.mockResolvedValue({ data: { token: "tok" } });
+
+ await googleAuthUser("google-code");
+
+ expect(mockPost).toHaveBeenCalledWith("/api/auth/google", { code: "google-code" });
+ });
+
+ it("forgotPasswordUser POSTs the email", async () => {
+ mockPost.mockResolvedValue({ data: { message: "ok" } });
+
+ await forgotPasswordUser("a@b.com");
+
+ expect(mockPost).toHaveBeenCalledWith("/api/auth/forgot-password", { email: "a@b.com" });
+ });
+
+ it("resetPasswordUser POSTs token and new password", async () => {
+ mockPost.mockResolvedValue({ data: { message: "ok" } });
+
+ await resetPasswordUser("token-123", "newpass12");
+
+ expect(mockPost).toHaveBeenCalledWith("/api/auth/reset-password", {
+ token: "token-123",
+ newPassword: "newpass12",
+ });
+ });
+
+ it("rethrows server-supplied error message when login fails", async () => {
+ mockPost.mockRejectedValue({ response: { data: { error: "Invalid credentials" } } });
+
+ await expect(loginUser({ email: "a@b.com", password: "x" })).rejects.toThrow(
+ "Invalid credentials",
+ );
+ });
+
+ it("falls back to default message when error response has no body", async () => {
+ mockPost.mockRejectedValue(new Error("network down"));
+
+ await expect(signupUser({})).rejects.toThrow("network down");
+ });
+});
diff --git a/client/src/components/FeaturedCarousel.jsx b/client/src/components/FeaturedCarousel.jsx
index 5a69a5f..debd267 100644
--- a/client/src/components/FeaturedCarousel.jsx
+++ b/client/src/components/FeaturedCarousel.jsx
@@ -118,18 +118,18 @@ export default function FeaturedCarousel({ featuredMatches }) {
{featuredMatches.map((match) => (
{/* Decorative accent top border */}
-
+
-
-
+
+
{match.status}
-
+
{match.sport}
diff --git a/client/src/components/__tests__/Button.test.jsx b/client/src/components/__tests__/Button.test.jsx
new file mode 100644
index 0000000..f46592e
--- /dev/null
+++ b/client/src/components/__tests__/Button.test.jsx
@@ -0,0 +1,49 @@
+import { render, screen } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
+import { describe, expect, it, vi } from "vitest";
+import Button from "../Button";
+
+describe("
", () => {
+ it("renders children", () => {
+ render(
);
+ expect(screen.getByRole("button", { name: /click me/i })).toBeInTheDocument();
+ });
+
+ it("shows the loading spinner when isLoading is true", () => {
+ render(
);
+ expect(screen.getByTitle("Loading")).toBeInTheDocument();
+ });
+
+ it("is disabled while loading", () => {
+ render(
);
+ expect(screen.getByRole("button")).toBeDisabled();
+ });
+
+ it("is disabled when disabled prop is set", () => {
+ render(
);
+ expect(screen.getByRole("button")).toBeDisabled();
+ });
+
+ it("invokes onClick when clicked", async () => {
+ const onClick = vi.fn();
+ render(
);
+ await userEvent.click(screen.getByRole("button"));
+ expect(onClick).toHaveBeenCalledTimes(1);
+ });
+
+ it("does not invoke onClick when disabled", async () => {
+ const onClick = vi.fn();
+ render(
+
,
+ );
+ await userEvent.click(screen.getByRole("button"));
+ expect(onClick).not.toHaveBeenCalled();
+ });
+
+ it("applies the outline variant class", () => {
+ render(
);
+ expect(screen.getByRole("button").className).toMatch(/border/);
+ });
+});
diff --git a/client/src/components/__tests__/Input.test.jsx b/client/src/components/__tests__/Input.test.jsx
new file mode 100644
index 0000000..e1da257
--- /dev/null
+++ b/client/src/components/__tests__/Input.test.jsx
@@ -0,0 +1,32 @@
+import { render, screen } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
+import { createRef } from "react";
+import { describe, expect, it, vi } from "vitest";
+import Input from "../Input";
+
+describe("
", () => {
+ it("renders the label and associates it with the input", () => {
+ render(
);
+ const input = screen.getByLabelText(/email/i);
+ expect(input).toBeInTheDocument();
+ expect(input).toHaveAttribute("name", "email");
+ });
+
+ it("renders an error message when error is provided", () => {
+ render(
);
+ expect(screen.getByText("Required")).toBeInTheDocument();
+ });
+
+ it("forwards a ref to the underlying input", () => {
+ const ref = createRef();
+ render(
);
+ expect(ref.current).toBeInstanceOf(HTMLInputElement);
+ });
+
+ it("invokes onChange when the user types", async () => {
+ const onChange = vi.fn();
+ render(
);
+ await userEvent.type(screen.getByLabelText(/email/i), "hi");
+ expect(onChange).toHaveBeenCalled();
+ });
+});
diff --git a/client/src/pages/__tests__/Login.test.jsx b/client/src/pages/__tests__/Login.test.jsx
new file mode 100644
index 0000000..624e1df
--- /dev/null
+++ b/client/src/pages/__tests__/Login.test.jsx
@@ -0,0 +1,88 @@
+import { render, screen, waitFor } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
+import { MemoryRouter } from "react-router-dom";
+import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
+
+// Mock the API layer so the page exercises form behaviour, not network.
+const loginUser = vi.fn();
+vi.mock("../../api/auth", () => ({
+ loginUser: (...args) => loginUser(...args),
+ googleAuthUser: vi.fn(),
+}));
+
+// Mock @react-oauth/google so we don't need a real provider in the tree.
+vi.mock("@react-oauth/google", () => ({
+ useGoogleLogin: () => () => {},
+}));
+
+// Mock useNavigate to observe redirects.
+const navigate = vi.fn();
+vi.mock("react-router-dom", async () => {
+ const actual = await vi.importActual("react-router-dom");
+ return { ...actual, useNavigate: () => navigate };
+});
+
+// Mock AuthContext - the real one has an unrelated bug we don't want to fix here.
+const loginFn = vi.fn();
+vi.mock("../../context/AuthContext", () => ({
+ useAuth: () => ({ login: loginFn }),
+}));
+
+const Login = (await import("../Login")).default;
+
+const renderPage = () =>
+ render(
+
+
+ ,
+ );
+
+describe("
E2E flow", () => {
+ beforeEach(() => {
+ loginUser.mockReset();
+ navigate.mockReset();
+ loginFn.mockReset();
+ });
+
+ afterEach(() => {
+ loginUser.mockReset();
+ });
+
+ it("submits the form and navigates home on success", async () => {
+ loginUser.mockResolvedValue({ token: "tok", user: { id: 1, email: "a@b.com" } });
+
+ renderPage();
+
+ await userEvent.type(screen.getByLabelText(/email address/i), "a@b.com");
+ await userEvent.type(screen.getByLabelText(/password/i), "secret12");
+ await userEvent.click(screen.getByRole("button", { name: /^log in$/i }));
+
+ await waitFor(() => {
+ expect(loginUser).toHaveBeenCalledWith({ email: "a@b.com", password: "secret12" });
+ });
+ expect(loginFn).toHaveBeenCalledWith({ id: 1, email: "a@b.com" }, "tok");
+ expect(navigate).toHaveBeenCalledWith("/");
+ });
+
+ it("shows the server error message when login fails", async () => {
+ loginUser.mockRejectedValue(new Error("Invalid credentials"));
+
+ renderPage();
+
+ await userEvent.type(screen.getByLabelText(/email address/i), "a@b.com");
+ await userEvent.type(screen.getByLabelText(/password/i), "wrongpass");
+ await userEvent.click(screen.getByRole("button", { name: /^log in$/i }));
+
+ expect(await screen.findByText(/invalid credentials/i)).toBeInTheDocument();
+ expect(navigate).not.toHaveBeenCalled();
+ });
+
+ it("blocks submission when fields are empty", async () => {
+ renderPage();
+
+ await userEvent.click(screen.getByRole("button", { name: /^log in$/i }));
+
+ expect(await screen.findByText(/all fields are required/i)).toBeInTheDocument();
+ expect(loginUser).not.toHaveBeenCalled();
+ });
+});
diff --git a/client/src/test/setup.js b/client/src/test/setup.js
new file mode 100644
index 0000000..1efae31
--- /dev/null
+++ b/client/src/test/setup.js
@@ -0,0 +1,7 @@
+import "@testing-library/jest-dom/vitest";
+import { cleanup } from "@testing-library/react";
+import { afterEach } from "vitest";
+
+afterEach(() => {
+ cleanup();
+});
diff --git a/client/vite.config.js b/client/vite.config.js
index 649205b..257acdf 100644
--- a/client/vite.config.js
+++ b/client/vite.config.js
@@ -12,4 +12,15 @@ export default defineConfig({
}),
tailwindcss(),
],
+ test: {
+ environment: "jsdom",
+ globals: true,
+ setupFiles: ["./src/test/setup.js"],
+ css: false,
+ coverage: {
+ provider: "v8",
+ reporter: ["text", "lcov", "json-summary"],
+ include: ["src/components/**", "src/context/**", "src/api/**", "src/utils/**"],
+ },
+ },
});
diff --git a/infra/eks/cluster.yaml b/infra/eks/cluster.yaml
new file mode 100644
index 0000000..7a4ee08
--- /dev/null
+++ b/infra/eks/cluster.yaml
@@ -0,0 +1,45 @@
+# eksctl ClusterConfig tuned for AWS Academy Learner Lab.
+#
+# Why this file exists: Learner Lab denies `iam:CreateRole`, so the default
+# `eksctl create cluster --managed` flow blows up trying to create its own
+# service / node roles. This config tells eksctl to reuse the pre-provisioned
+# `LabRole` for both the EKS control-plane service role and the worker-node
+# instance role.
+#
+# Before applying, replace 541357644124 in the two ARNs below with your own
+# 12-digit account id (visible in `aws sts get-caller-identity`).
+#
+# Apply with:
+# eksctl create cluster -f infra/eks/cluster.yaml
+apiVersion: eksctl.io/v1alpha5
+kind: ClusterConfig
+
+metadata:
+ name: stathub-eks
+ region: us-east-1
+ version: "1.30"
+
+iam:
+ instanceRoleARN: arn:aws:iam::
:role/LabRole
+ withOIDC: false
+
+# Auto Mode is being made the default in upcoming eksctl versions; pin
+# the legacy behaviour explicitly so this config is reproducible.
+autoModeConfig:
+ enabled: false
+
+addons:
+ - name: vpc-cni
+ - name: coredns
+ - name: kube-proxy
+
+managedNodeGroups:
+ - name: stathub-ng
+ instanceType: t3.small
+ desiredCapacity: 2
+ minSize: 2
+ maxSize: 3
+ volumeSize: 20
+ privateNetworking: false
+ iam:
+ instanceRoleARN: arn:aws:iam::541357644124:role/LabRole
diff --git a/infra/k8s/client-deployment.yaml b/infra/k8s/client-deployment.yaml
new file mode 100644
index 0000000..a95fbca
--- /dev/null
+++ b/infra/k8s/client-deployment.yaml
@@ -0,0 +1,75 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: stathub-client
+ namespace: stathub
+ labels:
+ app: stathub-client
+ app.kubernetes.io/part-of: stathub
+spec:
+ replicas: 2
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxSurge: 1
+ maxUnavailable: 0
+ selector:
+ matchLabels:
+ app: stathub-client
+ template:
+ metadata:
+ labels:
+ app: stathub-client
+ spec:
+ securityContext:
+ runAsNonRoot: true
+ runAsUser: 101
+ fsGroup: 101
+ containers:
+ - name: client
+ image: CLIENT_IMAGE_PLACEHOLDER
+ imagePullPolicy: Always
+ ports:
+ - name: http
+ containerPort: 8080
+ resources:
+ requests:
+ cpu: "50m"
+ memory: "64Mi"
+ limits:
+ cpu: "200m"
+ memory: "256Mi"
+ readinessProbe:
+ httpGet:
+ path: /healthz
+ port: http
+ initialDelaySeconds: 3
+ periodSeconds: 10
+ timeoutSeconds: 3
+ failureThreshold: 3
+ livenessProbe:
+ httpGet:
+ path: /healthz
+ port: http
+ initialDelaySeconds: 10
+ periodSeconds: 20
+ timeoutSeconds: 3
+ failureThreshold: 3
+ # The nginx-unprivileged image needs writable /var/cache/nginx and
+ # /tmp; mount tmpfs volumes so we can keep the root FS read-only.
+ volumeMounts:
+ - name: nginx-cache
+ mountPath: /var/cache/nginx
+ - name: tmp
+ mountPath: /tmp
+ securityContext:
+ allowPrivilegeEscalation: false
+ readOnlyRootFilesystem: true
+ capabilities:
+ drop:
+ - ALL
+ volumes:
+ - name: nginx-cache
+ emptyDir: {}
+ - name: tmp
+ emptyDir: {}
diff --git a/infra/k8s/client-service.yaml b/infra/k8s/client-service.yaml
new file mode 100644
index 0000000..ac5df3c
--- /dev/null
+++ b/infra/k8s/client-service.yaml
@@ -0,0 +1,20 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: stathub-client
+ namespace: stathub
+ labels:
+ app: stathub-client
+ annotations:
+ # NLB is the only Load Balancer type allowed by AWS Academy LabRole.
+ service.beta.kubernetes.io/aws-load-balancer-type: nlb
+ service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
+spec:
+ type: LoadBalancer
+ selector:
+ app: stathub-client
+ ports:
+ - name: http
+ port: 80
+ targetPort: http
+ protocol: TCP
diff --git a/infra/k8s/namespace.yaml b/infra/k8s/namespace.yaml
new file mode 100644
index 0000000..6f83324
--- /dev/null
+++ b/infra/k8s/namespace.yaml
@@ -0,0 +1,7 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+ name: stathub
+ labels:
+ name: stathub
+ app.kubernetes.io/part-of: stathub
diff --git a/infra/k8s/secret.example.yaml b/infra/k8s/secret.example.yaml
new file mode 100644
index 0000000..b846caa
--- /dev/null
+++ b/infra/k8s/secret.example.yaml
@@ -0,0 +1,20 @@
+# Example shape of the server Secret. Do NOT commit a populated copy.
+# In CI the workflow creates this Secret from GitHub repo secrets via
+# `kubectl create secret generic --from-literal=...`. For local manual
+# work, copy this file, fill in the values, and apply with kubectl.
+apiVersion: v1
+kind: Secret
+metadata:
+ name: stathub-server-secrets
+ namespace: stathub
+type: Opaque
+stringData:
+ DATABASE_URL: "postgres://USER:PASS@HOST:5432/DBNAME"
+ JWT_SECRET: "change-me"
+ ARCJET_KEY: ""
+ ARCJET_MODE: "DRY_RUN"
+ RESEND_API_KEY: ""
+ GOOGLE_CLIENT_ID: ""
+ GOOGLE_CLIENT_SECRET: ""
+ CLIENT_URL: "http://localhost:5173"
+ CLIENT_PASSWORD_RESET_URL: "http://localhost:5173/reset-password"
diff --git a/infra/k8s/server-deployment.yaml b/infra/k8s/server-deployment.yaml
new file mode 100644
index 0000000..d4c3685
--- /dev/null
+++ b/infra/k8s/server-deployment.yaml
@@ -0,0 +1,73 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: stathub-server
+ namespace: stathub
+ labels:
+ app: stathub-server
+ app.kubernetes.io/part-of: stathub
+spec:
+ replicas: 2
+ strategy:
+ type: RollingUpdate
+ rollingUpdate:
+ maxSurge: 1
+ maxUnavailable: 0
+ selector:
+ matchLabels:
+ app: stathub-server
+ template:
+ metadata:
+ labels:
+ app: stathub-server
+ spec:
+ securityContext:
+ runAsNonRoot: true
+ runAsUser: 1000
+ fsGroup: 1000
+ containers:
+ - name: server
+ image: SERVER_IMAGE_PLACEHOLDER
+ imagePullPolicy: Always
+ ports:
+ - name: http
+ containerPort: 8000
+ env:
+ - name: PORT
+ value: "8000"
+ - name: HOST
+ value: "0.0.0.0"
+ - name: ARCJET_ENV
+ value: "development"
+ envFrom:
+ - secretRef:
+ name: stathub-server-secrets
+ resources:
+ requests:
+ cpu: "100m"
+ memory: "128Mi"
+ limits:
+ cpu: "500m"
+ memory: "512Mi"
+ readinessProbe:
+ httpGet:
+ path: /health
+ port: http
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ timeoutSeconds: 3
+ failureThreshold: 3
+ livenessProbe:
+ httpGet:
+ path: /health
+ port: http
+ initialDelaySeconds: 15
+ periodSeconds: 20
+ timeoutSeconds: 3
+ failureThreshold: 3
+ securityContext:
+ allowPrivilegeEscalation: false
+ readOnlyRootFilesystem: true
+ capabilities:
+ drop:
+ - ALL
diff --git a/infra/k8s/server-service-lb.yaml b/infra/k8s/server-service-lb.yaml
new file mode 100644
index 0000000..7eee39e
--- /dev/null
+++ b/infra/k8s/server-service-lb.yaml
@@ -0,0 +1,22 @@
+# Public LoadBalancer for the API. Useful when the client (which is also
+# served from a separate LB) needs to reach the API from a browser. If you
+# only want to expose the client, you can skip applying this manifest.
+apiVersion: v1
+kind: Service
+metadata:
+ name: stathub-server-lb
+ namespace: stathub
+ labels:
+ app: stathub-server
+ annotations:
+ service.beta.kubernetes.io/aws-load-balancer-type: nlb
+ service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
+spec:
+ type: LoadBalancer
+ selector:
+ app: stathub-server
+ ports:
+ - name: http
+ port: 80
+ targetPort: http
+ protocol: TCP
diff --git a/infra/k8s/server-service.yaml b/infra/k8s/server-service.yaml
new file mode 100644
index 0000000..d31b153
--- /dev/null
+++ b/infra/k8s/server-service.yaml
@@ -0,0 +1,16 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: stathub-server
+ namespace: stathub
+ labels:
+ app: stathub-server
+spec:
+ type: ClusterIP
+ selector:
+ app: stathub-server
+ ports:
+ - name: http
+ port: 80
+ targetPort: http
+ protocol: TCP
diff --git a/infra/terraform/.gitignore b/infra/terraform/.gitignore
new file mode 100644
index 0000000..8467abb
--- /dev/null
+++ b/infra/terraform/.gitignore
@@ -0,0 +1,12 @@
+.terraform/
+*.tfstate
+*.tfstate.*
+*.tfvars
+crash.log
+crash.*.log
+override.tf
+override.tf.json
+*_override.tf
+*_override.tf.json
+.terraformrc
+terraform.rc
diff --git a/infra/terraform/main.tf b/infra/terraform/main.tf
new file mode 100644
index 0000000..d2d3b7e
--- /dev/null
+++ b/infra/terraform/main.tf
@@ -0,0 +1,164 @@
+provider "aws" {
+ region = var.aws_region
+
+ default_tags {
+ tags = {
+ Project = var.project
+ Environment = var.environment
+ ManagedBy = "terraform"
+ }
+ }
+}
+
+# Random suffix keeps the bucket name globally unique even when the
+# stack is destroyed and recreated.
+resource "random_id" "bucket_suffix" {
+ byte_length = 4
+}
+
+locals {
+ bucket_name = "${var.bucket_name_prefix}-${random_id.bucket_suffix.hex}"
+}
+
+resource "aws_s3_bucket" "artifacts" {
+ bucket = local.bucket_name
+}
+
+resource "aws_s3_bucket_versioning" "artifacts" {
+ bucket = aws_s3_bucket.artifacts.id
+
+ versioning_configuration {
+ status = "Enabled"
+ }
+}
+
+resource "aws_s3_bucket_server_side_encryption_configuration" "artifacts" {
+ bucket = aws_s3_bucket.artifacts.id
+
+ rule {
+ apply_server_side_encryption_by_default {
+ sse_algorithm = "AES256"
+ }
+ }
+}
+
+resource "aws_s3_bucket_public_access_block" "artifacts" {
+ bucket = aws_s3_bucket.artifacts.id
+
+ block_public_acls = true
+ block_public_policy = true
+ ignore_public_acls = true
+ restrict_public_buckets = true
+}
+
+resource "aws_s3_bucket_ownership_controls" "artifacts" {
+ bucket = aws_s3_bucket.artifacts.id
+
+ rule {
+ object_ownership = "BucketOwnerEnforced"
+ }
+}
+
+# Two ECR repos so server and client images are kept separate.
+# SHA-tagged images are immutable to prevent silent rewrites of a deployed
+# build; `latest` is excluded from the rule because the CI workflow
+# re-points it at every successful build.
+resource "aws_ecr_repository" "server" {
+ name = "${var.project}-server"
+ image_tag_mutability = "IMMUTABLE_WITH_EXCLUSION"
+
+ image_tag_mutability_exclusion_filter {
+ filter = "latest"
+ filter_type = "WILDCARD"
+ }
+
+ image_scanning_configuration {
+ scan_on_push = true
+ }
+
+ encryption_configuration {
+ encryption_type = "AES256"
+ }
+}
+
+resource "aws_ecr_repository" "client" {
+ name = "${var.project}-client"
+ image_tag_mutability = "IMMUTABLE_WITH_EXCLUSION"
+
+ image_tag_mutability_exclusion_filter {
+ filter = "latest"
+ filter_type = "WILDCARD"
+ }
+
+ image_scanning_configuration {
+ scan_on_push = true
+ }
+
+ encryption_configuration {
+ encryption_type = "AES256"
+ }
+}
+
+# Keep the registries from growing unbounded - hold the most recent 10
+# tagged images and prune anything older or untagged after a day.
+resource "aws_ecr_lifecycle_policy" "server" {
+ repository = aws_ecr_repository.server.name
+
+ policy = jsonencode({
+ rules = [
+ {
+ rulePriority = 1
+ description = "Keep last 10 tagged images"
+ selection = {
+ tagStatus = "tagged"
+ tagPatternList = ["*"]
+ countType = "imageCountMoreThan"
+ countNumber = 10
+ }
+ action = { type = "expire" }
+ },
+ {
+ rulePriority = 2
+ description = "Expire untagged images after 1 day"
+ selection = {
+ tagStatus = "untagged"
+ countType = "sinceImagePushed"
+ countUnit = "days"
+ countNumber = 1
+ }
+ action = { type = "expire" }
+ }
+ ]
+ })
+}
+
+resource "aws_ecr_lifecycle_policy" "client" {
+ repository = aws_ecr_repository.client.name
+
+ policy = jsonencode({
+ rules = [
+ {
+ rulePriority = 1
+ description = "Keep last 10 tagged images"
+ selection = {
+ tagStatus = "tagged"
+ tagPatternList = ["*"]
+ countType = "imageCountMoreThan"
+ countNumber = 10
+ }
+ action = { type = "expire" }
+ },
+ {
+ rulePriority = 2
+ description = "Expire untagged images after 1 day"
+ selection = {
+ tagStatus = "untagged"
+ countType = "sinceImagePushed"
+ countUnit = "days"
+ countNumber = 1
+ }
+ action = { type = "expire" }
+ }
+ ]
+ })
+}
diff --git a/infra/terraform/outputs.tf b/infra/terraform/outputs.tf
new file mode 100644
index 0000000..3f5cc26
--- /dev/null
+++ b/infra/terraform/outputs.tf
@@ -0,0 +1,19 @@
+output "artifacts_bucket_name" {
+ description = "Name of the artifacts S3 bucket."
+ value = aws_s3_bucket.artifacts.bucket
+}
+
+output "artifacts_bucket_arn" {
+ description = "ARN of the artifacts S3 bucket."
+ value = aws_s3_bucket.artifacts.arn
+}
+
+output "ecr_server_repository_url" {
+ description = "URL of the ECR repository for the server image."
+ value = aws_ecr_repository.server.repository_url
+}
+
+output "ecr_client_repository_url" {
+ description = "URL of the ECR repository for the client image."
+ value = aws_ecr_repository.client.repository_url
+}
diff --git a/infra/terraform/variables.tf b/infra/terraform/variables.tf
new file mode 100644
index 0000000..36cb8a5
--- /dev/null
+++ b/infra/terraform/variables.tf
@@ -0,0 +1,23 @@
+variable "aws_region" {
+ description = "AWS region for all resources."
+ type = string
+ default = "us-east-1"
+}
+
+variable "project" {
+ description = "Short project identifier used as a prefix in resource names."
+ type = string
+ default = "stathub"
+}
+
+variable "environment" {
+ description = "Environment name (dev, prod, ...)."
+ type = string
+ default = "dev"
+}
+
+variable "bucket_name_prefix" {
+ description = "Prefix for the S3 artifacts bucket. A random suffix is appended to make it globally unique."
+ type = string
+ default = "stathub-artifacts"
+}
diff --git a/infra/terraform/versions.tf b/infra/terraform/versions.tf
new file mode 100644
index 0000000..dcc14c5
--- /dev/null
+++ b/infra/terraform/versions.tf
@@ -0,0 +1,25 @@
+terraform {
+ # 1.10 added native S3 state locking via `use_lockfile`, so we don't need
+ # a separate DynamoDB table.
+ required_version = ">= 1.10.0"
+
+ # Partial backend config. The bucket / key / region are supplied at init
+ # time via `-backend-config=...` so the state bucket name doesn't have to
+ # be hard-coded into source.
+ backend "s3" {
+ encrypt = true
+ use_lockfile = true
+ }
+
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ # 6.10+ for `image_tag_mutability_exclusion_filter` on aws_ecr_repository.
+ version = "~> 6.10"
+ }
+ random = {
+ source = "hashicorp/random"
+ version = "~> 3.6"
+ }
+ }
+}
diff --git a/server/Dockerfile b/server/Dockerfile
index 96a99ab..dce60f8 100644
--- a/server/Dockerfile
+++ b/server/Dockerfile
@@ -1,23 +1,31 @@
FROM node:24.14.0-alpine AS builder
-RUN npm i -g pnpm
-
-RUN node -v
-RUN pnpm -v
+RUN npm i -g pnpm@10.30.3
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
-COPY src ./src
+RUN pnpm install --frozen-lockfile --prod
-RUN pnpm install --frozen-lockfile
+COPY src ./src
FROM node:24.14.0-alpine AS production
+RUN apk add --no-cache wget && \
+ addgroup -S app && adduser -S app -G app
+
WORKDIR /app
-COPY --from=builder /app/ /app
+COPY --from=builder --chown=app:app /app/node_modules ./node_modules
+COPY --from=builder --chown=app:app /app/package.json ./package.json
+COPY --from=builder --chown=app:app /app/src ./src
+
+USER app
+ENV PORT=8000
EXPOSE 8000
+HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
+ CMD wget --quiet --spider http://127.0.0.1:${PORT}/health || exit 1
+
CMD ["node", "src/index.js"]
diff --git a/server/__tests__/e2e/auth-flow.test.js b/server/__tests__/e2e/auth-flow.test.js
new file mode 100644
index 0000000..354a5e5
--- /dev/null
+++ b/server/__tests__/e2e/auth-flow.test.js
@@ -0,0 +1,116 @@
+import { beforeEach, describe, expect, it, jest } from "@jest/globals";
+
+// Lightweight in-memory backing store. The mocked drizzle methods read and
+// write here so a signup followed by a login can be exercised end-to-end.
+const store = { users: [] };
+let nextId = 1;
+
+jest.unstable_mockModule("../../src/db/db.js", () => {
+ let lastInsertValues;
+ let lastUpdateValues;
+
+ const builder = {
+ select: () => ({
+ from: () => ({
+ // drizzle's eq() returns an opaque comparison object that we
+ // can't introspect, so we just return every user and let the
+ // controller's subsequent checks decide. The e2e flow only
+ // looks up users by email, so this is fine.
+ where: async () => store.users.slice(),
+ }),
+ }),
+ insert: () => ({
+ values: (v) => {
+ lastInsertValues = v;
+ return {
+ returning: async () => {
+ const row = {
+ id: nextId++,
+ role: "user",
+ metadata: {},
+ isDeleted: false,
+ ...lastInsertValues,
+ };
+ store.users.push(row);
+ return [row];
+ },
+ };
+ },
+ }),
+ update: () => ({
+ set: (v) => {
+ lastUpdateValues = v;
+ return {
+ where: () => ({
+ returning: async () => {
+ const target = store.users[0];
+ if (target) Object.assign(target, lastUpdateValues);
+ return [target];
+ },
+ }),
+ };
+ },
+ }),
+ };
+
+ return { db: builder, pool: { end: jest.fn() } };
+});
+
+jest.unstable_mockModule("../../src/arcjet.js", () => ({
+ httpArcjet: null,
+ wsArcjet: null,
+ securityMiddleware: () => (_req, _res, next) => next(),
+ wsSecurityMiddleware: async () => ({ success: true }),
+}));
+
+const request = (await import("supertest")).default;
+const { createApp } = await import("../../src/app.js");
+const { verifyToken } = await import("../../src/utils/jwt.js");
+
+const app = createApp();
+
+describe("E2E: signup then login", () => {
+ beforeEach(() => {
+ store.users = [];
+ nextId = 1;
+ });
+
+ it("a freshly signed-up user can log in with the same credentials", async () => {
+ const credentials = {
+ name: "Eve",
+ email: "eve@example.com",
+ password: "secret12",
+ };
+
+ const signup = await request(app).post("/api/auth/signup").send(credentials);
+ expect(signup.status).toBe(201);
+ expect(signup.body.user.email).toBe(credentials.email);
+
+ const decoded = verifyToken(signup.body.token);
+ expect(decoded.id).toBe(signup.body.user.id);
+
+ const login = await request(app).post("/api/auth/login").send({
+ email: credentials.email,
+ password: credentials.password,
+ });
+
+ expect(login.status).toBe(200);
+ expect(login.body.user.email).toBe(credentials.email);
+ expect(typeof login.body.token).toBe("string");
+ });
+
+ it("login with the wrong password is rejected", async () => {
+ await request(app).post("/api/auth/signup").send({
+ name: "Eve",
+ email: "eve@example.com",
+ password: "secret12",
+ });
+
+ const res = await request(app).post("/api/auth/login").send({
+ email: "eve@example.com",
+ password: "wrongpass",
+ });
+
+ expect(res.status).toBe(401);
+ });
+});
diff --git a/server/__tests__/integration/auth.test.js b/server/__tests__/integration/auth.test.js
new file mode 100644
index 0000000..df70333
--- /dev/null
+++ b/server/__tests__/integration/auth.test.js
@@ -0,0 +1,131 @@
+import { beforeEach, describe, expect, it, jest } from "@jest/globals";
+
+// In-memory user store used by the mocked db query builder.
+const store = { users: [] };
+let nextId = 1;
+
+// Minimal stand-in for drizzle's chained query API. It only needs to support
+// the call patterns used by authController.js: select().from().where(),
+// insert().values().returning(), update().set().where().returning().
+function buildSelect() {
+ const q = {
+ from: () => q,
+ where: async () => store.users.slice(),
+ };
+ return q;
+}
+
+function buildInsert() {
+ let pendingValues;
+ const q = {
+ values: (v) => {
+ pendingValues = v;
+ return q;
+ },
+ returning: async () => {
+ const row = { id: nextId++, role: "user", metadata: {}, ...pendingValues };
+ store.users.push(row);
+ return [row];
+ },
+ };
+ return q;
+}
+
+function buildUpdate() {
+ let pendingSet;
+ const q = {
+ set: (v) => {
+ pendingSet = v;
+ return q;
+ },
+ where: () => q,
+ returning: async () => {
+ if (store.users[0]) Object.assign(store.users[0], pendingSet);
+ return [store.users[0]];
+ },
+ };
+ return q;
+}
+
+jest.unstable_mockModule("../../src/db/db.js", () => ({
+ db: {
+ select: () => buildSelect(),
+ insert: () => buildInsert(),
+ update: () => buildUpdate(),
+ },
+ pool: { end: jest.fn() },
+}));
+
+jest.unstable_mockModule("../../src/arcjet.js", () => ({
+ httpArcjet: null,
+ wsArcjet: null,
+ securityMiddleware: () => (_req, _res, next) => next(),
+ wsSecurityMiddleware: async () => ({ success: true }),
+}));
+
+const request = (await import("supertest")).default;
+const { createApp } = await import("../../src/app.js");
+
+const app = createApp();
+
+describe("POST /api/auth/signup", () => {
+ beforeEach(() => {
+ store.users = [];
+ nextId = 1;
+ });
+
+ it("returns 201 and a JWT for a valid signup", async () => {
+ const res = await request(app).post("/api/auth/signup").send({
+ name: "Alice",
+ email: "alice@example.com",
+ password: "secret12",
+ });
+
+ expect(res.status).toBe(201);
+ expect(res.body.user.email).toBe("alice@example.com");
+ expect(res.body.user).not.toHaveProperty("password");
+ expect(typeof res.body.token).toBe("string");
+ });
+
+ it("returns 400 when validation fails", async () => {
+ const res = await request(app).post("/api/auth/signup").send({
+ name: "A",
+ email: "not-an-email",
+ password: "x",
+ });
+ expect(res.status).toBe(400);
+ expect(Array.isArray(res.body.error)).toBe(true);
+ });
+
+ it("returns 409 when email already exists", async () => {
+ store.users = [{ id: 1, email: "alice@example.com", role: "user", isDeleted: false }];
+
+ const res = await request(app).post("/api/auth/signup").send({
+ name: "Alice",
+ email: "alice@example.com",
+ password: "secret12",
+ });
+
+ expect(res.status).toBe(409);
+ });
+});
+
+describe("POST /api/auth/login", () => {
+ it("returns 401 when no user is found", async () => {
+ store.users = [];
+
+ const res = await request(app).post("/api/auth/login").send({
+ email: "missing@example.com",
+ password: "anything",
+ });
+ expect(res.status).toBe(401);
+ });
+
+ it("returns 400 when validation fails", async () => {
+ const res = await request(app).post("/api/auth/login").send({
+ email: "not-an-email",
+ password: "",
+ });
+ expect(res.status).toBe(400);
+ });
+});
diff --git a/server/__tests__/integration/health.test.js b/server/__tests__/integration/health.test.js
new file mode 100644
index 0000000..6e02eef
--- /dev/null
+++ b/server/__tests__/integration/health.test.js
@@ -0,0 +1,36 @@
+import { describe, expect, it, jest } from "@jest/globals";
+
+// Stub modules that touch external services so importing the app stays cheap.
+jest.unstable_mockModule("../../src/db/db.js", () => ({
+ db: {},
+ pool: { end: jest.fn() },
+}));
+jest.unstable_mockModule("../../src/arcjet.js", () => ({
+ httpArcjet: null,
+ wsArcjet: null,
+ securityMiddleware: () => (_req, _res, next) => next(),
+ wsSecurityMiddleware: async () => ({ success: true }),
+}));
+
+const request = (await import("supertest")).default;
+const { createApp } = await import("../../src/app.js");
+
+describe("GET /health", () => {
+ const app = createApp();
+
+ it("returns 200 with status ok", async () => {
+ const res = await request(app).get("/health");
+ expect(res.status).toBe(200);
+ expect(res.body).toEqual({ status: "ok" });
+ });
+});
+
+describe("GET /", () => {
+ const app = createApp();
+
+ it("returns the root greeting", async () => {
+ const res = await request(app).get("/");
+ expect(res.status).toBe(200);
+ expect(res.text).toContain("Server is up");
+ });
+});
diff --git a/server/__tests__/setup/env.js b/server/__tests__/setup/env.js
new file mode 100644
index 0000000..b8ac91c
--- /dev/null
+++ b/server/__tests__/setup/env.js
@@ -0,0 +1,13 @@
+// Test-only env. Loaded before any module imports so modules that read
+// from process.env at import-time (jwt.js, db.js) don't crash.
+process.env.NODE_ENV = "test";
+process.env.JWT_SECRET = "test-jwt-secret-do-not-use-in-prod";
+process.env.DATABASE_URL = "postgres://test:test@localhost:5432/test";
+process.env.ARCJET_KEY = "";
+process.env.ARCJET_MODE = "DRY_RUN";
+process.env.RESEND_API_KEY = "test-resend";
+process.env.GOOGLE_CLIENT_ID = "test-google-id";
+process.env.GOOGLE_CLIENT_SECRET = "test-google-secret";
+process.env.CLIENT_URL = "http://localhost:5173";
+process.env.CLIENT_PASSWORD_RESET_URL = "http://localhost:5173/reset-password";
+process.env.PORT = "0";
diff --git a/server/__tests__/unit/jwt.test.js b/server/__tests__/unit/jwt.test.js
new file mode 100644
index 0000000..7900598
--- /dev/null
+++ b/server/__tests__/unit/jwt.test.js
@@ -0,0 +1,19 @@
+import { describe, expect, it } from "@jest/globals";
+import { generateToken, verifyToken } from "../../src/utils/jwt.js";
+
+describe("jwt utilities", () => {
+ it("generates a verifiable token containing the payload", () => {
+ const token = generateToken({ id: 123, role: "user" });
+ expect(typeof token).toBe("string");
+ const decoded = verifyToken(token);
+ expect(decoded.id).toBe(123);
+ expect(decoded.role).toBe("user");
+ expect(decoded.exp).toBeGreaterThan(Math.floor(Date.now() / 1000));
+ });
+
+ it("throws when verifying a tampered token", () => {
+ const token = generateToken({ id: 1, role: "user" });
+ const tampered = `${token}garbage`;
+ expect(() => verifyToken(tampered)).toThrow();
+ });
+});
diff --git a/server/__tests__/unit/match-status.test.js b/server/__tests__/unit/match-status.test.js
new file mode 100644
index 0000000..1f27859
--- /dev/null
+++ b/server/__tests__/unit/match-status.test.js
@@ -0,0 +1,76 @@
+import { describe, expect, it, jest } from "@jest/globals";
+import { getMatchStatus, syncMatchStatus } from "../../src/utils/match-status.js";
+import { MATCH_STATUS } from "../../src/validation/matches.js";
+
+describe("getMatchStatus", () => {
+ const start = "2026-01-01T10:00:00Z";
+ const end = "2026-01-01T12:00:00Z";
+
+ it("returns SCHEDULED when now is before start", () => {
+ expect(getMatchStatus(start, end, new Date("2026-01-01T09:00:00Z"))).toBe(
+ MATCH_STATUS.SCHEDULED,
+ );
+ });
+
+ it("returns LIVE when now is between start and end", () => {
+ expect(getMatchStatus(start, end, new Date("2026-01-01T11:00:00Z"))).toBe(MATCH_STATUS.LIVE);
+ });
+
+ it("returns FINISHED when now is at or after end", () => {
+ expect(getMatchStatus(start, end, new Date("2026-01-01T12:00:00Z"))).toBe(
+ MATCH_STATUS.FINISHED,
+ );
+ });
+
+ it("returns LIVE exactly at start time (boundary)", () => {
+ expect(getMatchStatus(start, end, new Date(start))).toBe(MATCH_STATUS.LIVE);
+ });
+
+ it("returns null when startTime is invalid", () => {
+ expect(getMatchStatus("not-a-date", end, new Date(start))).toBeNull();
+ });
+
+ it("returns null when endTime is invalid", () => {
+ expect(getMatchStatus(start, "garbage", new Date(start))).toBeNull();
+ });
+});
+
+describe("syncMatchStatus", () => {
+ it("calls updateStatus and updates match when status changes", async () => {
+ // match has scheduled status but its window is in the past, so it should become FINISHED
+ const match = {
+ startTime: "2020-01-01T10:00:00Z",
+ endTime: "2020-01-01T12:00:00Z",
+ status: MATCH_STATUS.SCHEDULED,
+ };
+ const updateStatus = jest.fn().mockResolvedValue(undefined);
+ const result = await syncMatchStatus(match, updateStatus);
+
+ expect(updateStatus).toHaveBeenCalledWith(MATCH_STATUS.FINISHED);
+ expect(result).toBe(MATCH_STATUS.FINISHED);
+ expect(match.status).toBe(MATCH_STATUS.FINISHED);
+ });
+
+ it("does not call updateStatus when status is unchanged", async () => {
+ // match window far in the future and already SCHEDULED - no change
+ const match = {
+ startTime: "2099-01-01T10:00:00Z",
+ endTime: "2099-01-01T12:00:00Z",
+ status: MATCH_STATUS.SCHEDULED,
+ };
+ const updateStatus = jest.fn();
+ const result = await syncMatchStatus(match, updateStatus);
+
+ expect(updateStatus).not.toHaveBeenCalled();
+ expect(result).toBe(MATCH_STATUS.SCHEDULED);
+ });
+
+ it("returns existing status when timestamps are invalid", async () => {
+ const match = { startTime: "bad", endTime: "bad", status: MATCH_STATUS.LIVE };
+ const updateStatus = jest.fn();
+ const result = await syncMatchStatus(match, updateStatus);
+
+ expect(updateStatus).not.toHaveBeenCalled();
+ expect(result).toBe(MATCH_STATUS.LIVE);
+ });
+});
diff --git a/server/__tests__/unit/utils.test.js b/server/__tests__/unit/utils.test.js
new file mode 100644
index 0000000..5252701
--- /dev/null
+++ b/server/__tests__/unit/utils.test.js
@@ -0,0 +1,38 @@
+import { afterEach, beforeEach, describe, expect, it, jest } from "@jest/globals";
+import { logError, logIt, logWarn } from "../../src/utils/utils.js";
+
+describe("logger helpers", () => {
+ let logSpy;
+ let warnSpy;
+ let errorSpy;
+
+ beforeEach(() => {
+ logSpy = jest.spyOn(console, "log").mockImplementation(() => {});
+ warnSpy = jest.spyOn(console, "warn").mockImplementation(() => {});
+ errorSpy = jest.spyOn(console, "error").mockImplementation(() => {});
+ });
+
+ afterEach(() => {
+ logSpy.mockRestore();
+ warnSpy.mockRestore();
+ errorSpy.mockRestore();
+ });
+
+ it("logIt forwards to console.log", () => {
+ logIt("hello", "world");
+ expect(logSpy).toHaveBeenCalled();
+ const args = logSpy.mock.calls[0];
+ expect(args.join(" ")).toContain("hello");
+ expect(args.join(" ")).toContain("world");
+ });
+
+ it("logWarn forwards to console.warn", () => {
+ logWarn("careful");
+ expect(warnSpy).toHaveBeenCalled();
+ });
+
+ it("logError forwards to console.error", () => {
+ logError("oops");
+ expect(errorSpy).toHaveBeenCalled();
+ });
+});
diff --git a/server/__tests__/unit/validation-auth.test.js b/server/__tests__/unit/validation-auth.test.js
new file mode 100644
index 0000000..4f9fe83
--- /dev/null
+++ b/server/__tests__/unit/validation-auth.test.js
@@ -0,0 +1,75 @@
+import { describe, expect, it } from "@jest/globals";
+import { loginSchema, signupSchema } from "../../src/validation/auth.js";
+
+describe("signupSchema", () => {
+ it("accepts a valid payload", async () => {
+ const result = await signupSchema.safeParseAsync({
+ name: "Alice",
+ email: "alice@example.com",
+ password: "secret12",
+ });
+ expect(result.success).toBe(true);
+ });
+
+ it("rejects short names", async () => {
+ const result = await signupSchema.safeParseAsync({
+ name: "A",
+ email: "a@b.com",
+ password: "secret12",
+ });
+ expect(result.success).toBe(false);
+ });
+
+ it("rejects invalid email", async () => {
+ const result = await signupSchema.safeParseAsync({
+ name: "Alice",
+ email: "not-an-email",
+ password: "secret12",
+ });
+ expect(result.success).toBe(false);
+ });
+
+ it("rejects passwords shorter than 8 chars", async () => {
+ const result = await signupSchema.safeParseAsync({
+ name: "Alice",
+ email: "a@b.com",
+ password: "short",
+ });
+ expect(result.success).toBe(false);
+ });
+
+ it("rejects passwords longer than 15 chars", async () => {
+ const result = await signupSchema.safeParseAsync({
+ name: "Alice",
+ email: "a@b.com",
+ password: "thispasswordistoolong",
+ });
+ expect(result.success).toBe(false);
+ });
+});
+
+describe("loginSchema", () => {
+ it("accepts a valid payload", async () => {
+ const result = await loginSchema.safeParseAsync({
+ email: "alice@example.com",
+ password: "anything",
+ });
+ expect(result.success).toBe(true);
+ });
+
+ it("rejects invalid email", async () => {
+ const result = await loginSchema.safeParseAsync({
+ email: "nope",
+ password: "anything",
+ });
+ expect(result.success).toBe(false);
+ });
+
+ it("rejects empty password", async () => {
+ const result = await loginSchema.safeParseAsync({
+ email: "a@b.com",
+ password: "",
+ });
+ expect(result.success).toBe(false);
+ });
+});
diff --git a/server/__tests__/unit/validation-matches.test.js b/server/__tests__/unit/validation-matches.test.js
new file mode 100644
index 0000000..78f0dca
--- /dev/null
+++ b/server/__tests__/unit/validation-matches.test.js
@@ -0,0 +1,95 @@
+import { describe, expect, it } from "@jest/globals";
+import {
+ createMatchSchema,
+ listMatchesQuerySchema,
+ matchIdParamSchema,
+ updateScoreSchema,
+} from "../../src/validation/matches.js";
+
+describe("createMatchSchema", () => {
+ const valid = {
+ sport: "football",
+ homeTeam: "A",
+ awayTeam: "B",
+ startTime: "2026-01-01T10:00:00Z",
+ endTime: "2026-01-01T12:00:00Z",
+ };
+
+ it("accepts a valid match", async () => {
+ const result = await createMatchSchema.safeParseAsync(valid);
+ expect(result.success).toBe(true);
+ });
+
+ it("rejects when endTime <= startTime", async () => {
+ const result = await createMatchSchema.safeParseAsync({
+ ...valid,
+ endTime: "2026-01-01T10:00:00Z",
+ });
+ expect(result.success).toBe(false);
+ });
+
+ it("rejects empty sport", async () => {
+ const result = await createMatchSchema.safeParseAsync({ ...valid, sport: "" });
+ expect(result.success).toBe(false);
+ });
+
+ it("coerces numeric scores from strings", async () => {
+ const result = await createMatchSchema.safeParseAsync({
+ ...valid,
+ homeScore: "3",
+ awayScore: "1",
+ });
+ expect(result.success).toBe(true);
+ if (result.success) {
+ expect(result.data.homeScore).toBe(3);
+ expect(result.data.awayScore).toBe(1);
+ }
+ });
+
+ it("rejects negative scores", async () => {
+ const result = await createMatchSchema.safeParseAsync({ ...valid, homeScore: -1 });
+ expect(result.success).toBe(false);
+ });
+});
+
+describe("listMatchesQuerySchema", () => {
+ it("accepts a positive integer limit", () => {
+ const result = listMatchesQuerySchema.safeParse({ limit: "10" });
+ expect(result.success).toBe(true);
+ });
+
+ it("rejects limit > 100", () => {
+ const result = listMatchesQuerySchema.safeParse({ limit: 101 });
+ expect(result.success).toBe(false);
+ });
+
+ it("allows missing limit", () => {
+ const result = listMatchesQuerySchema.safeParse({});
+ expect(result.success).toBe(true);
+ });
+});
+
+describe("matchIdParamSchema", () => {
+ it("coerces a string id to number", () => {
+ const result = matchIdParamSchema.safeParse({ id: "42" });
+ expect(result.success).toBe(true);
+ if (result.success) expect(result.data.id).toBe(42);
+ });
+
+ it("rejects non-positive ids", () => {
+ const result = matchIdParamSchema.safeParse({ id: "0" });
+ expect(result.success).toBe(false);
+ });
+});
+
+describe("updateScoreSchema", () => {
+ it("accepts non-negative integer scores", () => {
+ const result = updateScoreSchema.safeParse({ homeScore: 2, awayScore: 0 });
+ expect(result.success).toBe(true);
+ });
+
+ it("rejects negative scores", () => {
+ const result = updateScoreSchema.safeParse({ homeScore: -1, awayScore: 0 });
+ expect(result.success).toBe(false);
+ });
+});
diff --git a/server/jest.config.js b/server/jest.config.js
new file mode 100644
index 0000000..cf6375a
--- /dev/null
+++ b/server/jest.config.js
@@ -0,0 +1,12 @@
+export default {
+ testEnvironment: "node",
+ transform: {},
+ testMatch: ["/__tests__/**/*.test.js"],
+ setupFiles: ["/__tests__/setup/env.js"],
+ moduleNameMapper: {
+ "^(\\.{1,2}/.*)\\.js$": "$1",
+ },
+ collectCoverageFrom: ["src/utils/**/*.js", "src/validation/**/*.js", "src/app.js"],
+ coverageDirectory: "coverage",
+ coverageReporters: ["text", "lcov", "json-summary"],
+};
diff --git a/server/package.json b/server/package.json
index 1e7bf3b..46bb16d 100644
--- a/server/package.json
+++ b/server/package.json
@@ -4,7 +4,8 @@
"description": "",
"main": "src/index.js",
"scripts": {
- "test": "echo \"Error: no test specified\" && exit 1",
+ "test": "NODE_OPTIONS='--experimental-vm-modules --no-warnings' jest",
+ "test:ci": "NODE_OPTIONS='--experimental-vm-modules --no-warnings' jest --ci --reporters=default --reporters=jest-junit --coverage",
"start": "node src/index.js",
"dev": "node --watch src/index.js",
"db:generate": "drizzle-kit generate",
@@ -33,6 +34,9 @@
"zod": "^4.3.6"
},
"devDependencies": {
- "drizzle-kit": "^0.31.8"
+ "drizzle-kit": "^0.31.8",
+ "jest": "^30.2.0",
+ "jest-junit": "^16.0.0",
+ "supertest": "^7.1.4"
}
}
diff --git a/server/pnpm-lock.yaml b/server/pnpm-lock.yaml
index 1eff2b4..03a75b5 100644
--- a/server/pnpm-lock.yaml
+++ b/server/pnpm-lock.yaml
@@ -54,6 +54,15 @@ importers:
drizzle-kit:
specifier: ^0.31.8
version: 0.31.10
+ jest:
+ specifier: ^30.2.0
+ version: 30.3.0(@types/node@25.6.0)
+ jest-junit:
+ specifier: ^16.0.0
+ version: 16.0.0
+ supertest:
+ specifier: ^7.1.4
+ version: 7.2.2
packages:
@@ -121,6 +130,171 @@ packages:
resolution: {integrity: sha512-iXTWtCL1Tl/RkB+oZBzt6PNRz0tNyt87JagCj8JjPvA3QsEfcxQlt9hs4rAEVvanASi17k1wFhbjOF1ejBzFnQ==}
engines: {node: '>=20'}
+ '@babel/code-frame@7.29.0':
+ resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/compat-data@7.29.3':
+ resolution: {integrity: sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/core@7.29.0':
+ resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/generator@7.29.1':
+ resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-compilation-targets@7.28.6':
+ resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-globals@7.28.0':
+ resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-imports@7.28.6':
+ resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-transforms@7.28.6':
+ resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-plugin-utils@7.28.6':
+ resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-string-parser@7.27.1':
+ resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-identifier@7.28.5':
+ resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-option@7.27.1':
+ resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helpers@7.29.2':
+ resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/parser@7.29.3':
+ resolution: {integrity: sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/plugin-syntax-async-generators@7.8.4':
+ resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-bigint@7.8.3':
+ resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-class-properties@7.12.13':
+ resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-class-static-block@7.14.5':
+ resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-import-attributes@7.28.6':
+ resolution: {integrity: sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-import-meta@7.10.4':
+ resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-json-strings@7.8.3':
+ resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-jsx@7.28.6':
+ resolution: {integrity: sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-logical-assignment-operators@7.10.4':
+ resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3':
+ resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-numeric-separator@7.10.4':
+ resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-object-rest-spread@7.8.3':
+ resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-optional-catch-binding@7.8.3':
+ resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-optional-chaining@7.8.3':
+ resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-private-property-in-object@7.14.5':
+ resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-top-level-await@7.14.5':
+ resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-typescript@7.28.6':
+ resolution: {integrity: sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/template@7.28.6':
+ resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/traverse@7.29.0':
+ resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/types@7.29.0':
+ resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==}
+ engines: {node: '>=6.9.0'}
+
+ '@bcoe/v8-coverage@0.2.3':
+ resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
+
'@bufbuild/protobuf@2.11.0':
resolution: {integrity: sha512-sBXGT13cpmPR5BMgHE6UEEfEaShh5Ror6rfN3yEK5si7QVrtZg8LEPQb0VVhiLRUslD2yLnXtnRzG035J/mZXQ==}
@@ -145,6 +319,15 @@ packages:
'@drizzle-team/brocli@0.10.2':
resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==}
+ '@emnapi/core@1.10.0':
+ resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==}
+
+ '@emnapi/runtime@1.10.0':
+ resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==}
+
+ '@emnapi/wasi-threads@1.2.1':
+ resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==}
+
'@esbuild-kit/core-utils@3.3.2':
resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==}
deprecated: 'Merged into tsx: https://tsx.is'
@@ -597,9 +780,288 @@ packages:
cpu: [x64]
os: [win32]
+ '@isaacs/cliui@8.0.2':
+ resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
+ engines: {node: '>=12'}
+
+ '@istanbuljs/load-nyc-config@1.1.0':
+ resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==}
+ engines: {node: '>=8'}
+
+ '@istanbuljs/schema@0.1.6':
+ resolution: {integrity: sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==}
+ engines: {node: '>=8'}
+
+ '@jest/console@30.3.0':
+ resolution: {integrity: sha512-PAwCvFJ4696XP2qZj+LAn1BWjZaJ6RjG6c7/lkMaUJnkyMS34ucuIsfqYvfskVNvUI27R/u4P1HMYFnlVXG/Ww==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ '@jest/core@30.3.0':
+ resolution: {integrity: sha512-U5mVPsBxLSO6xYbf+tgkymLx+iAhvZX43/xI1+ej2ZOPnPdkdO1CzDmFKh2mZBn2s4XZixszHeQnzp1gm/DIxw==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+ peerDependencies:
+ node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
+ peerDependenciesMeta:
+ node-notifier:
+ optional: true
+
+ '@jest/diff-sequences@30.3.0':
+ resolution: {integrity: sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ '@jest/environment@30.3.0':
+ resolution: {integrity: sha512-SlLSF4Be735yQXyh2+mctBOzNDx5s5uLv88/j8Qn1wH679PDcwy67+YdADn8NJnGjzlXtN62asGH/T4vWOkfaw==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ '@jest/expect-utils@30.3.0':
+ resolution: {integrity: sha512-j0+W5iQQ8hBh7tHZkTQv3q2Fh/M7Je72cIsYqC4OaktgtO7v1So9UTjp6uPBHIaB6beoF/RRsCgMJKvti0wADA==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ '@jest/expect@30.3.0':
+ resolution: {integrity: sha512-76Nlh4xJxk2D/9URCn3wFi98d2hb19uWE1idLsTt2ywhvdOldbw3S570hBgn25P4ICUZ/cBjybrBex2g17IDbg==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ '@jest/fake-timers@30.3.0':
+ resolution: {integrity: sha512-WUQDs8SOP9URStX1DzhD425CqbN/HxUYCTwVrT8sTVBfMvFqYt/s61EK5T05qnHu0po6RitXIvP9otZxYDzTGQ==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ '@jest/get-type@30.1.0':
+ resolution: {integrity: sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ '@jest/globals@30.3.0':
+ resolution: {integrity: sha512-+owLCBBdfpgL3HU+BD5etr1SvbXpSitJK0is1kiYjJxAAJggYMRQz5hSdd5pq1sSggfxPbw2ld71pt4x5wwViA==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ '@jest/pattern@30.0.1':
+ resolution: {integrity: sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ '@jest/reporters@30.3.0':
+ resolution: {integrity: sha512-a09z89S+PkQnL055bVj8+pe2Caed2PBOaczHcXCykW5ngxX9EWx/1uAwncxc/HiU0oZqfwseMjyhxgRjS49qPw==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+ peerDependencies:
+ node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
+ peerDependenciesMeta:
+ node-notifier:
+ optional: true
+
+ '@jest/schemas@30.0.5':
+ resolution: {integrity: sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ '@jest/snapshot-utils@30.3.0':
+ resolution: {integrity: sha512-ORbRN9sf5PP82v3FXNSwmO1OTDR2vzR2YTaR+E3VkSBZ8zadQE6IqYdYEeFH1NIkeB2HIGdF02dapb6K0Mj05g==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ '@jest/source-map@30.0.1':
+ resolution: {integrity: sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ '@jest/test-result@30.3.0':
+ resolution: {integrity: sha512-e/52nJGuD74AKTSe0P4y5wFRlaXP0qmrS17rqOMHeSwm278VyNyXE3gFO/4DTGF9w+65ra3lo3VKj0LBrzmgdQ==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ '@jest/test-sequencer@30.3.0':
+ resolution: {integrity: sha512-dgbWy9b8QDlQeRZcv7LNF+/jFiiYHTKho1xirauZ7kVwY7avjFF6uTT0RqlgudB5OuIPagFdVtfFMosjVbk1eA==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ '@jest/transform@30.3.0':
+ resolution: {integrity: sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ '@jest/types@30.3.0':
+ resolution: {integrity: sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ '@jridgewell/gen-mapping@0.3.13':
+ resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
+
+ '@jridgewell/remapping@2.3.5':
+ resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
+
+ '@jridgewell/resolve-uri@3.1.2':
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/sourcemap-codec@1.5.5':
+ resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+
+ '@napi-rs/wasm-runtime@0.2.12':
+ resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==}
+
+ '@noble/hashes@1.8.0':
+ resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==}
+ engines: {node: ^14.21.3 || >=16}
+
+ '@paralleldrive/cuid2@2.3.1':
+ resolution: {integrity: sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==}
+
+ '@pkgjs/parseargs@0.11.0':
+ resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
+ engines: {node: '>=14'}
+
+ '@pkgr/core@0.2.9':
+ resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==}
+ engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
+
+ '@sinclair/typebox@0.34.49':
+ resolution: {integrity: sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==}
+
+ '@sinonjs/commons@3.0.1':
+ resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==}
+
+ '@sinonjs/fake-timers@15.3.2':
+ resolution: {integrity: sha512-mrn35Jl2pCpns+mE3HaZa1yPN5EYCRgiMI+135COjr2hr8Cls9DXqIZ57vZe2cz7y2XVSq92tcs6kGQcT1J8Rw==}
+
'@stablelib/base64@1.0.1':
resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==}
+ '@tybys/wasm-util@0.10.1':
+ resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==}
+
+ '@types/babel__core@7.20.5':
+ resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
+
+ '@types/babel__generator@7.27.0':
+ resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==}
+
+ '@types/babel__template@7.4.4':
+ resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==}
+
+ '@types/babel__traverse@7.28.0':
+ resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==}
+
+ '@types/istanbul-lib-coverage@2.0.6':
+ resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==}
+
+ '@types/istanbul-lib-report@3.0.3':
+ resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==}
+
+ '@types/istanbul-reports@3.0.4':
+ resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==}
+
+ '@types/node@25.6.0':
+ resolution: {integrity: sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==}
+
+ '@types/stack-utils@2.0.3':
+ resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==}
+
+ '@types/yargs-parser@21.0.3':
+ resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==}
+
+ '@types/yargs@17.0.35':
+ resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==}
+
+ '@ungap/structured-clone@1.3.0':
+ resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
+
+ '@unrs/resolver-binding-android-arm-eabi@1.11.1':
+ resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==}
+ cpu: [arm]
+ os: [android]
+
+ '@unrs/resolver-binding-android-arm64@1.11.1':
+ resolution: {integrity: sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==}
+ cpu: [arm64]
+ os: [android]
+
+ '@unrs/resolver-binding-darwin-arm64@1.11.1':
+ resolution: {integrity: sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@unrs/resolver-binding-darwin-x64@1.11.1':
+ resolution: {integrity: sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@unrs/resolver-binding-freebsd-x64@1.11.1':
+ resolution: {integrity: sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1':
+ resolution: {integrity: sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==}
+ cpu: [arm]
+ os: [linux]
+
+ '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1':
+ resolution: {integrity: sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==}
+ cpu: [arm]
+ os: [linux]
+
+ '@unrs/resolver-binding-linux-arm64-gnu@1.11.1':
+ resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@unrs/resolver-binding-linux-arm64-musl@1.11.1':
+ resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1':
+ resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
+ '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1':
+ resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [glibc]
+
+ '@unrs/resolver-binding-linux-riscv64-musl@1.11.1':
+ resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [musl]
+
+ '@unrs/resolver-binding-linux-s390x-gnu@1.11.1':
+ resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@unrs/resolver-binding-linux-x64-gnu@1.11.1':
+ resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@unrs/resolver-binding-linux-x64-musl@1.11.1':
+ resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@unrs/resolver-binding-wasm32-wasi@1.11.1':
+ resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==}
+ engines: {node: '>=14.0.0'}
+ cpu: [wasm32]
+
+ '@unrs/resolver-binding-win32-arm64-msvc@1.11.1':
+ resolution: {integrity: sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@unrs/resolver-binding-win32-ia32-msvc@1.11.1':
+ resolution: {integrity: sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==}
+ cpu: [ia32]
+ os: [win32]
+
+ '@unrs/resolver-binding-win32-x64-msvc@1.11.1':
+ resolution: {integrity: sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==}
+ cpu: [x64]
+ os: [win32]
+
accepts@2.0.0:
resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==}
engines: {node: '>= 0.6'}
@@ -608,13 +1070,83 @@ packages:
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
engines: {node: '>= 14'}
+ ansi-escapes@4.3.2:
+ resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==}
+ engines: {node: '>=8'}
+
+ ansi-regex@5.0.1:
+ resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+ engines: {node: '>=8'}
+
+ ansi-regex@6.2.2:
+ resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==}
+ engines: {node: '>=12'}
+
+ ansi-styles@4.3.0:
+ resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+ engines: {node: '>=8'}
+
+ ansi-styles@5.2.0:
+ resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
+ engines: {node: '>=10'}
+
+ ansi-styles@6.2.3:
+ resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==}
+ engines: {node: '>=12'}
+
+ anymatch@3.1.3:
+ resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
+ engines: {node: '>= 8'}
+
arcjet@1.3.1:
resolution: {integrity: sha512-dcjPkWgk0BqSIcYdAqEqscXt0hBB6nMjybzkNiY4FW4s/T9MVIvIG09fiXUpqXKLPLTkjhUWc+oAfV5jtnNR+w==}
engines: {node: '>=20'}
+ argparse@1.0.10:
+ resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
+
+ asap@2.0.6:
+ resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==}
+
+ asynckit@0.4.0:
+ resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
+
+ babel-jest@30.3.0:
+ resolution: {integrity: sha512-gRpauEU2KRrCox5Z296aeVHR4jQ98BCnu0IO332D/xpHNOsIH/bgSRk9k6GbKIbBw8vFeN6ctuu6tV8WOyVfYQ==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+ peerDependencies:
+ '@babel/core': ^7.11.0 || ^8.0.0-0
+
+ babel-plugin-istanbul@7.0.1:
+ resolution: {integrity: sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==}
+ engines: {node: '>=12'}
+
+ babel-plugin-jest-hoist@30.3.0:
+ resolution: {integrity: sha512-+TRkByhsws6sfPjVaitzadk1I0F5sPvOVUH5tyTSzhePpsGIVrdeunHSw/C36QeocS95OOk8lunc4rlu5Anwsg==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ babel-preset-current-node-syntax@1.2.0:
+ resolution: {integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==}
+ peerDependencies:
+ '@babel/core': ^7.0.0 || ^8.0.0-0
+
+ babel-preset-jest@30.3.0:
+ resolution: {integrity: sha512-6ZcUbWHC+dMz2vfzdNwi87Z1gQsLNK2uLuK1Q89R11xdvejcivlYYwDlEv0FHX3VwEXpbBQ9uufB/MUNpZGfhQ==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+ peerDependencies:
+ '@babel/core': ^7.11.0 || ^8.0.0-beta.1
+
+ balanced-match@1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
+ baseline-browser-mapping@2.10.25:
+ resolution: {integrity: sha512-QO/VHsXCQdnzADMfmkeOPvHdIAkoB7i0/rGjINPJEetLx75hNttVWGQ/jycHUDP9zZ9rupbm60WRxcwViB0MiA==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
bcryptjs@3.0.3:
resolution: {integrity: sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g==}
hasBin: true
@@ -626,6 +1158,20 @@ packages:
resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==}
engines: {node: '>=18'}
+ brace-expansion@1.1.14:
+ resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==}
+
+ brace-expansion@2.1.0:
+ resolution: {integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==}
+
+ browserslist@4.28.2:
+ resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==}
+ engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+ hasBin: true
+
+ bser@2.1.1:
+ resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==}
+
buffer-equal-constant-time@1.0.1:
resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
@@ -644,6 +1190,64 @@ packages:
resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
engines: {node: '>= 0.4'}
+ callsites@3.1.0:
+ resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
+ engines: {node: '>=6'}
+
+ camelcase@5.3.1:
+ resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
+ engines: {node: '>=6'}
+
+ camelcase@6.3.0:
+ resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
+ engines: {node: '>=10'}
+
+ caniuse-lite@1.0.30001791:
+ resolution: {integrity: sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ==}
+
+ chalk@4.1.2:
+ resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+ engines: {node: '>=10'}
+
+ char-regex@1.0.2:
+ resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==}
+ engines: {node: '>=10'}
+
+ ci-info@4.4.0:
+ resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==}
+ engines: {node: '>=8'}
+
+ cjs-module-lexer@2.2.0:
+ resolution: {integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==}
+
+ cliui@8.0.1:
+ resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
+ engines: {node: '>=12'}
+
+ co@4.6.0:
+ resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==}
+ engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'}
+
+ collect-v8-coverage@1.0.3:
+ resolution: {integrity: sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==}
+
+ color-convert@2.0.1:
+ resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+ engines: {node: '>=7.0.0'}
+
+ color-name@1.1.4:
+ resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+
+ combined-stream@1.0.8:
+ resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
+ engines: {node: '>= 0.8'}
+
+ component-emitter@1.3.1:
+ resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==}
+
+ concat-map@0.0.1:
+ resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
content-disposition@1.0.1:
resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==}
engines: {node: '>=18'}
@@ -652,6 +1256,9 @@ packages:
resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==}
engines: {node: '>= 0.6'}
+ convert-source-map@2.0.0:
+ resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
cookie-signature@1.2.2:
resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==}
engines: {node: '>=6.6.0'}
@@ -660,10 +1267,17 @@ packages:
resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==}
engines: {node: '>= 0.6'}
+ cookiejar@2.1.4:
+ resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==}
+
cors@2.8.6:
resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==}
engines: {node: '>= 0.10'}
+ cross-spawn@7.0.6:
+ resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
+ engines: {node: '>= 8'}
+
data-uri-to-buffer@4.0.1:
resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
engines: {node: '>= 12'}
@@ -677,10 +1291,33 @@ packages:
supports-color:
optional: true
+ dedent@1.7.2:
+ resolution: {integrity: sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==}
+ peerDependencies:
+ babel-plugin-macros: ^3.1.0
+ peerDependenciesMeta:
+ babel-plugin-macros:
+ optional: true
+
+ deepmerge@4.3.1:
+ resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
+ engines: {node: '>=0.10.0'}
+
+ delayed-stream@1.0.0:
+ resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
+ engines: {node: '>=0.4.0'}
+
depd@2.0.0:
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
engines: {node: '>= 0.8'}
+ detect-newline@3.1.0:
+ resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==}
+ engines: {node: '>=8'}
+
+ dezalgo@1.0.4:
+ resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==}
+
dotenv@17.4.1:
resolution: {integrity: sha512-k8DaKGP6r1G30Lx8V4+pCsLzKr8vLmV2paqEj1Y55GdAgJuIqpRp5FfajGF8KtwMxCz9qJc6wUIJnm053d/WCw==}
engines: {node: '>=12'}
@@ -785,16 +1422,35 @@ packages:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'}
+ eastasianwidth@0.2.0:
+ resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
+
ecdsa-sig-formatter@1.0.11:
resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
+ electron-to-chromium@1.5.349:
+ resolution: {integrity: sha512-QsWVGyRuY07Aqb234QytTfwd5d9AJlfNIQ5wIOl1L+PZDzI9d9+Fn0FRale/QYlFxt/bUnB0/nLd1jFPGxGK1A==}
+
+ emittery@0.13.1:
+ resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==}
+ engines: {node: '>=12'}
+
+ emoji-regex@8.0.0:
+ resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
+
+ emoji-regex@9.2.2:
+ resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
+
encodeurl@2.0.0:
resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
engines: {node: '>= 0.8'}
+ error-ex@1.3.4:
+ resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==}
+
es-define-property@1.0.1:
resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
engines: {node: '>= 0.4'}
@@ -807,6 +1463,10 @@ packages:
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
engines: {node: '>= 0.4'}
+ es-set-tostringtag@2.1.0:
+ resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
+ engines: {node: '>= 0.4'}
+
esbuild@0.18.20:
resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==}
engines: {node: '>=12'}
@@ -822,13 +1482,38 @@ packages:
engines: {node: '>=18'}
hasBin: true
+ escalade@3.2.0:
+ resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+ engines: {node: '>=6'}
+
escape-html@1.0.3:
resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
+ escape-string-regexp@2.0.0:
+ resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==}
+ engines: {node: '>=8'}
+
+ esprima@4.0.1:
+ resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
+ engines: {node: '>=4'}
+ hasBin: true
+
etag@1.8.1:
resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
engines: {node: '>= 0.6'}
+ execa@5.1.1:
+ resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
+ engines: {node: '>=10'}
+
+ exit-x@0.2.2:
+ resolution: {integrity: sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==}
+ engines: {node: '>= 0.8.0'}
+
+ expect@30.3.0:
+ resolution: {integrity: sha512-1zQrciTiQfRdo7qJM1uG4navm8DayFa2TgCSRlzUyNkhcJ6XUZF3hjnpkyr3VhAqPH7i/9GkG7Tv5abz6fqz0Q==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
express@5.2.1:
resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==}
engines: {node: '>= 18'}
@@ -836,9 +1521,18 @@ packages:
extend@3.0.2:
resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
+ fast-json-stable-stringify@2.1.0:
+ resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+
+ fast-safe-stringify@2.1.1:
+ resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==}
+
fast-sha256@1.3.0:
resolution: {integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==}
+ fb-watchman@2.0.2:
+ resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==}
+
fetch-blob@3.2.0:
resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==}
engines: {node: ^12.20 || >= 14.13}
@@ -847,9 +1541,25 @@ packages:
resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==}
engines: {node: '>= 18.0.0'}
- formdata-polyfill@4.0.10:
- resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
- engines: {node: '>=12.20.0'}
+ find-up@4.1.0:
+ resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
+ engines: {node: '>=8'}
+
+ foreground-child@3.3.1:
+ resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
+ engines: {node: '>=14'}
+
+ form-data@4.0.5:
+ resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==}
+ engines: {node: '>= 6'}
+
+ formdata-polyfill@4.0.10:
+ resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
+ engines: {node: '>=12.20.0'}
+
+ formidable@3.5.4:
+ resolution: {integrity: sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==}
+ engines: {node: '>=14.0.0'}
forwarded@0.2.0:
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
@@ -859,6 +1569,9 @@ packages:
resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==}
engines: {node: '>= 0.8'}
+ fs.realpath@1.0.0:
+ resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
+
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@@ -875,17 +1588,42 @@ packages:
resolution: {integrity: sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==}
engines: {node: '>=18'}
+ gensync@1.0.0-beta.2:
+ resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+ engines: {node: '>=6.9.0'}
+
+ get-caller-file@2.0.5:
+ resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
+ engines: {node: 6.* || 8.* || >= 10.*}
+
get-intrinsic@1.3.0:
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
engines: {node: '>= 0.4'}
+ get-package-type@0.1.0:
+ resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==}
+ engines: {node: '>=8.0.0'}
+
get-proto@1.0.1:
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
engines: {node: '>= 0.4'}
+ get-stream@6.0.1:
+ resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
+ engines: {node: '>=10'}
+
get-tsconfig@4.13.6:
resolution: {integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==}
+ glob@10.5.0:
+ resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==}
+ deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
+ hasBin: true
+
+ glob@7.2.3:
+ resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
+ deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
+
google-auth-library@10.6.2:
resolution: {integrity: sha512-e27Z6EThmVNNvtYASwQxose/G57rkRuaRbQyxM2bvYLLX/GqWZ5chWq2EBoUchJbCc57eC9ArzO5wMsEmWftCw==}
engines: {node: '>=18'}
@@ -898,14 +1636,28 @@ packages:
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
engines: {node: '>= 0.4'}
+ graceful-fs@4.2.11:
+ resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
+ has-flag@4.0.0:
+ resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+ engines: {node: '>=8'}
+
has-symbols@1.1.0:
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
engines: {node: '>= 0.4'}
+ has-tostringtag@1.0.2:
+ resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
+ engines: {node: '>= 0.4'}
+
hasown@2.0.2:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
+ html-escaper@2.0.2:
+ resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
+
http-errors@2.0.1:
resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==}
engines: {node: '>= 0.8'}
@@ -914,10 +1666,27 @@ packages:
resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
engines: {node: '>= 14'}
+ human-signals@2.1.0:
+ resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
+ engines: {node: '>=10.17.0'}
+
iconv-lite@0.7.2:
resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==}
engines: {node: '>=0.10.0'}
+ import-local@3.2.0:
+ resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==}
+ engines: {node: '>=8'}
+ hasBin: true
+
+ imurmurhash@0.1.4:
+ resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
+ engines: {node: '>=0.8.19'}
+
+ inflight@1.0.6:
+ resolution: {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.
+
inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
@@ -925,12 +1694,205 @@ packages:
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
engines: {node: '>= 0.10'}
+ is-arrayish@0.2.1:
+ resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
+
+ is-fullwidth-code-point@3.0.0:
+ resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
+ engines: {node: '>=8'}
+
+ is-generator-fn@2.1.0:
+ resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==}
+ engines: {node: '>=6'}
+
is-promise@4.0.0:
resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==}
+ is-stream@2.0.1:
+ resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
+ engines: {node: '>=8'}
+
+ isexe@2.0.0:
+ resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+
+ istanbul-lib-coverage@3.2.2:
+ resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==}
+ engines: {node: '>=8'}
+
+ istanbul-lib-instrument@6.0.3:
+ resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==}
+ engines: {node: '>=10'}
+
+ istanbul-lib-report@3.0.1:
+ resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==}
+ engines: {node: '>=10'}
+
+ istanbul-lib-source-maps@5.0.6:
+ resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==}
+ engines: {node: '>=10'}
+
+ istanbul-reports@3.2.0:
+ resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==}
+ engines: {node: '>=8'}
+
+ jackspeak@3.4.3:
+ resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
+
+ jest-changed-files@30.3.0:
+ resolution: {integrity: sha512-B/7Cny6cV5At6M25EWDgf9S617lHivamL8vl6KEpJqkStauzcG4e+WPfDgMMF+H4FVH4A2PLRyvgDJan4441QA==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ jest-circus@30.3.0:
+ resolution: {integrity: sha512-PyXq5szeSfR/4f1lYqCmmQjh0vqDkURUYi9N6whnHjlRz4IUQfMcXkGLeEoiJtxtyPqgUaUUfyQlApXWBSN1RA==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ jest-cli@30.3.0:
+ resolution: {integrity: sha512-l6Tqx+j1fDXJEW5bqYykDQQ7mQg+9mhWXtnj+tQZrTWYHyHoi6Be8HPumDSA+UiX2/2buEgjA58iJzdj146uCw==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+ hasBin: true
+ peerDependencies:
+ node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
+ peerDependenciesMeta:
+ node-notifier:
+ optional: true
+
+ jest-config@30.3.0:
+ resolution: {integrity: sha512-WPMAkMAtNDY9P/oKObtsRG/6KTrhtgPJoBTmk20uDn4Uy6/3EJnnaZJre/FMT1KVRx8cve1r7/FlMIOfRVWL4w==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+ peerDependencies:
+ '@types/node': '*'
+ esbuild-register: '>=3.4.0'
+ ts-node: '>=9.0.0'
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ esbuild-register:
+ optional: true
+ ts-node:
+ optional: true
+
+ jest-diff@30.3.0:
+ resolution: {integrity: sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ jest-docblock@30.2.0:
+ resolution: {integrity: sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ jest-each@30.3.0:
+ resolution: {integrity: sha512-V8eMndg/aZ+3LnCJgSm13IxS5XSBM22QSZc9BtPK8Dek6pm+hfUNfwBdvsB3d342bo1q7wnSkC38zjX259qZNA==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ jest-environment-node@30.3.0:
+ resolution: {integrity: sha512-4i6HItw/JSiJVsC5q0hnKIe/hbYfZLVG9YJ/0pU9Hz2n/9qZe3Rhn5s5CUZA5ORZlcdT/vmAXRMyONXJwPrmYQ==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ jest-haste-map@30.3.0:
+ resolution: {integrity: sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ jest-junit@16.0.0:
+ resolution: {integrity: sha512-A94mmw6NfJab4Fg/BlvVOUXzXgF0XIH6EmTgJ5NDPp4xoKq0Kr7sErb+4Xs9nZvu58pJojz5RFGpqnZYJTrRfQ==}
+ engines: {node: '>=10.12.0'}
+
+ jest-leak-detector@30.3.0:
+ resolution: {integrity: sha512-cuKmUUGIjfXZAiGJ7TbEMx0bcqNdPPI6P1V+7aF+m/FUJqFDxkFR4JqkTu8ZOiU5AaX/x0hZ20KaaIPXQzbMGQ==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ jest-matcher-utils@30.3.0:
+ resolution: {integrity: sha512-HEtc9uFQgaUHkC7nLSlQL3Tph4Pjxt/yiPvkIrrDCt9jhoLIgxaubo1G+CFOnmHYMxHwwdaSN7mkIFs6ZK8OhA==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ jest-message-util@30.3.0:
+ resolution: {integrity: sha512-Z/j4Bo+4ySJ+JPJN3b2Qbl9hDq3VrXmnjjGEWD/x0BCXeOXPTV1iZYYzl2X8c1MaCOL+ewMyNBcm88sboE6YWw==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ jest-mock@30.3.0:
+ resolution: {integrity: sha512-OTzICK8CpE+t4ndhKrwlIdbM6Pn8j00lvmSmq5ejiO+KxukbLjgOflKWMn3KE34EZdQm5RqTuKj+5RIEniYhog==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ jest-pnp-resolver@1.2.3:
+ resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==}
+ engines: {node: '>=6'}
+ peerDependencies:
+ jest-resolve: '*'
+ peerDependenciesMeta:
+ jest-resolve:
+ optional: true
+
+ jest-regex-util@30.0.1:
+ resolution: {integrity: sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ jest-resolve-dependencies@30.3.0:
+ resolution: {integrity: sha512-9ev8s3YN6Hsyz9LV75XUwkCVFlwPbaFn6Wp75qnI0wzAINYWY8Fb3+6y59Rwd3QaS3kKXffHXsZMziMavfz/nw==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ jest-resolve@30.3.0:
+ resolution: {integrity: sha512-NRtTAHQlpd15F9rUR36jqwelbrDV/dY4vzNte3S2kxCKUJRYNd5/6nTSbYiak1VX5g8IoFF23Uj5TURkUW8O5g==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ jest-runner@30.3.0:
+ resolution: {integrity: sha512-gDv6C9LGKWDPLia9TSzZwf4h3kMQCqyTpq+95PODnTRDO0g9os48XIYYkS6D236vjpBir2fF63YmJFtqkS5Duw==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ jest-runtime@30.3.0:
+ resolution: {integrity: sha512-CgC+hIBJbuh78HEffkhNKcbXAytQViplcl8xupqeIWyKQF50kCQA8J7GeJCkjisC6hpnC9Muf8jV5RdtdFbGng==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ jest-snapshot@30.3.0:
+ resolution: {integrity: sha512-f14c7atpb4O2DeNhwcvS810Y63wEn8O1HqK/luJ4F6M4NjvxmAKQwBUWjbExUtMxWJQ0wVgmCKymeJK6NZMnfQ==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ jest-util@30.3.0:
+ resolution: {integrity: sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ jest-validate@30.3.0:
+ resolution: {integrity: sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ jest-watcher@30.3.0:
+ resolution: {integrity: sha512-PJ1d9ThtTR8aMiBWUdcownq9mDdLXsQzJayTk4kmaBRHKvwNQn+ANveuhEBUyNI2hR1TVhvQ8D5kHubbzBHR/w==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ jest-worker@30.3.0:
+ resolution: {integrity: sha512-DrCKkaQwHexjRUFTmPzs7sHQe0TSj9nvDALKGdwmK5mW9v7j90BudWirKAJHt3QQ9Dhrg1F7DogPzhChppkJpQ==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
+ jest@30.3.0:
+ resolution: {integrity: sha512-AkXIIFcaazymvey2i/+F94XRnM6TsVLZDhBMLsd1Sf/W0wzsvvpjeyUrCZD6HGG4SDYPgDJDBKeiJTBb10WzMg==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+ hasBin: true
+ peerDependencies:
+ node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
+ peerDependenciesMeta:
+ node-notifier:
+ optional: true
+
+ js-tokens@4.0.0:
+ resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+ js-yaml@3.14.2:
+ resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==}
+ hasBin: true
+
+ jsesc@3.1.0:
+ resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
+ engines: {node: '>=6'}
+ hasBin: true
+
json-bigint@1.0.0:
resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==}
+ json-parse-even-better-errors@2.3.1:
+ resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
+
+ json5@2.2.3:
+ resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+ engines: {node: '>=6'}
+ hasBin: true
+
jsonwebtoken@9.0.3:
resolution: {integrity: sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==}
engines: {node: '>=12', npm: '>=6'}
@@ -941,6 +1903,17 @@ packages:
jws@4.0.1:
resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==}
+ leven@3.1.0:
+ resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==}
+ engines: {node: '>=6'}
+
+ lines-and-columns@1.2.4:
+ resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
+
+ locate-path@5.0.0:
+ resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
+ engines: {node: '>=8'}
+
lodash.includes@4.3.0:
resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==}
@@ -962,6 +1935,19 @@ packages:
lodash.once@4.1.1:
resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==}
+ lru-cache@10.4.3:
+ resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
+
+ lru-cache@5.1.1:
+ resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+
+ make-dir@4.0.0:
+ resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==}
+ engines: {node: '>=10'}
+
+ makeerror@1.0.12:
+ resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==}
+
math-intrinsics@1.1.0:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
@@ -974,17 +1960,65 @@ packages:
resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==}
engines: {node: '>=18'}
+ merge-stream@2.0.0:
+ resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
+
+ methods@1.1.2:
+ resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==}
+ engines: {node: '>= 0.6'}
+
+ mime-db@1.52.0:
+ resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
+ engines: {node: '>= 0.6'}
+
mime-db@1.54.0:
resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==}
engines: {node: '>= 0.6'}
+ mime-types@2.1.35:
+ resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
+ engines: {node: '>= 0.6'}
+
mime-types@3.0.2:
resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==}
engines: {node: '>=18'}
+ mime@2.6.0:
+ resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==}
+ engines: {node: '>=4.0.0'}
+ hasBin: true
+
+ mimic-fn@2.1.0:
+ resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
+ engines: {node: '>=6'}
+
+ minimatch@3.1.5:
+ resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==}
+
+ minimatch@9.0.9:
+ resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ minipass@7.1.3:
+ resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ mkdirp@1.0.4:
+ resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
+ engines: {node: '>=10'}
+ hasBin: true
+
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+ napi-postinstall@0.3.4:
+ resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==}
+ engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
+ hasBin: true
+
+ natural-compare@1.4.0:
+ resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+
negotiator@1.0.0:
resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==}
engines: {node: '>= 0.6'}
@@ -998,6 +2032,20 @@ packages:
resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+ node-int64@0.4.0:
+ resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==}
+
+ node-releases@2.0.38:
+ resolution: {integrity: sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==}
+
+ normalize-path@3.0.0:
+ resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
+ engines: {node: '>=0.10.0'}
+
+ npm-run-path@4.0.1:
+ resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
+ engines: {node: '>=8'}
+
object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
@@ -1013,10 +2061,53 @@ packages:
once@1.4.0:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
+ onetime@5.1.2:
+ resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
+ engines: {node: '>=6'}
+
+ p-limit@2.3.0:
+ resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
+ engines: {node: '>=6'}
+
+ p-limit@3.1.0:
+ resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+ engines: {node: '>=10'}
+
+ p-locate@4.1.0:
+ resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
+ engines: {node: '>=8'}
+
+ p-try@2.2.0:
+ resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
+ engines: {node: '>=6'}
+
+ package-json-from-dist@1.0.1:
+ resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
+
+ parse-json@5.2.0:
+ resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
+ engines: {node: '>=8'}
+
parseurl@1.3.3:
resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
engines: {node: '>= 0.8'}
+ path-exists@4.0.0:
+ resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+ engines: {node: '>=8'}
+
+ path-is-absolute@1.0.1:
+ resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
+ engines: {node: '>=0.10.0'}
+
+ path-key@3.1.1:
+ resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+ engines: {node: '>=8'}
+
+ path-scurry@1.11.1:
+ resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
+ engines: {node: '>=16 || 14 >=14.18'}
+
path-to-regexp@8.3.0:
resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==}
@@ -1054,6 +2145,25 @@ packages:
pgpass@1.0.5:
resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==}
+ picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+ picomatch@2.3.2:
+ resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==}
+ engines: {node: '>=8.6'}
+
+ picomatch@4.0.4:
+ resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==}
+ engines: {node: '>=12'}
+
+ pirates@4.0.7:
+ resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==}
+ engines: {node: '>= 6'}
+
+ pkg-dir@4.2.0:
+ resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==}
+ engines: {node: '>=8'}
+
postal-mime@2.7.4:
resolution: {integrity: sha512-0WdnFQYUrPGGTFu1uOqD2s7omwua8xaeYGdO6rb88oD5yJ/4pPHDA4sdWqfD8wQVfCny563n/HQS7zTFft+f/g==}
@@ -1073,10 +2183,17 @@ packages:
resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==}
engines: {node: '>=0.10.0'}
+ pretty-format@30.3.0:
+ resolution: {integrity: sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==}
+ engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
+
proxy-addr@2.0.7:
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
engines: {node: '>= 0.10'}
+ pure-rand@7.0.1:
+ resolution: {integrity: sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==}
+
qs@6.15.0:
resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==}
engines: {node: '>=0.6'}
@@ -1089,6 +2206,13 @@ packages:
resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==}
engines: {node: '>= 0.10'}
+ react-is@18.3.1:
+ resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
+
+ require-directory@2.1.1:
+ resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
+ engines: {node: '>=0.10.0'}
+
resend@6.10.0:
resolution: {integrity: sha512-i7CwZpYj4Oho1RxsTpLcCUkO08+HiL4NXrm6jLJ2WzJ89UGI8eROSieLONJA3hnUrf1OYnCyfq5F6POnHUMv1Q==}
engines: {node: '>=20'}
@@ -1098,6 +2222,14 @@ packages:
'@react-email/render':
optional: true
+ resolve-cwd@3.0.0:
+ resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==}
+ engines: {node: '>=8'}
+
+ resolve-from@5.0.0:
+ resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
+ engines: {node: '>=8'}
+
resolve-pkg-maps@1.0.0:
resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
@@ -1111,6 +2243,10 @@ packages:
safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+ semver@6.3.1:
+ resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
+ hasBin: true
+
semver@7.7.4:
resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==}
engines: {node: '>=10'}
@@ -1127,6 +2263,14 @@ packages:
setprototypeof@1.2.0:
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
+ shebang-command@2.0.0:
+ resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+ engines: {node: '>=8'}
+
+ shebang-regex@3.0.0:
+ resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+ engines: {node: '>=8'}
+
side-channel-list@1.0.0:
resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
engines: {node: '>= 0.4'}
@@ -1143,6 +2287,20 @@ packages:
resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
engines: {node: '>= 0.4'}
+ signal-exit@3.0.7:
+ resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
+
+ signal-exit@4.1.0:
+ resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
+ engines: {node: '>=14'}
+
+ slash@3.0.0:
+ resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
+ engines: {node: '>=8'}
+
+ source-map-support@0.5.13:
+ resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==}
+
source-map-support@0.5.21:
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
@@ -1154,6 +2312,13 @@ packages:
resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
engines: {node: '>= 10.x'}
+ sprintf-js@1.0.3:
+ resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
+
+ stack-utils@2.0.6:
+ resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==}
+ engines: {node: '>=10'}
+
standardwebhooks@1.0.0:
resolution: {integrity: sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==}
@@ -1161,18 +2326,88 @@ packages:
resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==}
engines: {node: '>= 0.8'}
+ string-length@4.0.2:
+ resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==}
+ engines: {node: '>=10'}
+
+ string-width@4.2.3:
+ resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
+ engines: {node: '>=8'}
+
+ string-width@5.1.2:
+ resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
+ engines: {node: '>=12'}
+
+ strip-ansi@6.0.1:
+ resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
+ engines: {node: '>=8'}
+
+ strip-ansi@7.2.0:
+ resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==}
+ engines: {node: '>=12'}
+
+ strip-bom@4.0.0:
+ resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==}
+ engines: {node: '>=8'}
+
+ strip-final-newline@2.0.0:
+ resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
+ engines: {node: '>=6'}
+
+ strip-json-comments@3.1.1:
+ resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
+ engines: {node: '>=8'}
+
+ superagent@10.3.0:
+ resolution: {integrity: sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ==}
+ engines: {node: '>=14.18.0'}
+
+ supertest@7.2.2:
+ resolution: {integrity: sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==}
+ engines: {node: '>=14.18.0'}
+
+ supports-color@7.2.0:
+ resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+ engines: {node: '>=8'}
+
+ supports-color@8.1.1:
+ resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
+ engines: {node: '>=10'}
+
svix@1.88.0:
resolution: {integrity: sha512-vm/JrrUd3bVyBE+3L33TIyVSs8gS5fYx7lrISvKlDJXTYX1ACH4REX8P1tHxsSKoZi/rvifM1t0XRc5Vc45THw==}
+ synckit@0.11.12:
+ resolution: {integrity: sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+
+ test-exclude@6.0.0:
+ resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==}
+ engines: {node: '>=8'}
+
+ tmpl@1.0.5:
+ resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==}
+
toidentifier@1.0.1:
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
engines: {node: '>=0.6'}
+ tslib@2.8.1:
+ resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
+
tsx@4.21.0:
resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==}
engines: {node: '>=18.0.0'}
hasBin: true
+ type-detect@4.0.8:
+ resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==}
+ engines: {node: '>=4'}
+
+ type-fest@0.21.3:
+ resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==}
+ engines: {node: '>=10'}
+
type-is@2.0.1:
resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==}
engines: {node: '>= 0.6'}
@@ -1180,10 +2415,22 @@ packages:
typeid-js@1.2.0:
resolution: {integrity: sha512-t76ZucAnvGC60ea/HjVsB0TSoB0cw9yjnfurUgtInXQWUI/VcrlZGpO23KN3iSe8yOGUgb1zr7W7uEzJ3hSljA==}
+ undici-types@7.19.2:
+ resolution: {integrity: sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==}
+
unpipe@1.0.0:
resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
engines: {node: '>= 0.8'}
+ unrs-resolver@1.11.1:
+ resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==}
+
+ update-browserslist-db@1.2.3:
+ resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==}
+ hasBin: true
+ peerDependencies:
+ browserslist: '>= 4.21.0'
+
uuid@10.0.0:
resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==}
hasBin: true
@@ -1192,17 +2439,46 @@ packages:
resolution: {integrity: sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==}
hasBin: true
+ uuid@8.3.2:
+ resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
+ deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).
+ hasBin: true
+
+ v8-to-istanbul@9.3.0:
+ resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==}
+ engines: {node: '>=10.12.0'}
+
vary@1.1.2:
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
engines: {node: '>= 0.8'}
+ walker@1.0.8:
+ resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==}
+
web-streams-polyfill@3.3.3:
resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==}
engines: {node: '>= 8'}
+ which@2.0.2:
+ resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+ engines: {node: '>= 8'}
+ hasBin: true
+
+ wrap-ansi@7.0.0:
+ resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
+ engines: {node: '>=10'}
+
+ wrap-ansi@8.1.0:
+ resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
+ engines: {node: '>=12'}
+
wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
+ write-file-atomic@5.0.1:
+ resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==}
+ engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+
ws@8.20.0:
resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==}
engines: {node: '>=10.0.0'}
@@ -1215,10 +2491,32 @@ packages:
utf-8-validate:
optional: true
+ xml@1.0.1:
+ resolution: {integrity: sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==}
+
xtend@4.0.2:
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
engines: {node: '>=0.4'}
+ y18n@5.0.8:
+ resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
+ engines: {node: '>=10'}
+
+ yallist@3.1.1:
+ resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+
+ yargs-parser@21.1.1:
+ resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
+ engines: {node: '>=12'}
+
+ yargs@17.7.2:
+ resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
+ engines: {node: '>=12'}
+
+ yocto-queue@0.1.0:
+ resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+ engines: {node: '>=10'}
+
zod@4.3.6:
resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==}
@@ -1282,56 +2580,261 @@ snapshots:
'@connectrpc/connect-node': 2.1.1(@bufbuild/protobuf@2.11.0)(@connectrpc/connect@2.1.1(@bufbuild/protobuf@2.11.0))
'@connectrpc/connect-web': 2.1.1(@bufbuild/protobuf@2.11.0)(@connectrpc/connect@2.1.1(@bufbuild/protobuf@2.11.0))
- '@bufbuild/protobuf@2.11.0': {}
+ '@babel/code-frame@7.29.0':
+ dependencies:
+ '@babel/helper-validator-identifier': 7.28.5
+ js-tokens: 4.0.0
+ picocolors: 1.1.1
- '@connectrpc/connect-node@2.1.1(@bufbuild/protobuf@2.11.0)(@connectrpc/connect@2.1.1(@bufbuild/protobuf@2.11.0))':
+ '@babel/compat-data@7.29.3': {}
+
+ '@babel/core@7.29.0':
dependencies:
- '@bufbuild/protobuf': 2.11.0
- '@connectrpc/connect': 2.1.1(@bufbuild/protobuf@2.11.0)
+ '@babel/code-frame': 7.29.0
+ '@babel/generator': 7.29.1
+ '@babel/helper-compilation-targets': 7.28.6
+ '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0)
+ '@babel/helpers': 7.29.2
+ '@babel/parser': 7.29.3
+ '@babel/template': 7.28.6
+ '@babel/traverse': 7.29.0
+ '@babel/types': 7.29.0
+ '@jridgewell/remapping': 2.3.5
+ convert-source-map: 2.0.0
+ debug: 4.4.3
+ gensync: 1.0.0-beta.2
+ json5: 2.2.3
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
- '@connectrpc/connect-web@2.1.1(@bufbuild/protobuf@2.11.0)(@connectrpc/connect@2.1.1(@bufbuild/protobuf@2.11.0))':
+ '@babel/generator@7.29.1':
dependencies:
- '@bufbuild/protobuf': 2.11.0
- '@connectrpc/connect': 2.1.1(@bufbuild/protobuf@2.11.0)
+ '@babel/parser': 7.29.3
+ '@babel/types': 7.29.0
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+ jsesc: 3.1.0
- '@connectrpc/connect@2.1.1(@bufbuild/protobuf@2.11.0)':
+ '@babel/helper-compilation-targets@7.28.6':
dependencies:
- '@bufbuild/protobuf': 2.11.0
+ '@babel/compat-data': 7.29.3
+ '@babel/helper-validator-option': 7.27.1
+ browserslist: 4.28.2
+ lru-cache: 5.1.1
+ semver: 6.3.1
- '@drizzle-team/brocli@0.10.2': {}
+ '@babel/helper-globals@7.28.0': {}
- '@esbuild-kit/core-utils@3.3.2':
+ '@babel/helper-module-imports@7.28.6':
dependencies:
- esbuild: 0.18.20
- source-map-support: 0.5.21
+ '@babel/traverse': 7.29.0
+ '@babel/types': 7.29.0
+ transitivePeerDependencies:
+ - supports-color
- '@esbuild-kit/esm-loader@2.6.5':
+ '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)':
dependencies:
- '@esbuild-kit/core-utils': 3.3.2
- get-tsconfig: 4.13.6
+ '@babel/core': 7.29.0
+ '@babel/helper-module-imports': 7.28.6
+ '@babel/helper-validator-identifier': 7.28.5
+ '@babel/traverse': 7.29.0
+ transitivePeerDependencies:
+ - supports-color
- '@esbuild/aix-ppc64@0.25.12':
- optional: true
+ '@babel/helper-plugin-utils@7.28.6': {}
- '@esbuild/aix-ppc64@0.27.4':
- optional: true
+ '@babel/helper-string-parser@7.27.1': {}
- '@esbuild/android-arm64@0.18.20':
- optional: true
+ '@babel/helper-validator-identifier@7.28.5': {}
- '@esbuild/android-arm64@0.25.12':
- optional: true
+ '@babel/helper-validator-option@7.27.1': {}
- '@esbuild/android-arm64@0.27.4':
- optional: true
+ '@babel/helpers@7.29.2':
+ dependencies:
+ '@babel/template': 7.28.6
+ '@babel/types': 7.29.0
- '@esbuild/android-arm@0.18.20':
- optional: true
+ '@babel/parser@7.29.3':
+ dependencies:
+ '@babel/types': 7.29.0
- '@esbuild/android-arm@0.25.12':
- optional: true
+ '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
- '@esbuild/android-arm@0.27.4':
+ '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+
+ '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+
+ '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+
+ '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+
+ '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+
+ '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+
+ '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+
+ '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+
+ '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+
+ '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+
+ '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+
+ '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+
+ '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+
+ '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+
+ '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+
+ '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.29.0)':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/helper-plugin-utils': 7.28.6
+
+ '@babel/template@7.28.6':
+ dependencies:
+ '@babel/code-frame': 7.29.0
+ '@babel/parser': 7.29.3
+ '@babel/types': 7.29.0
+
+ '@babel/traverse@7.29.0':
+ dependencies:
+ '@babel/code-frame': 7.29.0
+ '@babel/generator': 7.29.1
+ '@babel/helper-globals': 7.28.0
+ '@babel/parser': 7.29.3
+ '@babel/template': 7.28.6
+ '@babel/types': 7.29.0
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/types@7.29.0':
+ dependencies:
+ '@babel/helper-string-parser': 7.27.1
+ '@babel/helper-validator-identifier': 7.28.5
+
+ '@bcoe/v8-coverage@0.2.3': {}
+
+ '@bufbuild/protobuf@2.11.0': {}
+
+ '@connectrpc/connect-node@2.1.1(@bufbuild/protobuf@2.11.0)(@connectrpc/connect@2.1.1(@bufbuild/protobuf@2.11.0))':
+ dependencies:
+ '@bufbuild/protobuf': 2.11.0
+ '@connectrpc/connect': 2.1.1(@bufbuild/protobuf@2.11.0)
+
+ '@connectrpc/connect-web@2.1.1(@bufbuild/protobuf@2.11.0)(@connectrpc/connect@2.1.1(@bufbuild/protobuf@2.11.0))':
+ dependencies:
+ '@bufbuild/protobuf': 2.11.0
+ '@connectrpc/connect': 2.1.1(@bufbuild/protobuf@2.11.0)
+
+ '@connectrpc/connect@2.1.1(@bufbuild/protobuf@2.11.0)':
+ dependencies:
+ '@bufbuild/protobuf': 2.11.0
+
+ '@drizzle-team/brocli@0.10.2': {}
+
+ '@emnapi/core@1.10.0':
+ dependencies:
+ '@emnapi/wasi-threads': 1.2.1
+ tslib: 2.8.1
+ optional: true
+
+ '@emnapi/runtime@1.10.0':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@emnapi/wasi-threads@1.2.1':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@esbuild-kit/core-utils@3.3.2':
+ dependencies:
+ esbuild: 0.18.20
+ source-map-support: 0.5.21
+
+ '@esbuild-kit/esm-loader@2.6.5':
+ dependencies:
+ '@esbuild-kit/core-utils': 3.3.2
+ get-tsconfig: 4.13.6
+
+ '@esbuild/aix-ppc64@0.25.12':
+ optional: true
+
+ '@esbuild/aix-ppc64@0.27.4':
+ optional: true
+
+ '@esbuild/android-arm64@0.18.20':
+ optional: true
+
+ '@esbuild/android-arm64@0.25.12':
+ optional: true
+
+ '@esbuild/android-arm64@0.27.4':
+ optional: true
+
+ '@esbuild/android-arm@0.18.20':
+ optional: true
+
+ '@esbuild/android-arm@0.25.12':
+ optional: true
+
+ '@esbuild/android-arm@0.27.4':
optional: true
'@esbuild/android-x64@0.18.20':
@@ -1532,8 +3035,360 @@ snapshots:
'@esbuild/win32-x64@0.27.4':
optional: true
+ '@isaacs/cliui@8.0.2':
+ dependencies:
+ string-width: 5.1.2
+ string-width-cjs: string-width@4.2.3
+ strip-ansi: 7.2.0
+ strip-ansi-cjs: strip-ansi@6.0.1
+ wrap-ansi: 8.1.0
+ wrap-ansi-cjs: wrap-ansi@7.0.0
+
+ '@istanbuljs/load-nyc-config@1.1.0':
+ dependencies:
+ camelcase: 5.3.1
+ find-up: 4.1.0
+ get-package-type: 0.1.0
+ js-yaml: 3.14.2
+ resolve-from: 5.0.0
+
+ '@istanbuljs/schema@0.1.6': {}
+
+ '@jest/console@30.3.0':
+ dependencies:
+ '@jest/types': 30.3.0
+ '@types/node': 25.6.0
+ chalk: 4.1.2
+ jest-message-util: 30.3.0
+ jest-util: 30.3.0
+ slash: 3.0.0
+
+ '@jest/core@30.3.0':
+ dependencies:
+ '@jest/console': 30.3.0
+ '@jest/pattern': 30.0.1
+ '@jest/reporters': 30.3.0
+ '@jest/test-result': 30.3.0
+ '@jest/transform': 30.3.0
+ '@jest/types': 30.3.0
+ '@types/node': 25.6.0
+ ansi-escapes: 4.3.2
+ chalk: 4.1.2
+ ci-info: 4.4.0
+ exit-x: 0.2.2
+ graceful-fs: 4.2.11
+ jest-changed-files: 30.3.0
+ jest-config: 30.3.0(@types/node@25.6.0)
+ jest-haste-map: 30.3.0
+ jest-message-util: 30.3.0
+ jest-regex-util: 30.0.1
+ jest-resolve: 30.3.0
+ jest-resolve-dependencies: 30.3.0
+ jest-runner: 30.3.0
+ jest-runtime: 30.3.0
+ jest-snapshot: 30.3.0
+ jest-util: 30.3.0
+ jest-validate: 30.3.0
+ jest-watcher: 30.3.0
+ pretty-format: 30.3.0
+ slash: 3.0.0
+ transitivePeerDependencies:
+ - babel-plugin-macros
+ - esbuild-register
+ - supports-color
+ - ts-node
+
+ '@jest/diff-sequences@30.3.0': {}
+
+ '@jest/environment@30.3.0':
+ dependencies:
+ '@jest/fake-timers': 30.3.0
+ '@jest/types': 30.3.0
+ '@types/node': 25.6.0
+ jest-mock: 30.3.0
+
+ '@jest/expect-utils@30.3.0':
+ dependencies:
+ '@jest/get-type': 30.1.0
+
+ '@jest/expect@30.3.0':
+ dependencies:
+ expect: 30.3.0
+ jest-snapshot: 30.3.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@jest/fake-timers@30.3.0':
+ dependencies:
+ '@jest/types': 30.3.0
+ '@sinonjs/fake-timers': 15.3.2
+ '@types/node': 25.6.0
+ jest-message-util: 30.3.0
+ jest-mock: 30.3.0
+ jest-util: 30.3.0
+
+ '@jest/get-type@30.1.0': {}
+
+ '@jest/globals@30.3.0':
+ dependencies:
+ '@jest/environment': 30.3.0
+ '@jest/expect': 30.3.0
+ '@jest/types': 30.3.0
+ jest-mock: 30.3.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@jest/pattern@30.0.1':
+ dependencies:
+ '@types/node': 25.6.0
+ jest-regex-util: 30.0.1
+
+ '@jest/reporters@30.3.0':
+ dependencies:
+ '@bcoe/v8-coverage': 0.2.3
+ '@jest/console': 30.3.0
+ '@jest/test-result': 30.3.0
+ '@jest/transform': 30.3.0
+ '@jest/types': 30.3.0
+ '@jridgewell/trace-mapping': 0.3.31
+ '@types/node': 25.6.0
+ chalk: 4.1.2
+ collect-v8-coverage: 1.0.3
+ exit-x: 0.2.2
+ glob: 10.5.0
+ graceful-fs: 4.2.11
+ istanbul-lib-coverage: 3.2.2
+ istanbul-lib-instrument: 6.0.3
+ istanbul-lib-report: 3.0.1
+ istanbul-lib-source-maps: 5.0.6
+ istanbul-reports: 3.2.0
+ jest-message-util: 30.3.0
+ jest-util: 30.3.0
+ jest-worker: 30.3.0
+ slash: 3.0.0
+ string-length: 4.0.2
+ v8-to-istanbul: 9.3.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@jest/schemas@30.0.5':
+ dependencies:
+ '@sinclair/typebox': 0.34.49
+
+ '@jest/snapshot-utils@30.3.0':
+ dependencies:
+ '@jest/types': 30.3.0
+ chalk: 4.1.2
+ graceful-fs: 4.2.11
+ natural-compare: 1.4.0
+
+ '@jest/source-map@30.0.1':
+ dependencies:
+ '@jridgewell/trace-mapping': 0.3.31
+ callsites: 3.1.0
+ graceful-fs: 4.2.11
+
+ '@jest/test-result@30.3.0':
+ dependencies:
+ '@jest/console': 30.3.0
+ '@jest/types': 30.3.0
+ '@types/istanbul-lib-coverage': 2.0.6
+ collect-v8-coverage: 1.0.3
+
+ '@jest/test-sequencer@30.3.0':
+ dependencies:
+ '@jest/test-result': 30.3.0
+ graceful-fs: 4.2.11
+ jest-haste-map: 30.3.0
+ slash: 3.0.0
+
+ '@jest/transform@30.3.0':
+ dependencies:
+ '@babel/core': 7.29.0
+ '@jest/types': 30.3.0
+ '@jridgewell/trace-mapping': 0.3.31
+ babel-plugin-istanbul: 7.0.1
+ chalk: 4.1.2
+ convert-source-map: 2.0.0
+ fast-json-stable-stringify: 2.1.0
+ graceful-fs: 4.2.11
+ jest-haste-map: 30.3.0
+ jest-regex-util: 30.0.1
+ jest-util: 30.3.0
+ pirates: 4.0.7
+ slash: 3.0.0
+ write-file-atomic: 5.0.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@jest/types@30.3.0':
+ dependencies:
+ '@jest/pattern': 30.0.1
+ '@jest/schemas': 30.0.5
+ '@types/istanbul-lib-coverage': 2.0.6
+ '@types/istanbul-reports': 3.0.4
+ '@types/node': 25.6.0
+ '@types/yargs': 17.0.35
+ chalk: 4.1.2
+
+ '@jridgewell/gen-mapping@0.3.13':
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/remapping@2.3.5':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/resolve-uri@3.1.2': {}
+
+ '@jridgewell/sourcemap-codec@1.5.5': {}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ '@napi-rs/wasm-runtime@0.2.12':
+ dependencies:
+ '@emnapi/core': 1.10.0
+ '@emnapi/runtime': 1.10.0
+ '@tybys/wasm-util': 0.10.1
+ optional: true
+
+ '@noble/hashes@1.8.0': {}
+
+ '@paralleldrive/cuid2@2.3.1':
+ dependencies:
+ '@noble/hashes': 1.8.0
+
+ '@pkgjs/parseargs@0.11.0':
+ optional: true
+
+ '@pkgr/core@0.2.9': {}
+
+ '@sinclair/typebox@0.34.49': {}
+
+ '@sinonjs/commons@3.0.1':
+ dependencies:
+ type-detect: 4.0.8
+
+ '@sinonjs/fake-timers@15.3.2':
+ dependencies:
+ '@sinonjs/commons': 3.0.1
+
'@stablelib/base64@1.0.1': {}
+ '@tybys/wasm-util@0.10.1':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@types/babel__core@7.20.5':
+ dependencies:
+ '@babel/parser': 7.29.3
+ '@babel/types': 7.29.0
+ '@types/babel__generator': 7.27.0
+ '@types/babel__template': 7.4.4
+ '@types/babel__traverse': 7.28.0
+
+ '@types/babel__generator@7.27.0':
+ dependencies:
+ '@babel/types': 7.29.0
+
+ '@types/babel__template@7.4.4':
+ dependencies:
+ '@babel/parser': 7.29.3
+ '@babel/types': 7.29.0
+
+ '@types/babel__traverse@7.28.0':
+ dependencies:
+ '@babel/types': 7.29.0
+
+ '@types/istanbul-lib-coverage@2.0.6': {}
+
+ '@types/istanbul-lib-report@3.0.3':
+ dependencies:
+ '@types/istanbul-lib-coverage': 2.0.6
+
+ '@types/istanbul-reports@3.0.4':
+ dependencies:
+ '@types/istanbul-lib-report': 3.0.3
+
+ '@types/node@25.6.0':
+ dependencies:
+ undici-types: 7.19.2
+
+ '@types/stack-utils@2.0.3': {}
+
+ '@types/yargs-parser@21.0.3': {}
+
+ '@types/yargs@17.0.35':
+ dependencies:
+ '@types/yargs-parser': 21.0.3
+
+ '@ungap/structured-clone@1.3.0': {}
+
+ '@unrs/resolver-binding-android-arm-eabi@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-android-arm64@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-darwin-arm64@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-darwin-x64@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-freebsd-x64@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-linux-arm64-gnu@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-linux-arm64-musl@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-linux-riscv64-musl@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-linux-s390x-gnu@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-linux-x64-gnu@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-linux-x64-musl@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-wasm32-wasi@1.11.1':
+ dependencies:
+ '@napi-rs/wasm-runtime': 0.2.12
+ optional: true
+
+ '@unrs/resolver-binding-win32-arm64-msvc@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-win32-ia32-msvc@1.11.1':
+ optional: true
+
+ '@unrs/resolver-binding-win32-x64-msvc@1.11.1':
+ optional: true
+
accepts@2.0.0:
dependencies:
mime-types: 3.0.2
@@ -1541,6 +3396,27 @@ snapshots:
agent-base@7.1.4: {}
+ ansi-escapes@4.3.2:
+ dependencies:
+ type-fest: 0.21.3
+
+ ansi-regex@5.0.1: {}
+
+ ansi-regex@6.2.2: {}
+
+ ansi-styles@4.3.0:
+ dependencies:
+ color-convert: 2.0.1
+
+ ansi-styles@5.2.0: {}
+
+ ansi-styles@6.2.3: {}
+
+ anymatch@3.1.3:
+ dependencies:
+ normalize-path: 3.0.0
+ picomatch: 2.3.2
+
arcjet@1.3.1:
dependencies:
'@arcjet/analyze': 1.3.1
@@ -1551,8 +3427,72 @@ snapshots:
'@arcjet/runtime': 1.3.1
'@arcjet/stable-hash': 1.3.1
+ argparse@1.0.10:
+ dependencies:
+ sprintf-js: 1.0.3
+
+ asap@2.0.6: {}
+
+ asynckit@0.4.0: {}
+
+ babel-jest@30.3.0(@babel/core@7.29.0):
+ dependencies:
+ '@babel/core': 7.29.0
+ '@jest/transform': 30.3.0
+ '@types/babel__core': 7.20.5
+ babel-plugin-istanbul: 7.0.1
+ babel-preset-jest: 30.3.0(@babel/core@7.29.0)
+ chalk: 4.1.2
+ graceful-fs: 4.2.11
+ slash: 3.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ babel-plugin-istanbul@7.0.1:
+ dependencies:
+ '@babel/helper-plugin-utils': 7.28.6
+ '@istanbuljs/load-nyc-config': 1.1.0
+ '@istanbuljs/schema': 0.1.6
+ istanbul-lib-instrument: 6.0.3
+ test-exclude: 6.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ babel-plugin-jest-hoist@30.3.0:
+ dependencies:
+ '@types/babel__core': 7.20.5
+
+ babel-preset-current-node-syntax@1.2.0(@babel/core@7.29.0):
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.29.0)
+ '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.29.0)
+ '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.29.0)
+ '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.29.0)
+ '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.0)
+ '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.29.0)
+ '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.29.0)
+ '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.0)
+ '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.29.0)
+ '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.29.0)
+ '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.29.0)
+ '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.0)
+ '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.0)
+ '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.0)
+
+ babel-preset-jest@30.3.0(@babel/core@7.29.0):
+ dependencies:
+ '@babel/core': 7.29.0
+ babel-plugin-jest-hoist: 30.3.0
+ babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0)
+
+ balanced-match@1.0.2: {}
+
base64-js@1.5.1: {}
+ baseline-browser-mapping@2.10.25: {}
+
bcryptjs@3.0.3: {}
bignumber.js@9.3.1: {}
@@ -1571,6 +3511,27 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ brace-expansion@1.1.14:
+ dependencies:
+ balanced-match: 1.0.2
+ concat-map: 0.0.1
+
+ brace-expansion@2.1.0:
+ dependencies:
+ balanced-match: 1.0.2
+
+ browserslist@4.28.2:
+ dependencies:
+ baseline-browser-mapping: 2.10.25
+ caniuse-lite: 1.0.30001791
+ electron-to-chromium: 1.5.349
+ node-releases: 2.0.38
+ update-browserslist-db: 1.2.3(browserslist@4.28.2)
+
+ bser@2.1.1:
+ dependencies:
+ node-int64: 0.4.0
+
buffer-equal-constant-time@1.0.1: {}
buffer-from@1.1.2: {}
@@ -1587,27 +3548,93 @@ snapshots:
call-bind-apply-helpers: 1.0.2
get-intrinsic: 1.3.0
+ callsites@3.1.0: {}
+
+ camelcase@5.3.1: {}
+
+ camelcase@6.3.0: {}
+
+ caniuse-lite@1.0.30001791: {}
+
+ chalk@4.1.2:
+ dependencies:
+ ansi-styles: 4.3.0
+ supports-color: 7.2.0
+
+ char-regex@1.0.2: {}
+
+ ci-info@4.4.0: {}
+
+ cjs-module-lexer@2.2.0: {}
+
+ cliui@8.0.1:
+ dependencies:
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+ wrap-ansi: 7.0.0
+
+ co@4.6.0: {}
+
+ collect-v8-coverage@1.0.3: {}
+
+ color-convert@2.0.1:
+ dependencies:
+ color-name: 1.1.4
+
+ color-name@1.1.4: {}
+
+ combined-stream@1.0.8:
+ dependencies:
+ delayed-stream: 1.0.0
+
+ component-emitter@1.3.1: {}
+
+ concat-map@0.0.1: {}
+
content-disposition@1.0.1: {}
content-type@1.0.5: {}
+ convert-source-map@2.0.0: {}
+
cookie-signature@1.2.2: {}
cookie@0.7.2: {}
+ cookiejar@2.1.4: {}
+
cors@2.8.6:
dependencies:
object-assign: 4.1.1
vary: 1.1.2
+ cross-spawn@7.0.6:
+ dependencies:
+ path-key: 3.1.1
+ shebang-command: 2.0.0
+ which: 2.0.2
+
data-uri-to-buffer@4.0.1: {}
debug@4.4.3:
dependencies:
ms: 2.1.3
+ dedent@1.7.2: {}
+
+ deepmerge@4.3.1: {}
+
+ delayed-stream@1.0.0: {}
+
depd@2.0.0: {}
+ detect-newline@3.1.0: {}
+
+ dezalgo@1.0.4:
+ dependencies:
+ asap: 2.0.6
+ wrappy: 1.0.2
+
dotenv@17.4.1: {}
drizzle-kit@0.31.10:
@@ -1627,14 +3654,28 @@ snapshots:
es-errors: 1.3.0
gopd: 1.2.0
+ eastasianwidth@0.2.0: {}
+
ecdsa-sig-formatter@1.0.11:
dependencies:
safe-buffer: 5.2.1
ee-first@1.1.1: {}
+ electron-to-chromium@1.5.349: {}
+
+ emittery@0.13.1: {}
+
+ emoji-regex@8.0.0: {}
+
+ emoji-regex@9.2.2: {}
+
encodeurl@2.0.0: {}
+ error-ex@1.3.4:
+ dependencies:
+ is-arrayish: 0.2.1
+
es-define-property@1.0.1: {}
es-errors@1.3.0: {}
@@ -1643,6 +3684,13 @@ snapshots:
dependencies:
es-errors: 1.3.0
+ es-set-tostringtag@2.1.0:
+ dependencies:
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ has-tostringtag: 1.0.2
+ hasown: 2.0.2
+
esbuild@0.18.20:
optionalDependencies:
'@esbuild/android-arm': 0.18.20
@@ -1726,10 +3774,39 @@ snapshots:
'@esbuild/win32-ia32': 0.27.4
'@esbuild/win32-x64': 0.27.4
+ escalade@3.2.0: {}
+
escape-html@1.0.3: {}
+ escape-string-regexp@2.0.0: {}
+
+ esprima@4.0.1: {}
+
etag@1.8.1: {}
+ execa@5.1.1:
+ dependencies:
+ cross-spawn: 7.0.6
+ get-stream: 6.0.1
+ human-signals: 2.1.0
+ is-stream: 2.0.1
+ merge-stream: 2.0.0
+ npm-run-path: 4.0.1
+ onetime: 5.1.2
+ signal-exit: 3.0.7
+ strip-final-newline: 2.0.0
+
+ exit-x@0.2.2: {}
+
+ expect@30.3.0:
+ dependencies:
+ '@jest/expect-utils': 30.3.0
+ '@jest/get-type': 30.1.0
+ jest-matcher-utils: 30.3.0
+ jest-message-util: 30.3.0
+ jest-mock: 30.3.0
+ jest-util: 30.3.0
+
express@5.2.1:
dependencies:
accepts: 2.0.0
@@ -1765,8 +3842,16 @@ snapshots:
extend@3.0.2: {}
+ fast-json-stable-stringify@2.1.0: {}
+
+ fast-safe-stringify@2.1.1: {}
+
fast-sha256@1.3.0: {}
+ fb-watchman@2.0.2:
+ dependencies:
+ bser: 2.1.1
+
fetch-blob@3.2.0:
dependencies:
node-domexception: 1.0.0
@@ -1783,14 +3868,40 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ find-up@4.1.0:
+ dependencies:
+ locate-path: 5.0.0
+ path-exists: 4.0.0
+
+ foreground-child@3.3.1:
+ dependencies:
+ cross-spawn: 7.0.6
+ signal-exit: 4.1.0
+
+ form-data@4.0.5:
+ dependencies:
+ asynckit: 0.4.0
+ combined-stream: 1.0.8
+ es-set-tostringtag: 2.1.0
+ hasown: 2.0.2
+ mime-types: 2.1.35
+
formdata-polyfill@4.0.10:
dependencies:
fetch-blob: 3.2.0
+ formidable@3.5.4:
+ dependencies:
+ '@paralleldrive/cuid2': 2.3.1
+ dezalgo: 1.0.4
+ once: 1.4.0
+
forwarded@0.2.0: {}
fresh@2.0.0: {}
+ fs.realpath@1.0.0: {}
+
fsevents@2.3.3:
optional: true
@@ -1812,6 +3923,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ gensync@1.0.0-beta.2: {}
+
+ get-caller-file@2.0.5: {}
+
get-intrinsic@1.3.0:
dependencies:
call-bind-apply-helpers: 1.0.2
@@ -1825,15 +3940,37 @@ snapshots:
hasown: 2.0.2
math-intrinsics: 1.1.0
+ get-package-type@0.1.0: {}
+
get-proto@1.0.1:
dependencies:
dunder-proto: 1.0.1
es-object-atoms: 1.1.1
+ get-stream@6.0.1: {}
+
get-tsconfig@4.13.6:
dependencies:
resolve-pkg-maps: 1.0.0
+ glob@10.5.0:
+ dependencies:
+ foreground-child: 3.3.1
+ jackspeak: 3.4.3
+ minimatch: 9.0.9
+ minipass: 7.1.3
+ package-json-from-dist: 1.0.1
+ path-scurry: 1.11.1
+
+ glob@7.2.3:
+ dependencies:
+ fs.realpath: 1.0.0
+ inflight: 1.0.6
+ inherits: 2.0.4
+ minimatch: 3.1.5
+ once: 1.4.0
+ path-is-absolute: 1.0.1
+
google-auth-library@10.6.2:
dependencies:
base64-js: 1.5.1
@@ -1849,12 +3986,22 @@ snapshots:
gopd@1.2.0: {}
+ graceful-fs@4.2.11: {}
+
+ has-flag@4.0.0: {}
+
has-symbols@1.1.0: {}
+ has-tostringtag@1.0.2:
+ dependencies:
+ has-symbols: 1.1.0
+
hasown@2.0.2:
dependencies:
function-bind: 1.1.2
+ html-escaper@2.0.2: {}
+
http-errors@2.0.1:
dependencies:
depd: 2.0.0
@@ -1870,20 +4017,411 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ human-signals@2.1.0: {}
+
iconv-lite@0.7.2:
dependencies:
safer-buffer: 2.1.2
+ import-local@3.2.0:
+ dependencies:
+ pkg-dir: 4.2.0
+ resolve-cwd: 3.0.0
+
+ imurmurhash@0.1.4: {}
+
+ inflight@1.0.6:
+ dependencies:
+ once: 1.4.0
+ wrappy: 1.0.2
+
inherits@2.0.4: {}
ipaddr.js@1.9.1: {}
+ is-arrayish@0.2.1: {}
+
+ is-fullwidth-code-point@3.0.0: {}
+
+ is-generator-fn@2.1.0: {}
+
is-promise@4.0.0: {}
+ is-stream@2.0.1: {}
+
+ isexe@2.0.0: {}
+
+ istanbul-lib-coverage@3.2.2: {}
+
+ istanbul-lib-instrument@6.0.3:
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/parser': 7.29.3
+ '@istanbuljs/schema': 0.1.6
+ istanbul-lib-coverage: 3.2.2
+ semver: 7.7.4
+ transitivePeerDependencies:
+ - supports-color
+
+ istanbul-lib-report@3.0.1:
+ dependencies:
+ istanbul-lib-coverage: 3.2.2
+ make-dir: 4.0.0
+ supports-color: 7.2.0
+
+ istanbul-lib-source-maps@5.0.6:
+ dependencies:
+ '@jridgewell/trace-mapping': 0.3.31
+ debug: 4.4.3
+ istanbul-lib-coverage: 3.2.2
+ transitivePeerDependencies:
+ - supports-color
+
+ istanbul-reports@3.2.0:
+ dependencies:
+ html-escaper: 2.0.2
+ istanbul-lib-report: 3.0.1
+
+ jackspeak@3.4.3:
+ dependencies:
+ '@isaacs/cliui': 8.0.2
+ optionalDependencies:
+ '@pkgjs/parseargs': 0.11.0
+
+ jest-changed-files@30.3.0:
+ dependencies:
+ execa: 5.1.1
+ jest-util: 30.3.0
+ p-limit: 3.1.0
+
+ jest-circus@30.3.0:
+ dependencies:
+ '@jest/environment': 30.3.0
+ '@jest/expect': 30.3.0
+ '@jest/test-result': 30.3.0
+ '@jest/types': 30.3.0
+ '@types/node': 25.6.0
+ chalk: 4.1.2
+ co: 4.6.0
+ dedent: 1.7.2
+ is-generator-fn: 2.1.0
+ jest-each: 30.3.0
+ jest-matcher-utils: 30.3.0
+ jest-message-util: 30.3.0
+ jest-runtime: 30.3.0
+ jest-snapshot: 30.3.0
+ jest-util: 30.3.0
+ p-limit: 3.1.0
+ pretty-format: 30.3.0
+ pure-rand: 7.0.1
+ slash: 3.0.0
+ stack-utils: 2.0.6
+ transitivePeerDependencies:
+ - babel-plugin-macros
+ - supports-color
+
+ jest-cli@30.3.0(@types/node@25.6.0):
+ dependencies:
+ '@jest/core': 30.3.0
+ '@jest/test-result': 30.3.0
+ '@jest/types': 30.3.0
+ chalk: 4.1.2
+ exit-x: 0.2.2
+ import-local: 3.2.0
+ jest-config: 30.3.0(@types/node@25.6.0)
+ jest-util: 30.3.0
+ jest-validate: 30.3.0
+ yargs: 17.7.2
+ transitivePeerDependencies:
+ - '@types/node'
+ - babel-plugin-macros
+ - esbuild-register
+ - supports-color
+ - ts-node
+
+ jest-config@30.3.0(@types/node@25.6.0):
+ dependencies:
+ '@babel/core': 7.29.0
+ '@jest/get-type': 30.1.0
+ '@jest/pattern': 30.0.1
+ '@jest/test-sequencer': 30.3.0
+ '@jest/types': 30.3.0
+ babel-jest: 30.3.0(@babel/core@7.29.0)
+ chalk: 4.1.2
+ ci-info: 4.4.0
+ deepmerge: 4.3.1
+ glob: 10.5.0
+ graceful-fs: 4.2.11
+ jest-circus: 30.3.0
+ jest-docblock: 30.2.0
+ jest-environment-node: 30.3.0
+ jest-regex-util: 30.0.1
+ jest-resolve: 30.3.0
+ jest-runner: 30.3.0
+ jest-util: 30.3.0
+ jest-validate: 30.3.0
+ parse-json: 5.2.0
+ pretty-format: 30.3.0
+ slash: 3.0.0
+ strip-json-comments: 3.1.1
+ optionalDependencies:
+ '@types/node': 25.6.0
+ transitivePeerDependencies:
+ - babel-plugin-macros
+ - supports-color
+
+ jest-diff@30.3.0:
+ dependencies:
+ '@jest/diff-sequences': 30.3.0
+ '@jest/get-type': 30.1.0
+ chalk: 4.1.2
+ pretty-format: 30.3.0
+
+ jest-docblock@30.2.0:
+ dependencies:
+ detect-newline: 3.1.0
+
+ jest-each@30.3.0:
+ dependencies:
+ '@jest/get-type': 30.1.0
+ '@jest/types': 30.3.0
+ chalk: 4.1.2
+ jest-util: 30.3.0
+ pretty-format: 30.3.0
+
+ jest-environment-node@30.3.0:
+ dependencies:
+ '@jest/environment': 30.3.0
+ '@jest/fake-timers': 30.3.0
+ '@jest/types': 30.3.0
+ '@types/node': 25.6.0
+ jest-mock: 30.3.0
+ jest-util: 30.3.0
+ jest-validate: 30.3.0
+
+ jest-haste-map@30.3.0:
+ dependencies:
+ '@jest/types': 30.3.0
+ '@types/node': 25.6.0
+ anymatch: 3.1.3
+ fb-watchman: 2.0.2
+ graceful-fs: 4.2.11
+ jest-regex-util: 30.0.1
+ jest-util: 30.3.0
+ jest-worker: 30.3.0
+ picomatch: 4.0.4
+ walker: 1.0.8
+ optionalDependencies:
+ fsevents: 2.3.3
+
+ jest-junit@16.0.0:
+ dependencies:
+ mkdirp: 1.0.4
+ strip-ansi: 6.0.1
+ uuid: 8.3.2
+ xml: 1.0.1
+
+ jest-leak-detector@30.3.0:
+ dependencies:
+ '@jest/get-type': 30.1.0
+ pretty-format: 30.3.0
+
+ jest-matcher-utils@30.3.0:
+ dependencies:
+ '@jest/get-type': 30.1.0
+ chalk: 4.1.2
+ jest-diff: 30.3.0
+ pretty-format: 30.3.0
+
+ jest-message-util@30.3.0:
+ dependencies:
+ '@babel/code-frame': 7.29.0
+ '@jest/types': 30.3.0
+ '@types/stack-utils': 2.0.3
+ chalk: 4.1.2
+ graceful-fs: 4.2.11
+ picomatch: 4.0.4
+ pretty-format: 30.3.0
+ slash: 3.0.0
+ stack-utils: 2.0.6
+
+ jest-mock@30.3.0:
+ dependencies:
+ '@jest/types': 30.3.0
+ '@types/node': 25.6.0
+ jest-util: 30.3.0
+
+ jest-pnp-resolver@1.2.3(jest-resolve@30.3.0):
+ optionalDependencies:
+ jest-resolve: 30.3.0
+
+ jest-regex-util@30.0.1: {}
+
+ jest-resolve-dependencies@30.3.0:
+ dependencies:
+ jest-regex-util: 30.0.1
+ jest-snapshot: 30.3.0
+ transitivePeerDependencies:
+ - supports-color
+
+ jest-resolve@30.3.0:
+ dependencies:
+ chalk: 4.1.2
+ graceful-fs: 4.2.11
+ jest-haste-map: 30.3.0
+ jest-pnp-resolver: 1.2.3(jest-resolve@30.3.0)
+ jest-util: 30.3.0
+ jest-validate: 30.3.0
+ slash: 3.0.0
+ unrs-resolver: 1.11.1
+
+ jest-runner@30.3.0:
+ dependencies:
+ '@jest/console': 30.3.0
+ '@jest/environment': 30.3.0
+ '@jest/test-result': 30.3.0
+ '@jest/transform': 30.3.0
+ '@jest/types': 30.3.0
+ '@types/node': 25.6.0
+ chalk: 4.1.2
+ emittery: 0.13.1
+ exit-x: 0.2.2
+ graceful-fs: 4.2.11
+ jest-docblock: 30.2.0
+ jest-environment-node: 30.3.0
+ jest-haste-map: 30.3.0
+ jest-leak-detector: 30.3.0
+ jest-message-util: 30.3.0
+ jest-resolve: 30.3.0
+ jest-runtime: 30.3.0
+ jest-util: 30.3.0
+ jest-watcher: 30.3.0
+ jest-worker: 30.3.0
+ p-limit: 3.1.0
+ source-map-support: 0.5.13
+ transitivePeerDependencies:
+ - supports-color
+
+ jest-runtime@30.3.0:
+ dependencies:
+ '@jest/environment': 30.3.0
+ '@jest/fake-timers': 30.3.0
+ '@jest/globals': 30.3.0
+ '@jest/source-map': 30.0.1
+ '@jest/test-result': 30.3.0
+ '@jest/transform': 30.3.0
+ '@jest/types': 30.3.0
+ '@types/node': 25.6.0
+ chalk: 4.1.2
+ cjs-module-lexer: 2.2.0
+ collect-v8-coverage: 1.0.3
+ glob: 10.5.0
+ graceful-fs: 4.2.11
+ jest-haste-map: 30.3.0
+ jest-message-util: 30.3.0
+ jest-mock: 30.3.0
+ jest-regex-util: 30.0.1
+ jest-resolve: 30.3.0
+ jest-snapshot: 30.3.0
+ jest-util: 30.3.0
+ slash: 3.0.0
+ strip-bom: 4.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ jest-snapshot@30.3.0:
+ dependencies:
+ '@babel/core': 7.29.0
+ '@babel/generator': 7.29.1
+ '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0)
+ '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0)
+ '@babel/types': 7.29.0
+ '@jest/expect-utils': 30.3.0
+ '@jest/get-type': 30.1.0
+ '@jest/snapshot-utils': 30.3.0
+ '@jest/transform': 30.3.0
+ '@jest/types': 30.3.0
+ babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0)
+ chalk: 4.1.2
+ expect: 30.3.0
+ graceful-fs: 4.2.11
+ jest-diff: 30.3.0
+ jest-matcher-utils: 30.3.0
+ jest-message-util: 30.3.0
+ jest-util: 30.3.0
+ pretty-format: 30.3.0
+ semver: 7.7.4
+ synckit: 0.11.12
+ transitivePeerDependencies:
+ - supports-color
+
+ jest-util@30.3.0:
+ dependencies:
+ '@jest/types': 30.3.0
+ '@types/node': 25.6.0
+ chalk: 4.1.2
+ ci-info: 4.4.0
+ graceful-fs: 4.2.11
+ picomatch: 4.0.4
+
+ jest-validate@30.3.0:
+ dependencies:
+ '@jest/get-type': 30.1.0
+ '@jest/types': 30.3.0
+ camelcase: 6.3.0
+ chalk: 4.1.2
+ leven: 3.1.0
+ pretty-format: 30.3.0
+
+ jest-watcher@30.3.0:
+ dependencies:
+ '@jest/test-result': 30.3.0
+ '@jest/types': 30.3.0
+ '@types/node': 25.6.0
+ ansi-escapes: 4.3.2
+ chalk: 4.1.2
+ emittery: 0.13.1
+ jest-util: 30.3.0
+ string-length: 4.0.2
+
+ jest-worker@30.3.0:
+ dependencies:
+ '@types/node': 25.6.0
+ '@ungap/structured-clone': 1.3.0
+ jest-util: 30.3.0
+ merge-stream: 2.0.0
+ supports-color: 8.1.1
+
+ jest@30.3.0(@types/node@25.6.0):
+ dependencies:
+ '@jest/core': 30.3.0
+ '@jest/types': 30.3.0
+ import-local: 3.2.0
+ jest-cli: 30.3.0(@types/node@25.6.0)
+ transitivePeerDependencies:
+ - '@types/node'
+ - babel-plugin-macros
+ - esbuild-register
+ - supports-color
+ - ts-node
+
+ js-tokens@4.0.0: {}
+
+ js-yaml@3.14.2:
+ dependencies:
+ argparse: 1.0.10
+ esprima: 4.0.1
+
+ jsesc@3.1.0: {}
+
json-bigint@1.0.0:
dependencies:
bignumber.js: 9.3.1
+ json-parse-even-better-errors@2.3.1: {}
+
+ json5@2.2.3: {}
+
jsonwebtoken@9.0.3:
dependencies:
jws: 4.0.1
@@ -1908,6 +4446,14 @@ snapshots:
jwa: 2.0.1
safe-buffer: 5.2.1
+ leven@3.1.0: {}
+
+ lines-and-columns@1.2.4: {}
+
+ locate-path@5.0.0:
+ dependencies:
+ p-locate: 4.1.0
+
lodash.includes@4.3.0: {}
lodash.isboolean@3.0.3: {}
@@ -1922,20 +4468,64 @@ snapshots:
lodash.once@4.1.1: {}
+ lru-cache@10.4.3: {}
+
+ lru-cache@5.1.1:
+ dependencies:
+ yallist: 3.1.1
+
+ make-dir@4.0.0:
+ dependencies:
+ semver: 7.7.4
+
+ makeerror@1.0.12:
+ dependencies:
+ tmpl: 1.0.5
+
math-intrinsics@1.1.0: {}
media-typer@1.1.0: {}
merge-descriptors@2.0.0: {}
+ merge-stream@2.0.0: {}
+
+ methods@1.1.2: {}
+
+ mime-db@1.52.0: {}
+
mime-db@1.54.0: {}
+ mime-types@2.1.35:
+ dependencies:
+ mime-db: 1.52.0
+
mime-types@3.0.2:
dependencies:
mime-db: 1.54.0
+ mime@2.6.0: {}
+
+ mimic-fn@2.1.0: {}
+
+ minimatch@3.1.5:
+ dependencies:
+ brace-expansion: 1.1.14
+
+ minimatch@9.0.9:
+ dependencies:
+ brace-expansion: 2.1.0
+
+ minipass@7.1.3: {}
+
+ mkdirp@1.0.4: {}
+
ms@2.1.3: {}
+ napi-postinstall@0.3.4: {}
+
+ natural-compare@1.4.0: {}
+
negotiator@1.0.0: {}
node-domexception@1.0.0: {}
@@ -1946,6 +4536,16 @@ snapshots:
fetch-blob: 3.2.0
formdata-polyfill: 4.0.10
+ node-int64@0.4.0: {}
+
+ node-releases@2.0.38: {}
+
+ normalize-path@3.0.0: {}
+
+ npm-run-path@4.0.1:
+ dependencies:
+ path-key: 3.1.1
+
object-assign@4.1.1: {}
object-inspect@1.13.4: {}
@@ -1958,8 +4558,46 @@ snapshots:
dependencies:
wrappy: 1.0.2
+ onetime@5.1.2:
+ dependencies:
+ mimic-fn: 2.1.0
+
+ p-limit@2.3.0:
+ dependencies:
+ p-try: 2.2.0
+
+ p-limit@3.1.0:
+ dependencies:
+ yocto-queue: 0.1.0
+
+ p-locate@4.1.0:
+ dependencies:
+ p-limit: 2.3.0
+
+ p-try@2.2.0: {}
+
+ package-json-from-dist@1.0.1: {}
+
+ parse-json@5.2.0:
+ dependencies:
+ '@babel/code-frame': 7.29.0
+ error-ex: 1.3.4
+ json-parse-even-better-errors: 2.3.1
+ lines-and-columns: 1.2.4
+
parseurl@1.3.3: {}
+ path-exists@4.0.0: {}
+
+ path-is-absolute@1.0.1: {}
+
+ path-key@3.1.1: {}
+
+ path-scurry@1.11.1:
+ dependencies:
+ lru-cache: 10.4.3
+ minipass: 7.1.3
+
path-to-regexp@8.3.0: {}
pg-cloudflare@1.3.0:
@@ -1997,6 +4635,18 @@ snapshots:
dependencies:
split2: 4.2.0
+ picocolors@1.1.1: {}
+
+ picomatch@2.3.2: {}
+
+ picomatch@4.0.4: {}
+
+ pirates@4.0.7: {}
+
+ pkg-dir@4.2.0:
+ dependencies:
+ find-up: 4.1.0
+
postal-mime@2.7.4: {}
postgres-array@2.0.0: {}
@@ -2009,11 +4659,19 @@ snapshots:
dependencies:
xtend: 4.0.2
+ pretty-format@30.3.0:
+ dependencies:
+ '@jest/schemas': 30.0.5
+ ansi-styles: 5.2.0
+ react-is: 18.3.1
+
proxy-addr@2.0.7:
dependencies:
forwarded: 0.2.0
ipaddr.js: 1.9.1
+ pure-rand@7.0.1: {}
+
qs@6.15.0:
dependencies:
side-channel: 1.1.0
@@ -2027,11 +4685,21 @@ snapshots:
iconv-lite: 0.7.2
unpipe: 1.0.0
+ react-is@18.3.1: {}
+
+ require-directory@2.1.1: {}
+
resend@6.10.0:
dependencies:
postal-mime: 2.7.4
svix: 1.88.0
+ resolve-cwd@3.0.0:
+ dependencies:
+ resolve-from: 5.0.0
+
+ resolve-from@5.0.0: {}
+
resolve-pkg-maps@1.0.0: {}
router@2.2.0:
@@ -2048,6 +4716,8 @@ snapshots:
safer-buffer@2.1.2: {}
+ semver@6.3.1: {}
+
semver@7.7.4: {}
send@1.2.1:
@@ -2077,6 +4747,12 @@ snapshots:
setprototypeof@1.2.0: {}
+ shebang-command@2.0.0:
+ dependencies:
+ shebang-regex: 3.0.0
+
+ shebang-regex@3.0.0: {}
+
side-channel-list@1.0.0:
dependencies:
es-errors: 1.3.0
@@ -2105,6 +4781,17 @@ snapshots:
side-channel-map: 1.0.1
side-channel-weakmap: 1.0.2
+ signal-exit@3.0.7: {}
+
+ signal-exit@4.1.0: {}
+
+ slash@3.0.0: {}
+
+ source-map-support@0.5.13:
+ dependencies:
+ buffer-from: 1.1.2
+ source-map: 0.6.1
+
source-map-support@0.5.21:
dependencies:
buffer-from: 1.1.2
@@ -2114,6 +4801,12 @@ snapshots:
split2@4.2.0: {}
+ sprintf-js@1.0.3: {}
+
+ stack-utils@2.0.6:
+ dependencies:
+ escape-string-regexp: 2.0.0
+
standardwebhooks@1.0.0:
dependencies:
'@stablelib/base64': 1.0.1
@@ -2121,13 +4814,89 @@ snapshots:
statuses@2.0.2: {}
+ string-length@4.0.2:
+ dependencies:
+ char-regex: 1.0.2
+ strip-ansi: 6.0.1
+
+ string-width@4.2.3:
+ dependencies:
+ emoji-regex: 8.0.0
+ is-fullwidth-code-point: 3.0.0
+ strip-ansi: 6.0.1
+
+ string-width@5.1.2:
+ dependencies:
+ eastasianwidth: 0.2.0
+ emoji-regex: 9.2.2
+ strip-ansi: 7.2.0
+
+ strip-ansi@6.0.1:
+ dependencies:
+ ansi-regex: 5.0.1
+
+ strip-ansi@7.2.0:
+ dependencies:
+ ansi-regex: 6.2.2
+
+ strip-bom@4.0.0: {}
+
+ strip-final-newline@2.0.0: {}
+
+ strip-json-comments@3.1.1: {}
+
+ superagent@10.3.0:
+ dependencies:
+ component-emitter: 1.3.1
+ cookiejar: 2.1.4
+ debug: 4.4.3
+ fast-safe-stringify: 2.1.1
+ form-data: 4.0.5
+ formidable: 3.5.4
+ methods: 1.1.2
+ mime: 2.6.0
+ qs: 6.15.0
+ transitivePeerDependencies:
+ - supports-color
+
+ supertest@7.2.2:
+ dependencies:
+ cookie-signature: 1.2.2
+ methods: 1.1.2
+ superagent: 10.3.0
+ transitivePeerDependencies:
+ - supports-color
+
+ supports-color@7.2.0:
+ dependencies:
+ has-flag: 4.0.0
+
+ supports-color@8.1.1:
+ dependencies:
+ has-flag: 4.0.0
+
svix@1.88.0:
dependencies:
standardwebhooks: 1.0.0
uuid: 10.0.0
+ synckit@0.11.12:
+ dependencies:
+ '@pkgr/core': 0.2.9
+
+ test-exclude@6.0.0:
+ dependencies:
+ '@istanbuljs/schema': 0.1.6
+ glob: 7.2.3
+ minimatch: 3.1.5
+
+ tmpl@1.0.5: {}
+
toidentifier@1.0.1: {}
+ tslib@2.8.1:
+ optional: true
+
tsx@4.21.0:
dependencies:
esbuild: 0.27.4
@@ -2135,6 +4904,10 @@ snapshots:
optionalDependencies:
fsevents: 2.3.3
+ type-detect@4.0.8: {}
+
+ type-fest@0.21.3: {}
+
type-is@2.0.1:
dependencies:
content-type: 1.0.5
@@ -2145,20 +4918,105 @@ snapshots:
dependencies:
uuid: 10.0.0
+ undici-types@7.19.2: {}
+
unpipe@1.0.0: {}
+ unrs-resolver@1.11.1:
+ dependencies:
+ napi-postinstall: 0.3.4
+ optionalDependencies:
+ '@unrs/resolver-binding-android-arm-eabi': 1.11.1
+ '@unrs/resolver-binding-android-arm64': 1.11.1
+ '@unrs/resolver-binding-darwin-arm64': 1.11.1
+ '@unrs/resolver-binding-darwin-x64': 1.11.1
+ '@unrs/resolver-binding-freebsd-x64': 1.11.1
+ '@unrs/resolver-binding-linux-arm-gnueabihf': 1.11.1
+ '@unrs/resolver-binding-linux-arm-musleabihf': 1.11.1
+ '@unrs/resolver-binding-linux-arm64-gnu': 1.11.1
+ '@unrs/resolver-binding-linux-arm64-musl': 1.11.1
+ '@unrs/resolver-binding-linux-ppc64-gnu': 1.11.1
+ '@unrs/resolver-binding-linux-riscv64-gnu': 1.11.1
+ '@unrs/resolver-binding-linux-riscv64-musl': 1.11.1
+ '@unrs/resolver-binding-linux-s390x-gnu': 1.11.1
+ '@unrs/resolver-binding-linux-x64-gnu': 1.11.1
+ '@unrs/resolver-binding-linux-x64-musl': 1.11.1
+ '@unrs/resolver-binding-wasm32-wasi': 1.11.1
+ '@unrs/resolver-binding-win32-arm64-msvc': 1.11.1
+ '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1
+ '@unrs/resolver-binding-win32-x64-msvc': 1.11.1
+
+ update-browserslist-db@1.2.3(browserslist@4.28.2):
+ dependencies:
+ browserslist: 4.28.2
+ escalade: 3.2.0
+ picocolors: 1.1.1
+
uuid@10.0.0: {}
uuid@13.0.0: {}
+ uuid@8.3.2: {}
+
+ v8-to-istanbul@9.3.0:
+ dependencies:
+ '@jridgewell/trace-mapping': 0.3.31
+ '@types/istanbul-lib-coverage': 2.0.6
+ convert-source-map: 2.0.0
+
vary@1.1.2: {}
+ walker@1.0.8:
+ dependencies:
+ makeerror: 1.0.12
+
web-streams-polyfill@3.3.3: {}
+ which@2.0.2:
+ dependencies:
+ isexe: 2.0.0
+
+ wrap-ansi@7.0.0:
+ dependencies:
+ ansi-styles: 4.3.0
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+
+ wrap-ansi@8.1.0:
+ dependencies:
+ ansi-styles: 6.2.3
+ string-width: 5.1.2
+ strip-ansi: 7.2.0
+
wrappy@1.0.2: {}
+ write-file-atomic@5.0.1:
+ dependencies:
+ imurmurhash: 0.1.4
+ signal-exit: 4.1.0
+
ws@8.20.0: {}
+ xml@1.0.1: {}
+
xtend@4.0.2: {}
+ y18n@5.0.8: {}
+
+ yallist@3.1.1: {}
+
+ yargs-parser@21.1.1: {}
+
+ yargs@17.7.2:
+ dependencies:
+ cliui: 8.0.1
+ escalade: 3.2.0
+ get-caller-file: 2.0.5
+ require-directory: 2.1.1
+ string-width: 4.2.3
+ y18n: 5.0.8
+ yargs-parser: 21.1.1
+
+ yocto-queue@0.1.0: {}
+
zod@4.3.6: {}
diff --git a/server/src/app.js b/server/src/app.js
new file mode 100644
index 0000000..9721b01
--- /dev/null
+++ b/server/src/app.js
@@ -0,0 +1,37 @@
+import cors from "cors";
+import express from "express";
+import { securityMiddleware } from "./arcjet.js";
+import { CONFIG } from "./config/config.js";
+import { authRouter } from "./routes/authRoutes.js";
+import { commentaryRouter } from "./routes/commentary.js";
+import { matchesRouter } from "./routes/matches.js";
+
+export function createApp() {
+ const app = express();
+
+ app.use(express.json());
+ app.use(
+ cors({
+ origin: CONFIG.CLIENT_URL,
+ credentials: true,
+ }),
+ );
+
+ app.get("/", (_req, res) => {
+ res.send("Server is up and running!");
+ });
+
+ // Lightweight liveness/readiness endpoint used by Docker HEALTHCHECK
+ // and Kubernetes probes. Must not depend on external services.
+ app.get("/health", (_req, res) => {
+ res.status(200).json({ status: "ok" });
+ });
+
+ app.use(securityMiddleware());
+
+ app.use("/matches", matchesRouter);
+ app.use("/matches/:id/commentary", commentaryRouter);
+ app.use("/api/auth", authRouter);
+
+ return app;
+}
diff --git a/server/src/index.js b/server/src/index.js
index 1d0a6f7..cc1bdb9 100644
--- a/server/src/index.js
+++ b/server/src/index.js
@@ -1,41 +1,16 @@
import "dotenv/config";
import http from "node:http";
-import cors from "cors";
-import express from "express";
-import { securityMiddleware } from "./arcjet.js";
+import { createApp } from "./app.js";
import { CONFIG } from "./config/config.js";
-import { authRouter } from "./routes/authRoutes.js";
-import { commentaryRouter } from "./routes/commentary.js";
-import { matchesRouter } from "./routes/matches.js";
import { logIt } from "./utils/utils.js";
import { attachWebSocketServer } from "./ws/server.js";
const PORT = Number(CONFIG.PORT);
const HOST = CONFIG.HOST;
-const app = express();
+const app = createApp();
const server = http.createServer(app);
-app.use(express.json());
-app.use(
- cors({
- origin: CONFIG.CLIENT_URL,
- credentials: true,
- }),
-);
-app.get("/", (_req, res) => {
- res.send("Server is up and running!");
-});
-
-// arcjet security middleware to protect the api endpoints
-app.use(securityMiddleware());
-
-// Routes
-app.use("/matches", matchesRouter);
-app.use("/matches/:id/commentary", commentaryRouter);
-app.use("/api/auth", authRouter);
-
-// attach the websocket server with the express app
const { broadcastMatchCreated, broadcastCommentary } = attachWebSocketServer(server);
app.locals.broadcastMatchCreated = broadcastMatchCreated;
app.locals.broadcastCommentary = broadcastCommentary;