diff --git a/.github/ISSUE_TEMPLATE/add-edit-team-meeting-time.md b/.github/ISSUE_TEMPLATE/add-edit-team-meeting-time.md
index ccec3c2dc..2cecb0d71 100644
--- a/.github/ISSUE_TEMPLATE/add-edit-team-meeting-time.md
+++ b/.github/ISSUE_TEMPLATE/add-edit-team-meeting-time.md
@@ -2,8 +2,7 @@
name: Add/edit team meeting time
about: Use this template to request a meeting time change for your project
title: Meeting time change request for [Project Name]
-labels: '1 week change request, feature: meeting time change ticket, role: Product,
- size: 0.25, time-sensitive'
+labels: '1 week change request, role: Product, time-sensitive'
assignees: ''
---
diff --git a/.github/ISSUE_TEMPLATE/add-remove-pm-admin-access.md b/.github/ISSUE_TEMPLATE/add-remove-pm-admin-access.md
index 7aba258c8..904ce3d6a 100644
--- a/.github/ISSUE_TEMPLATE/add-remove-pm-admin-access.md
+++ b/.github/ISSUE_TEMPLATE/add-remove-pm-admin-access.md
@@ -3,19 +3,23 @@ name: Add/Remove PM Admin Access
about: VRMS admin team members are currently adding or removing product managers manually.
This issue is how PMs can request changes to access on VRMS.
title: 'Add/Remove PM Admin Access: [name of project]'
-labels: 'role: Product, size: 0.25, time-sensitive'
+labels: 'p-feature: Add/Remove PM access, role: Product, time-sensitive'
assignees: ''
---
Person who is staying on the team or leaving a team, person adds ticket.
-feature: add/remove admin access
+### Required information
+- Name of PM requesting changes (your name):
+- Slack handle:
-Required information:
-- Name of PM:
-- Are they being added or remove Who is being added as a new PM on the team and needs admin access to the project on VRMS?
- - Slack Handle:
- - Email
- - Add or Remove.
- - Who has the access now, who to remove?
+#### People you are removing
+- Name:
+- Slack Handle:
+- Email:
+
+#### People you are adding
+- Name:
+- Slack Handle:
+- Email:
diff --git a/.github/ISSUE_TEMPLATE/address-warnings-for-rule-.md b/.github/ISSUE_TEMPLATE/address-warnings-for-rule-.md
index 908f326f9..1be2edfa8 100644
--- a/.github/ISSUE_TEMPLATE/address-warnings-for-rule-.md
+++ b/.github/ISSUE_TEMPLATE/address-warnings-for-rule-.md
@@ -2,7 +2,7 @@
name: 'Address warnings for rule:'
about: Describe this issue template's purpose here.
title: 'Address warnings for rule:'
-labels: 'feature: eslint warnings, role: Front End, size: 1pt'
+labels: 'role: Front End, size: 1pt'
assignees: ''
---
diff --git a/.github/ISSUE_TEMPLATE/blank-issue.md b/.github/ISSUE_TEMPLATE/blank-issue.md
index 3d510859f..8e0f64d61 100644
--- a/.github/ISSUE_TEMPLATE/blank-issue.md
+++ b/.github/ISSUE_TEMPLATE/blank-issue.md
@@ -2,7 +2,7 @@
name: Blank Issue
about: Consistent formatting make Issues concise and easy to navigate
title: ''
-labels: ''
+labels: 'feature: missing, milestone: missing, role: Missing, size: missing'
assignees: ''
---
diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml
new file mode 100644
index 000000000..8d1a03a33
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug-report.yml
@@ -0,0 +1,64 @@
+name: 'Bug Report'
+description: 'Bug report HackforLA issue form.'
+
+body:
+ - type: textarea
+ id: description
+ attributes:
+ label: Describe the Bug
+ description: Clearly state the bug
+ validations:
+ required: true
+ - type: dropdown
+ id: feature
+ attributes:
+ label: Feature
+ description: Select a feature related to the bug
+ options:
+ - AWS
+ - Brand
+ - Login
+ - Users
+ - Agenda
+ - Check in
+ - Database
+ - Dashboard
+ - Marketing
+ - Onboarding
+ - Repo Update
+ - Documentation
+ - GitHub Actions
+ - GitHub Hygiene
+ - Infrastructure
+ - Package Update
+ - ESLint Warnings
+ - Form validation
+ - Recurring Events
+ - Project Management
+ - Account Setup Automation
+ - Meeting Time Change Ticket
+ - Add/Remove PM access
+ validations:
+ required: true
+ - type: textarea
+ id: replicate
+ attributes:
+ label: How to Replicate
+ description: Clearly state how to replicate the bug
+ validations:
+ required: true
+ - type: dropdown
+ id: notification
+ attributes:
+ label: Request notification after bug squashing
+ description: Request notification after a bug is squashed
+ options:
+ - Yes
+ - No
+ validations:
+ required: true
+ - type: textarea
+ id: resource
+ attributes:
+ label: Resources/Information
+ description: Include any important resources/information
\ No newline at end of file
diff --git a/.github/workflows/New-issue-create-card.yml b/.github/workflows/New-issue-create-card.yml
deleted file mode 100644
index 2c406221b..000000000
--- a/.github/workflows/New-issue-create-card.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-name: Create card for new issues
-on:
- issues:
- types: [opened]
-jobs:
- createCard:
- runs-on: ubuntu-latest
- steps:
- - name: Create or Update Project Card
- uses: peter-evans/create-or-update-project-card@v2
- with:
- project-name: VRMS v0.4
- column-name: New Issue Approval
diff --git a/.github/workflows/all-PRs.yaml b/.github/workflows/all-PRs.yaml
index 2e10cc6a3..03b6f8cfd 100644
--- a/.github/workflows/all-PRs.yaml
+++ b/.github/workflows/all-PRs.yaml
@@ -9,24 +9,24 @@ jobs:
test-backend-unit:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
- name: Make envfile
- uses: SpicyPizza/create-envfile@v1
+ uses: SpicyPizza/create-envfile@v2
with:
envkey_BACKEND_PORT: ${{ secrets.BACKEND_PORT }}
envkey_REACT_APP_PROXY: ${{ secrets.REACT_APP_PROXY }}
file_name: backend/.env
- name: Build the backend container
- run: docker-compose build backend
+ run: docker compose build backend
- name: Run backend unit test suite
- run: docker-compose run --rm backend yarn run test --testPathIgnorePatterns=routers
+ run: docker compose run --rm backend yarn run test --testPathIgnorePatterns=routers
test-backend-integration:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
- name: Make envfile
- uses: SpicyPizza/create-envfile@v1
+ uses: SpicyPizza/create-envfile@v2
with:
envkey_CUSTOM_REQUEST_HEADER: ${{ secrets.CUSTOM_REQUEST_HEADER }}
envkey_SLACK_OAUTH_TOKEN: ${{ secrets.SLACK_OAUTH_TOKEN }}
@@ -47,24 +47,24 @@ jobs:
envkey_MAILHOG_PASSWORD: ${{ secrets.MAILHOG_PASSWORD }}
file_name: backend/.env
- name: Build the backend container
- run: docker-compose build backend
+ run: docker compose build backend
- name: Run backend integration test suite
- run: docker-compose run --rm backend yarn run test backend/routers/
+ run: docker compose run --rm backend yarn run test backend/routers/
test-client-unit:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
- name: Build the client container
- run: docker-compose build client
+ run: docker compose build client
- name: Run client unit test suite
- run: docker-compose run --rm client yarn run test
+ run: docker compose run --rm client yarn run test
# test-mvp-unit:
# runs-on: ubuntu-latest
# steps:
-# - uses: actions/checkout@v2
+# - uses: actions/checkout@v4
# - name: Build the client mvp container
-# run: docker-compose build client-mvp-04
+# run: docker compose build client-mvp-04
# - name: Run frontend unit test suite
-# run: docker-compose run --rm client-mvp-04 yarn run test
+# run: docker compose run --rm client-mvp-04 yarn run test
diff --git a/.github/workflows/all-merges.yaml b/.github/workflows/all-merges.yaml
index a2b7e9d07..7eeeb0479 100644
--- a/.github/workflows/all-merges.yaml
+++ b/.github/workflows/all-merges.yaml
@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
if: github.event.pull_request.merged == true
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
diff --git a/.github/workflows/aws-backend-deploy.yml b/.github/workflows/aws-backend-deploy.yml
index 277ad8e5b..a4a7f1a0c 100644
--- a/.github/workflows/aws-backend-deploy.yml
+++ b/.github/workflows/aws-backend-deploy.yml
@@ -4,12 +4,12 @@ on:
inputs:
env:
type: choice
- description: 'AWS Incubator Env'
- options:
- - dev
- - prod
+ description: "AWS Incubator Env"
+ options:
+ - dev
+ - prod
ref:
- description: 'Branch, Tag, or SHA'
+ description: "Branch, Tag, or SHA"
required: true
env:
AWS_SHARED_CLUSTER: incubator-prod
@@ -19,95 +19,95 @@ env:
DOCKER_PATH: backend
jobs:
setup_env:
- name: Set-up environment
+ name: Set-up environment
runs-on: ubuntu-latest
steps:
- - name: Debug Action
- uses: hmarr/debug-action@v1.0.0
- - name: Checkout
- uses: actions/checkout@v2
- with:
- ref: ${{ github.event.inputs.ref }}
- - name: Set AWS Env & Image Tag per workflow
- run: |
- SHORT_SHA=$(git rev-parse --short HEAD)
- if [[ "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]]; then
- INPUT_ENV=${{ github.event.inputs.env }}; INPUT_REF=${{ github.event.inputs.ref }}
- echo AWS_APPENV="$AWS_APP_NAME"-$INPUT_ENV >> $GITHUB_ENV
- echo IMAGE_TAG=$SHORT_SHA >> $GITHUB_ENV
- fi
+ - name: Debug Action
+ uses: hmarr/debug-action@v1.0.0
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.inputs.ref }}
+ - name: Set AWS Env & Image Tag per workflow
+ run: |
+ if [[ "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]]; then
+ INPUT_ENV=${{ github.event.inputs.env }}; INPUT_REF=${{ github.event.inputs.ref }}
+ echo AWS_APPENV=$AWS_APP_NAME >> $GITHUB_ENV
+ echo IMAGE_TAG=$(git rev-parse --short HEAD) >> $GITHUB_ENV
+ echo BUILD_SHA=$(git rev-parse --short HEAD) >> $GITHUB_ENV
+ fi
outputs:
AWS_APPENV: ${{ env.AWS_APPENV }}
IMAGE_TAG: ${{ env.IMAGE_TAG }}
+ BUILD_SHA: ${{ env.BUILD_SHA }}
build:
name: Build & Push Docker Image
runs-on: ubuntu-latest
+ permissions:
+ id-token: write # Needed for OIDC authentication to AWS
needs: [setup_env]
steps:
- - name: Checkout
- uses: actions/checkout@v2
- with:
- ref: ${{ github.event.inputs.ref }}
- - name: Configure AWS credentials
- uses: aws-actions/configure-aws-credentials@v1
- with:
- aws-access-key-id: ${{ secrets.INCUBATOR_AWS_ACCESS_KEY_ID }}
- aws-secret-access-key: ${{ secrets.INCUBATOR_AWS_SECRET_ACCESS_KEY }}
- aws-region: ${{ env.AWS_REGION }}
- - name: Login to Amazon ECR
- id: login-ecr
- uses: aws-actions/amazon-ecr-login@v1
- - name: Init Docker Cache
- uses: jpribyl/action-docker-layer-caching@v0.1.0
- with:
- key: ${{ github.workflow }}-2-{hash}
- restore-keys: |
- ${{ github.workflow }}-2-
- - name: Build & Push Image to ECR
- uses: kciter/aws-ecr-action@v3
- with:
- access_key_id: ${{ secrets.INCUBATOR_AWS_ACCESS_KEY_ID }}
- secret_access_key: ${{ secrets.INCUBATOR_AWS_SECRET_ACCESS_KEY }}
- account_id: ${{ secrets.INCUBATOR_AWS_ACCOUNT_ID }}
- repo: ${{ needs.setup_env.outputs.AWS_APPENV }}
- region: ${{ env.AWS_REGION }}
- tags: latest,${{ needs.setup_env.outputs.IMAGE_TAG }}
- dockerfile: ${{ env.DOCKERFILE }}
- path: ${{ env.DOCKER_PATH }}
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.inputs.ref }}
+ - name: Configure AWS credentials
+ uses: aws-actions/configure-aws-credentials@v3 # Sets AWS credentials for CLI
+ with:
+ role-to-assume: arn:aws:iam::035866691871:role/incubator-cicd-vrms # IAM role for deploy
+ role-session-name: incubator-cicd-vrms-gha # Session name for audit
+ aws-region: us-west-2 # AWS region
+ - name: Login to Amazon ECR
+ id: login-ecr
+ uses: aws-actions/amazon-ecr-login@v2
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
+ - name: Debug Build Variables
+ env:
+ BUILD_SHA: ${{ needs.setup_env.outputs.BUILD_SHA }}
+ IMAGE_TAG: ${{ needs.setup_env.outputs.IMAGE_TAG }}
+ run: |
+ echo "=== Build Debug Information ==="
+ echo "BUILD_SHA: $BUILD_SHA"
+ echo "IMAGE_TAG: $IMAGE_TAG"
+ echo "DOCKERFILE: ${{ env.DOCKERFILE }}"
+ echo "DOCKER_PATH: ${{ env.DOCKER_PATH }}"
+ echo "================================"
+ - name: Build & Push Image to ECR
+ env:
+ ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
+ ECR_REPOSITORY: ${{ needs.setup_env.outputs.AWS_APPENV }}
+ IMAGE_TAG: ${{ needs.setup_env.outputs.IMAGE_TAG }}
+ BUILD_SHA: ${{ needs.setup_env.outputs.BUILD_SHA }}
+ run: |
+ docker buildx build \
+ --platform linux/amd64 \
+ --cache-from type=gha \
+ --cache-to type=gha,mode=max \
+ --push \
+ --build-arg BUILD_SHA=$BUILD_SHA \
+ -f ${{ env.DOCKERFILE }} \
+ -t $ECR_REGISTRY/$ECR_REPOSITORY:${{ github.event.inputs.env }} \
+ -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG \
+ ${{ env.DOCKER_PATH }}
deploy:
name: Deploy to AWS ECS
runs-on: ubuntu-latest
needs: [setup_env, build]
+ permissions:
+ id-token: write # Needed for OIDC authentication to AWS
steps:
- - name: Configure AWS credentials
- uses: aws-actions/configure-aws-credentials@v1
- with:
- aws-access-key-id: ${{ secrets.INCUBATOR_AWS_ACCESS_KEY_ID }}
- aws-secret-access-key: ${{ secrets.INCUBATOR_AWS_SECRET_ACCESS_KEY }}
- aws-region: ${{ env.AWS_REGION }}
- - name: Login to Amazon ECR
- id: login-ecr
- uses: aws-actions/amazon-ecr-login@v1
- - name: Pull Task Definition & write to file
- id: aws-task-definition
- run: |
- aws ecs describe-task-definition \
- --task-definition ${{ needs.setup_env.outputs.AWS_APPENV }} \
- --query taskDefinition | \
- jq 'del(.taskDefinitionArn,.revision,.status,.registeredBy,.registeredAt,.compatibilities,.requiresAttributes)' > task-def.json
- - name: Interpolate new Docker Image into Task Definition
- id: task-definition
- uses: aws-actions/amazon-ecs-render-task-definition@v1
- with:
- task-definition: task-def.json
- container-name: ${{ needs.setup_env.outputs.AWS_APPENV }}
- image: ${{ steps.login-ecr.outputs.registry }}/${{ needs.setup_env.outputs.AWS_APPENV }}:${{ needs.setup_env.outputs.IMAGE_TAG }}
- - name: Deploy Amazon ECS
- uses: aws-actions/amazon-ecs-deploy-task-definition@v1
- with:
- task-definition: ${{ steps.task-definition.outputs.task-definition }}
- service: ${{ needs.setup_env.outputs.AWS_APPENV }}
- cluster: ${{ env.AWS_SHARED_CLUSTER }}
- wait-for-service-stability: true
- wait-for-minutes: 5 minutes
-
+ - name: Configure AWS credentials
+ uses: aws-actions/configure-aws-credentials@v3 # Sets AWS credentials for CLI
+ with:
+ role-to-assume: arn:aws:iam::035866691871:role/incubator-cicd-vrms # IAM role for deploy
+ role-session-name: incubator-cicd-vrms-gha # Session name for audit
+ aws-region: us-west-2 # AWS region
+ - name: Restart ECS Service
+ id: redeploy-service
+ env:
+ SERVICE_NAME: ${{env.AWS_APP_NAME}}-${{ github.event.inputs.env }} # ECS service name
+ # Force a new deployment of the ECS service to use the latest Docker image
+ run: |
+ aws ecs update-service --force-new-deployment --service $SERVICE_NAME --cluster $AWS_SHARED_CLUSTER
+
\ No newline at end of file
diff --git a/.github/workflows/aws-frontend-deploy.mdx b/.github/workflows/aws-frontend-deploy.mdx
new file mode 100644
index 000000000..c9755cb10
--- /dev/null
+++ b/.github/workflows/aws-frontend-deploy.mdx
@@ -0,0 +1,182 @@
+---
+title: AWS Frontend Deploy Workflow
+---
+
+# AWS Frontend Deploy Workflow
+
+This document describes the purpose and structure of the GitHub Actions workflow defined in `.github/workflows/aws-frontend-deploy.yml`.
+
+## Overview
+
+This workflow automates the process of building, pushing, and deploying the frontend application to AWS. It is triggered manually via the GitHub Actions UI using `workflow_dispatch`:
+
+```yaml
+on:
+ workflow_dispatch: # Manual trigger from GitHub Actions UI
+ inputs:
+ env:
+ type: choice
+ description: "AWS Incubator Env"
+ options: # Selectable environment options
+ - dev
+ - prod
+ ref:
+ description: "Branch, Tag, or SHA" # Code reference to deploy
+ required: true
+```
+
+Users can select the environment (`dev` or `prod`) and specify a branch, tag, or SHA to deploy.
+
+## Environment Variables
+
+The workflow sets several environment variables for use throughout the jobs:
+
+```yaml
+env:
+ AWS_SHARED_CLUSTER: incubator-prod # Target ECS cluster name
+ AWS_APP_NAME: vrms-frontend # Application name for tagging and service
+ AWS_REGION: us-west-2 # AWS region for deployment
+ DOCKERFILE: Dockerfile.prod # Dockerfile used for build
+ DOCKER_PATH: client # Path to frontend source and Dockerfile
+```
+
+Each of these environment variables is set at the top level of the workflow and is available to all jobs and steps. Here is a description of each:
+
+- `AWS_SHARED_CLUSTER`: The name of the AWS ECS cluster to which the frontend will be deployed. In this workflow, it is set to `incubator-prod`. _Might be sourced from your AWS infrastructure naming conventions or deployment environment._
+- `AWS_APP_NAME`: The application name used for tagging Docker images and identifying the service in AWS. Here, it is set to `vrms-frontend`. _Might be sourced from your project or repository name._
+- `AWS_REGION`: The AWS region where resources are deployed. Set to `us-west-2` (Oregon). _Might be sourced from your AWS account's preferred deployment region._
+- `DOCKERFILE`: The Dockerfile used for building the frontend image. Set to `Dockerfile.prod`, indicating a production-ready build. _Might be sourced from your repository's Docker configuration._
+- `DOCKER_PATH`: The path to the directory containing the Dockerfile and frontend source code. Set to `client`. _Might be sourced from your repository structure._
+
+## Jobs
+
+### 1. `setup_env`
+
+This job checks out the code and sets up environment-specific variables for the deployment:
+
+```yaml
+jobs:
+ setup_env:
+ name: Set-up environment
+ runs-on: ubuntu-latest
+ steps:
+ - name: Debug Action
+ uses: hmarr/debug-action@v2 # Prints debug info to logs
+ - name: Checkout
+ uses: actions/checkout@v3 # Checks out code at specified ref
+ with:
+ ref: ${{ github.event.inputs.ref }} # Uses user-specified ref
+ - name: Set AWS Env & Image Tag per workflow
+ # Get short SHA of current commit
+ # if -- action is triggered manually
+ # Get environment input from workflow dispatch
+ # Get ref input from workflow dispatch
+ # Set AWS_APPENV for later steps
+ # Set IMAGE_TAG for later steps
+ # fi
+ run: |
+ SHORT_SHA=$(git rev-parse --short HEAD)
+ if [[ "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]]; then
+ INPUT_ENV=${{ github.event.inputs.env }}
+ INPUT_REF=${{ github.event.inputs.ref }}
+ echo AWS_APPENV="$AWS_APP_NAME"-$INPUT_ENV >> $GITHUB_ENV
+ echo IMAGE_TAG=$SHORT_SHA >> $GITHUB_ENV
+ fi
+```
+
+This job outputs the application environment and image tag for use in subsequent jobs.
+
+### 2. `build`
+
+This job builds the Docker image for the frontend and pushes it to Amazon ECR:
+
+```yaml
+build:
+ name: Build & Push Docker Image
+ runs-on: ubuntu-latest
+ permissions:
+ id-token: write # Needed for OIDC authentication to AWS
+ needs: [setup_env] # Waits for environment setup
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3 # Checks out code at specified ref
+ with:
+ ref: ${{ github.event.inputs.ref }}
+ - name: Setup Node.js
+ uses: actions/setup-node@v3 # Sets up Node.js for build
+ with:
+ node-version: 18 # Uses Node.js v18
+ cache: "npm" # Enables npm caching
+ - name: Configure AWS credentials
+ uses: aws-actions/configure-aws-credentials@v3 # Sets AWS credentials for CLI
+ with:
+ role-to-assume: arn:aws:iam::035866691871:role/incubator-cicd-vrms # IAM role for deploy
+ role-session-name: incubator-cicd-vrms-gha # Session name for audit
+ aws-region: us-west-2 # AWS region
+ - name: Login to Amazon ECR
+ id: login-ecr
+ uses: aws-actions/amazon-ecr-login@v1 # Authenticates Docker to ECR
+ - name: Build, tag, and push the image to Amazon ECR
+ id: build-push-image
+ env:
+ ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} # ECR registry URL
+ ECR_REPOSITORY: ${{ env.AWS_APP_NAME }} # ECR repo name
+ run: |
+ ls # List files for debug
+ cd ./${{ env.DOCKER_PATH }} # Enter frontend directory
+ docker build \
+ -f ${{ env.DOCKERFILE }} \ # Use production Dockerfile
+ -t ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ needs.setup_env.outputs.IMAGE_TAG }} \ # Tag with image SHA
+ -t ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ github.event.inputs.env }} \ # Tag with environment
+ .
+ docker image push --all-tags ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }} # Push all tags
+```
+
+### 3. `deploy`
+
+This job deploys the new Docker image to AWS ECS by forcing a new deployment of the ECS service:
+
+```yaml
+deploy:
+ name: Deploy to AWS ECS
+ runs-on: ubuntu-latest
+ needs: [setup_env, build] # Waits for setup and build jobs
+ permissions:
+ id-token: write # Needed for OIDC authentication to AWS
+ steps:
+ - name: Configure AWS credentials
+ uses: aws-actions/configure-aws-credentials@v3 # Sets AWS credentials for CLI
+ with:
+ role-to-assume: arn:aws:iam::035866691871:role/incubator-cicd-vrms # IAM role for deploy
+ role-session-name: incubator-cicd-vrms-gha # Session name for audit
+ aws-region: us-west-2 # AWS region
+ - name: Restart ECS Service
+ id: redeploy-service
+ env:
+ SERVICE_NAME: ${{env.AWS_APP_NAME}}-${{ github.event.inputs.env }} # ECS service name
+ run: |
+ aws ecs update-service --force-new-deployment --service $SERVICE_NAME --cluster $AWS_SHARED_CLUSTER # Triggers ECS redeploy
+```
+
+## Repository Checkout and Working Directory
+
+When this workflow runs, it uses the `actions/checkout@v3` action to clone the entire repository. The initial working directory for all steps is the root of the repository.
+
+Before building the Docker image, the workflow explicitly changes into the `client` directory using:
+
+```bash
+cd ./${{ env.DOCKER_PATH }}
+```
+
+This means that for the Docker build step, the working directory is `client/`, and the Dockerfile path `Dockerfile.prod` refers to `client/Dockerfile.prod`.
+
+**Summary:**
+
+- The workflow clones the entire repository.
+- The working directory starts at the repo root.
+- The workflow changes into the `client` directory before building the Docker image.
+- The Docker build context and Dockerfile are both relative to the `client` directory.
+
+## Summary
+
+This workflow provides a manual, environment-aware deployment pipeline for the frontend application, leveraging Docker, Amazon ECR, and ECS. It ensures that only the specified code reference is built and deployed, and that deployments are traceable and auditable via GitHub Actions.
diff --git a/.github/workflows/aws-frontend-deploy.yml b/.github/workflows/aws-frontend-deploy.yml
index 9b2b2bbe1..7fc27d4f7 100644
--- a/.github/workflows/aws-frontend-deploy.yml
+++ b/.github/workflows/aws-frontend-deploy.yml
@@ -1,113 +1,118 @@
name: Frontend Build and Deploy
on:
- workflow_dispatch:
+ workflow_dispatch: # Manual trigger from GitHub Actions UI
inputs:
env:
type: choice
- description: 'AWS Incubator Env'
- options:
- - dev
- - prod
+ description: "AWS Incubator Env"
+ options: # Selectable environment options
+ - dev
+ - prod
ref:
- description: 'Branch, Tag, or SHA'
+ description: "Branch, Tag, or SHA" # Code reference to deploy
required: true
env:
+ # Target ECS cluster name
AWS_SHARED_CLUSTER: incubator-prod
- AWS_APP_NAME: vrms-client
+ # Application name for tagging and service
+ AWS_APP_NAME: vrms-frontend
+ # AWS region for deployment
AWS_REGION: us-west-2
- DOCKERFILE: client/Dockerfile.prod
+ # Dockerfile used for build (located in client/)
+ DOCKERFILE: Dockerfile.prod
+ # Path to frontend source and Dockerfile
DOCKER_PATH: client
jobs:
setup_env:
- name: Set-up environment
+ name: Set-up environment
runs-on: ubuntu-latest
steps:
- - name: Debug Action
- uses: hmarr/debug-action@v2
- - name: Checkout
- uses: actions/checkout@v3
- with:
- ref: ${{ github.event.inputs.ref }}
- - name: Set AWS Env & Image Tag per workflow
- run: |
- SHORT_SHA=$(git rev-parse --short HEAD)
- if [[ "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]]; then
- INPUT_ENV=${{ github.event.inputs.env }}; INPUT_REF=${{ github.event.inputs.ref }}
- echo AWS_APPENV="$AWS_APP_NAME"-$INPUT_ENV >> $GITHUB_ENV
- echo IMAGE_TAG=$SHORT_SHA >> $GITHUB_ENV
- fi
+ - name: Debug Action
+ uses: hmarr/debug-action@v2 # Prints debug info to logs
+ - name: Checkout
+ uses: actions/checkout@v3 # Checks out code at specified ref
+ with:
+ ref: ${{ github.event.inputs.ref }} # Uses user-specified ref
+ # Get short SHA of current commit
+ # Only run if triggered manually
+ # Get environment input from workflow dispatch
+ # Get ref input from workflow dispatch
+ # Set AWS_APPENV for later steps
+ # Set IMAGE_TAG for later steps
+ - name: Set AWS Env & Image Tag per workflow
+ run: |
+ SHORT_SHA=$(git rev-parse --short HEAD)
+ if [[ "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]]; then
+ INPUT_ENV=${{ github.event.inputs.env }}; INPUT_REF=${{ github.event.inputs.ref }}
+ echo AWS_APPENV="$AWS_APP_NAME"-$INPUT_ENV >> $GITHUB_ENV
+ echo IMAGE_TAG=$SHORT_SHA >> $GITHUB_ENV
+ fi
outputs:
AWS_APPENV: ${{ env.AWS_APPENV }}
IMAGE_TAG: ${{ env.IMAGE_TAG }}
build:
name: Build & Push Docker Image
runs-on: ubuntu-latest
- needs: [setup_env]
+ permissions:
+ id-token: write # Needed for OIDC authentication to AWS
+ needs: [setup_env] # Waits for environment setup
steps:
- - name: Checkout
- uses: actions/checkout@v3
- with:
- ref: ${{ github.event.inputs.ref }}
- - name: Configure AWS credentials
- uses: aws-actions/configure-aws-credentials@v2
- with:
- aws-access-key-id: ${{ secrets.INCUBATOR_AWS_ACCESS_KEY_ID }}
- aws-secret-access-key: ${{ secrets.INCUBATOR_AWS_SECRET_ACCESS_KEY }}
- aws-region: ${{ env.AWS_REGION }}
- - name: Login to Amazon ECR
- id: login-ecr
- uses: aws-actions/amazon-ecr-login@v1
- - name: Init Docker Cache
- uses: jpribyl/action-docker-layer-caching@v0.1.0
- with:
- key: ${{ github.workflow }}-2-{hash}
- restore-keys: |
- ${{ github.workflow }}-2-
- - name: Build & Push Image to ECR
- uses: kciter/aws-ecr-action@v3
- with:
- access_key_id: ${{ secrets.INCUBATOR_AWS_ACCESS_KEY_ID }}
- secret_access_key: ${{ secrets.INCUBATOR_AWS_SECRET_ACCESS_KEY }}
- account_id: ${{ secrets.INCUBATOR_AWS_ACCOUNT_ID }}
- repo: ${{ needs.setup_env.outputs.AWS_APPENV }}
- region: ${{ env.AWS_REGION }}
- tags: latest,${{ needs.setup_env.outputs.IMAGE_TAG }}
- dockerfile: ${{ env.DOCKERFILE }}
- path: ${{ env.DOCKER_PATH }}
+ - name: Checkout
+ uses: actions/checkout@v3 # Checks out code at specified ref
+ with:
+ ref: ${{ github.event.inputs.ref }}
+ - name: Setup Node.js
+ uses: actions/setup-node@v3 # Sets up Node.js for build
+ with:
+ node-version: 18 # Uses Node.js v18
+ cache: "npm" # Enables npm caching
+ - name: Configure AWS credentials
+ uses: aws-actions/configure-aws-credentials@v3 # Sets AWS credentials for CLI
+ with:
+ role-to-assume: arn:aws:iam::035866691871:role/incubator-cicd-vrms # IAM role for deploy
+ role-session-name: incubator-cicd-vrms-gha # Session name for audit
+ aws-region: us-west-2 # AWS region
+ - name: Login to Amazon ECR
+ id: login-ecr
+ uses: aws-actions/amazon-ecr-login@v1 # Authenticates Docker to ECR
+ - name: Build, tag, and push the image to Amazon ECR
+ id: build-push-image
+ env:
+ ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} # ECR registry URL
+ ECR_REPOSITORY: ${{ env.AWS_APP_NAME }} # ECR repo name
+ # List files for debug
+ # Enter frontend directory for Docker build context
+ # Build Docker image using production Dockerfile
+ # Tag image with short SHA
+ # Tag image with environment (dev/prod)
+ # Use current directory as build context
+ # Push all tags for this image to ECR
+ run: |
+ ls
+ cd ./${{ env.DOCKER_PATH }}
+ docker build \
+ -f ${{ env.DOCKERFILE }} \
+ -t ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ needs.setup_env.outputs.IMAGE_TAG }} \
+ -t ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ github.event.inputs.env }} \
+ .
+ docker image push --all-tags ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}
deploy:
name: Deploy to AWS ECS
runs-on: ubuntu-latest
- needs: [setup_env, build]
+ needs: [setup_env, build] # Waits for setup and build jobs
+ permissions:
+ id-token: write # Needed for OIDC authentication to AWS
steps:
- - name: Configure AWS credentials
- uses: aws-actions/configure-aws-credentials@v2
- with:
- aws-access-key-id: ${{ secrets.INCUBATOR_AWS_ACCESS_KEY_ID }}
- aws-secret-access-key: ${{ secrets.INCUBATOR_AWS_SECRET_ACCESS_KEY }}
- aws-region: ${{ env.AWS_REGION }}
- - name: Login to Amazon ECR
- id: login-ecr
- uses: aws-actions/amazon-ecr-login@v1
- - name: Pull Task Definition & write to file
- id: aws-task-definition
- run: |
- aws ecs describe-task-definition \
- --task-definition ${{ needs.setup_env.outputs.AWS_APPENV }} \
- --query taskDefinition | \
- jq 'del(.taskDefinitionArn,.revision,.status,.registeredBy,.registeredAt,.compatibilities,.requiresAttributes)' > task-def.json
- - name: Interpolate new Docker Image into Task Definition
- id: task-definition
- uses: aws-actions/amazon-ecs-render-task-definition@v1
- with:
- task-definition: task-def.json
- container-name: ${{ needs.setup_env.outputs.AWS_APPENV }}
- image: ${{ steps.login-ecr.outputs.registry }}/${{ needs.setup_env.outputs.AWS_APPENV }}:${{ needs.setup_env.outputs.IMAGE_TAG }}
- - name: Deploy Amazon ECS
- uses: aws-actions/amazon-ecs-deploy-task-definition@v1
- with:
- task-definition: ${{ steps.task-definition.outputs.task-definition }}
- service: ${{ needs.setup_env.outputs.AWS_APPENV }}
- cluster: ${{ env.AWS_SHARED_CLUSTER }}
- wait-for-service-stability: true
- wait-for-minutes: 5 minutes
-
+ - name: Configure AWS credentials
+ uses: aws-actions/configure-aws-credentials@v3 # Sets AWS credentials for CLI
+ with:
+ role-to-assume: arn:aws:iam::035866691871:role/incubator-cicd-vrms # IAM role for deploy
+ role-session-name: incubator-cicd-vrms-gha # Session name for audit
+ aws-region: us-west-2 # AWS region
+ - name: Restart ECS Service
+ id: redeploy-service
+ env:
+ SERVICE_NAME: ${{env.AWS_APP_NAME}}-${{ github.event.inputs.env }} # ECS service name
+ # Force a new deployment of the ECS service to use the latest Docker image
+ run: |
+ aws ecs update-service --force-new-deployment --service $SERVICE_NAME --cluster $AWS_SHARED_CLUSTER
diff --git a/.github/workflows/pr-instructions.yml b/.github/workflows/pr-instructions.yml
index 43045d995..8f94abd3e 100644
--- a/.github/workflows/pr-instructions.yml
+++ b/.github/workflows/pr-instructions.yml
@@ -9,7 +9,7 @@ jobs:
Add-Pull-Request-Instructions:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
# Create the message to post
- name: Create Instruction
@@ -28,7 +28,7 @@ jobs:
echo ${{ steps.instruction.outputs.result }} > addingPrInstructions/artifact/artifact.txt
- name: Upload Artifacts
- uses: actions/upload-artifact@v2
+ uses: actions/upload-artifact@v4
with:
name: adding-pr-instructions-artifact
path: addingPrInstructions/artifact/
diff --git a/.github/workflows/waiting-to-merge.yaml b/.github/workflows/waiting-to-merge.yaml
new file mode 100644
index 000000000..9b41dc9ac
--- /dev/null
+++ b/.github/workflows/waiting-to-merge.yaml
@@ -0,0 +1,17 @@
+name: Waiting to Merge
+
+on:
+ pull_request:
+ types: [synchronize, opened, reopened, labeled, unlabeled]
+
+jobs:
+ do-not-merge:
+ if: ${{ contains(github.event.*.labels.*.name, 'waiting to merge') }}
+ name: Prevent Merging
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check for label
+ run: |
+ echo "Pull request is labeled as 'waiting to merge'"
+ echo "This workflow fails so that the pull request cannot be merged"
+ exit 1
diff --git a/.github/workflows/wr-pr-instructions.yml b/.github/workflows/wr-pr-instructions.yml
index 44c34bbdc..065978559 100644
--- a/.github/workflows/wr-pr-instructions.yml
+++ b/.github/workflows/wr-pr-instructions.yml
@@ -44,7 +44,7 @@ jobs:
const artifactJSON = JSON.parse(artifact);
return artifactJSON
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v4
# Create the message to post
- name: Post Comment
uses: actions/github-script@v4
diff --git a/.gitignore b/.gitignore
index 514725cad..1df37ea67 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,8 @@
+# vscode dirs
+.devcontainer
+.vscode
+# project dirs and files
node_modules/
npm-debug.log
.DS_Store
diff --git a/.husky/.gitignore b/.husky/.gitignore
new file mode 100644
index 000000000..31354ec13
--- /dev/null
+++ b/.husky/.gitignore
@@ -0,0 +1 @@
+_
diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100644
index 000000000..2312dc587
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1 @@
+npx lint-staged
diff --git a/.lintstagedrc.json b/.lintstagedrc.json
new file mode 100644
index 000000000..59f09ef13
--- /dev/null
+++ b/.lintstagedrc.json
@@ -0,0 +1,5 @@
+{
+ "*.{js,jsx,ts,tsx,json,css,md}": [
+ "sh -c 'biome check --write --no-errors-on-unmatched \"$@\" || true' --"
+ ]
+}
diff --git a/.nvmrc b/.nvmrc
new file mode 100644
index 000000000..a45fd52cc
--- /dev/null
+++ b/.nvmrc
@@ -0,0 +1 @@
+24
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 000000000..7bfb475c1
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1 @@
+VRMS is governed by the [Hack for LA Code of Conduct](https://www.hackforla.org/code-of-conduct/) which applies to any interaction on our VRMS slack channel (inside the HackforLA Slack workspace), direct slack messages, github org or repository, or any other communication medium.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 25bcc4fe7..82878d447 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -3,7 +3,6 @@
This document outlines the process for joining our team and contributing to the VRMS Github repository. If you notice errors or have important information to add, please feel free to propose changes to this document with a pull request
-
Table of Contents
- [**Part 1 : How to join the team**](#part-1--how-to-join-the-team)
@@ -23,16 +22,18 @@
- [**Part 4: How to create pull requests**](#part-4-how-to-create-pull-requests)
- [**4.1 Push changes to your forked repository**](#41-push-changes-to-your-forked-repository)
- [**4.2 Create a pull request on the VRMS repository**](#42-create-a-pull-request-on-the-vrms-repository)
-
+- [**Part 5: How to review pull requests**](#part-5-how-to-review-pull-requests)
## **Part 1 : How to join the team**
### **1.1 VRMS contributor expectations**
+
- Attend at least 1 team meeting per week
- Devote a minimum of 6 hours per week to working on VRMS assignments
- Communicate with the team leadership if you plan to step away from the project
### **1.2 Reach out to us on Slack**
+
If you would like to contribute to our project, please reach out to the team leads on Slack or at one of our weekly meetings. You can find the current project team, their slack links, and our team meeting times on the [VRMS Project Details Page](https://www.hackforla.org/projects/vrms).
### **1.3 Become a member of the repository Team**
@@ -45,58 +46,81 @@ Once you have accepted the GitHub invite (via email or in your GitHub notificati
1. Setup two factor authentication on your account https://github.com/hackforla/governance/issues/20
-These steps are manditory in order to contribute to all HackforLA projects.
+These steps are mandatory in order to contribute to all HackforLA projects.
## **Part 2: How to set up the development environment**
### **2.1 Fork the repository**
-In https://github.com/hackforla/VRMS, look for the fork icon in the top right. Click it and create a fork of the repository.
+_A fork is a copy of the repository that will be placed on your GitHub account url._
-For git beginners, a fork is a copy of the repository that will be placed on your GitHub account url.
+- In https://github.com/hackforla/VRMS, look for the fork icon in the top right. Click it and create a fork of the repository.
-It should create a copy here: https://github.com/your_GitHub_user_name/vrms, where `your_GitHub_user_name` is replaced with exactly that.
+- It should create a copy here: https://github.com/YOUR_GITHUB_USERNAME/vrms, where `YOUR_GITHUB_USERNAME` is replaced with your github username.
-Note that this copy is on a remote server on the GitHub website and not on your computer yet.
+> NOTE: This copy is on a remote server on the GitHub website and not on your computer yet.
-If you click the icon again, it will not create a new fork but instead give you the URL associated with your fork.
+- Click the icon again, it will give you the URL associated with your forked repository and not create a new fork.
### **2.2 Clone the remote repository to your local computer**
The following process will make a copy of the fork that you just created on your local computer.
-First create a new folder on your local computer that will contain `hackforla` projects.
+1. Create a new folder on your local computer that will contain `hackforla` projects.
-In your shell, navigate there then run the following commands:
+2. In your shell (terminal), navigate to this folder then run the following commands:
-```bash
-git clone https://github.com/your_GitHub_user_name/vrms.git
-```
+ ```bash
+ git clone https://github.com/YOUR_GITHUB_USERNAME/vrms.git
+ ```
-You should now have a new folder in your `hackforla` folder called `vrms`.
+ You should now have a new folder in your `hackforla` folder called `vrms`.
-Verify which URL your `origin` remote is pointing to:
+3. Verify which URL your `origin` remote is pointing to:
-```bash
-git remote show origin
-```
+ ```bash
+ git remote show origin
+ ```
-If you accidentally cloned the `hackforla/vrms.git` then you can change your local copy to upload to your fork with the following:
+ Your terminal should return:
-```bash
-git remote set-url origin https://github.com/your_user_name/vrms.git
-```
+ ```bash
+ remote origin
+ Fetch URL: https://github.com/YOUR_GITHUB_USERNAME/vrms.git
+ Push URL: https://github.com/YOUR_GITHUB_USERNAME/vrms.git
+ ...
+ ```
-Add another remote called `vrms` that points to the `hackforla` version of the repository. This will allow you to incorporate changes later:
+ If you accidentally cloned the `hackforla/vrms.git` then you can change your local copy to upload to your fork with the following:
-```bash
-git remote add vrms https://github.com/hackforla/vrms.git
-```
+ ```bash
+ git remote set-url origin https://github.com/YOUR_GITHUB_USERNAME/vrms.git
+ ```
+
+4. Add another remote called `vrms` that points to the `hackforla` version of the repository. This will allow you to incorporate changes later:
+ ```bash
+ git remote add vrms https://github.com/hackforla/vrms.git
+ ```
+
+Note: Understanding how git remotes work will make collaborating much easier. You can learn more about remotes [here](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/configuring-a-remote-for-a-fork) and [here](https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes).
-Note: Understanding how git remotes work will make collaborating much easier. You can learn more about remotes [here](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/configuring-a-remote-for-a-fork) and [here](https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes)
+### **2.3 Get up and running**
+1. Install [NVM (Node Version Manager)](https://github.com/nvm-sh/nvm). NVM allows you to easily manage and switch between multiple versions of Node.
-### **2.3 Get up and running**
+ - Verify the installation: `nvm --version`
+ - Once NVM is verified, run the following commands from the root of the project:
+
+ ```
+ # Install the project's Node version (specified in the .nvmrc file)
+ nvm install
+
+ # Instruct NVM to use the Node version defined in the .nvmrc file
+ nvm use
+
+ ```
+
+ > NOTE: If the major version of Node does not match the version specified in the .nvmrc file, you may need to be explicit with the version and use: `nvm install ` and `nvm use `
1. Have [Node](https://nodejs.org/en/download/) and NPM installed locally:
@@ -114,22 +138,33 @@ Note: Understanding how git remotes work will make collaborating much easier. Yo
- `cd vrms/` and run `yarn install`
- `cd client` and run `yarn install`
- - `cd client-mvp-04` and run `yarn install`
- `cd ../backend` and run `yarn install`
1. Add your required environment variables for the frontend and backend directories:
- `touch vrms/backend/.env`
- `touch vrms/client/.env`
- - `touch vrms/client-mvp-04/.env`
- Note 1: In the above example you are trying to create an empty file called `.env` in each of the listed directories: backend, client and client-mvp-04. You can use either `touch .env` or navigate to the directory and use `touch .env`
+ Note 1: In the above example you are trying to create an empty file called `.env` in each of the listed directories: backend and client. You can use either `touch .env` or navigate to the directory and use `touch .env`
Note 2: `touch` is a Unix/Linux or Mac command; It is not available in Windows. In Windows, use a text editor (e.g. Notepad) to create an empty file and save it in each of the locations as `.env` . (If you use Windows Explorer to create the file it will create a file called `.env.txt`, which will not work.)
- - Then paste the content from the [document](https://docs.google.com/document/d/1yDF6UmyO-MPNrl3y_Mw0mkm_WaixlSkXzWbudCzHXDY/edit?usp=sharing). It is accessible for the project team members only.
+ - Then paste the content from the [document](https://docs.google.com/document/d/1PdcZhyo2a2lr0JNcgyzpWi98tGkZeH1Zxz-Jnf6JQDU/edit?usp=sharing). It is accessible for the project team members only.
+
- _Please note that the `ports` for the frontend and backend are set in this location_
+1. Set up Husky for Git hooks (required for all contributors):
+
+ To help enforce code quality and prevent errors from being committed, we use [Husky](https://typicode.github.io/husky/) to manage Git hooks. Husky should install itself automatically after you install dependencies (thanks to the `prepare` script in `package.json`).
+
+ If you notice that Git hooks are not working (for example, you don't see linting or formatting checks when committing), you may need to set up Husky manually. To do this, run the following in the root of the project:
+
+ ```sh
+ npx husky install
+ ```
+
+ If you encounter issues, see the [Husky documentation](https://typicode.github.io/husky/#/) or reach out on Slack!
+
1. Take a second to review the `app.js` and `server.js` files in the `vrms/backend` folder. These two files are a blueprint for the back end, so please familiarize yourself with it. You'll see folders for the database collection models, routes for the API, and a config file which loads the necessary environment variables.
1. Start the local development servers (frontend & backend).
@@ -138,16 +173,15 @@ Note: Understanding how git remotes work will make collaborating much easier. Yo
- Navigate to the root of the application `vrms/` and run `yarn start`
- *Troubleshooting :* If you encounter the following error after running `yarn start`:
-
- ```
- Error: error:0308010C:digital envelope routines::unsupported
- ```
- Try changing your node version to `16.14.2` by running `nvm use 16.14.2`. If you do not have `nvm` installed, see [install instructions](https://github.com/nvm-sh/nvm#installing-and-updating)
+ _Troubleshooting :_ If you encounter the following error after running `yarn start`:
+ ```
+ Error: error:0308010C:digital envelope routines::unsupported
+ ```
-You should now have a live app. Happy hacking.
+ Try changing your node version to `16.14.2` by running `nvm use 16.14.2`. If you do not have `nvm` installed, see [install instructions](https://github.com/nvm-sh/nvm#installing-and-updating)
+You should now have a live app. Happy hacking.
### **2.4 Running Tests**
@@ -158,76 +192,164 @@ To run all of the tests run `npm run test:all` from the root folder.
### **2.5 Using the development database**
-The application uses MongoDB. We have created a shared development database using MongoDB Cloud and MongoDB Atlas. The conection string for the development database is included in the environmental variables that you pasted into your backend/.env file in step 5 of the "Get Up and Running" setion. If you completed that step successfully you should not need to do anything else.
+The application uses MongoDB. We have created a shared development database using MongoDB Cloud and MongoDB Atlas. The connection string for the development database is included in the environmental variables that you pasted into your backend/.env file in step 5 of the "Get Up and Running" section. If you completed that step successfully you should not need to do anything else.
To view and edit the development database manually, you can download [MongoDB Compass](https://www.mongodb.com/try/download/compass). To connect to the development database you will use the "DATABASE_URL" from the [document](https://docs.google.com/document/d/1yDF6UmyO-MPNrl3y_Mw0mkm_WaixlSkXzWbudCzHXDY/edit?usp=sharing) that contained the environmental variables. The string will start with "mongodb+srv://".
If you want to install a local copy to experiment with and learn more about MongoDB, you can use [this tutorial](https://zellwk.com/blog/local-mongodb/)
-
-
## **Part 3: How to work on issues**
-
### **3.1 Claim an Issue**
-Developers may assign themselves to issues from the [Prioritized Backlog column](https://github.com/hackforla/VRMS/projects/12#column-19074778) of the project board.
+Developers may assign themselves to issues from the [Prioritized Backlog column](https://github.com/orgs/hackforla/projects/72/views/1?filterQuery=backlog) of the project board.
The Prioritized Backlog column is filtered so the first (top) issue has the highest priority and should be worked on next if possible.
Developers may choose from issues with the following `role` labels:
+
- `role: Front End`
- `role: Back End`
- `role: Database`
+- `role: devops`
+- _Lead developers may choose from the above labels, as well as issues with the label:_ `role: Dev Lead`
Claiming an issue is a two step process:
+
1. Assign yourself to the issue using the gear icon in the upper right corner of the issue where it says "Assignees"
2. Move the issue from the `Prioritized Backlog` to the `In Progress` column of the project board
+You may want to consider bookmarking your Github Issues page to track your current tasks:
+[https://github.com/issues/assigned](https://github.com/issues/assigned)
### **3.2 Create a new branch for each issue you work on**
-Create a new branch for each issue you work on. Doing all your work on feature branches leaves your repository's main branch unmodified and greatly simplifies keeping your fork in sync with the main project.
+You will create a new branch for each issue you work on. Doing all your work on feature branches leaves your repository's main branch unmodified and greatly simplifies keeping your fork in sync with the main project.
-Before creating a new branch, always make sure you are currently on the `development` branch by using the command
-```
-git branch
-```
-Before creating a new branch, always pull down the latest changes from the `development` branch by using the command
-```
-git pull vrms development
-```
-Finally, create a new branch where you will work on your issue by using the command
-```
-git checkout -b your-branch-name
-```
+1. Before creating a new branch, always make sure you are currently on the `development` branch by using the command
+ ```bash
+ git branch
+ ```
+2. Before creating a new branch, always pull down the latest changes from the `development` branch by using the command
+ ```bash
+ git pull vrms development
+ ```
+3. Finally, create a new branch where you will work on your issue by using the command:
+ ```bash
+ git checkout -b your-branch-name
+ ```
+
+### **3.2.1 Check for and resolve linting/prettier errors before making changes**
+
+Before you begin working on any part of a file, always check for existing code errors in the codebase. This helps prevent introducing new issues and ensures a stable foundation for your work.
+
+- Use your code editor's error checking tools or run the appropriate linting/compilation commands (e.g., `yarn lint (filename)`, or your IDE's error panel) to identify any errors in the files you plan to edit. (This isn't your fault, it's just an old codebase 🤷️)
+- If you find errors, resolve them in a **separate commit** before starting your feature or fix work. Use a clear commit message such as:
+
+> fix: Resolve existing linting and compilation errors
+
+(This makes it easier for reviewers to distinguish between error fixes and your new changes.)
+
+- If you are unsure how to fix an error, ask for help in the team Slack channel or consult the documentation.
+- Only begin implementing new features or fixes after confirming the file is error-free.
+- If you are unable to resolve the errors after making a reasonable effort, it is acceptable to use `--no-verify` when committing or pushing your changes. Please leave a comment in your pull request explaining why this was necessary.
### **3.3 Work on the Issue**
-Every issue will contain action items you must complete before you are ready to submit a pull request. Be sure to use the checkboxes as you complete each action item so we can track your progress!
+Every issue will contain action items you must complete before you are ready to submit a pull request. Be sure to use the checkboxes as you complete each action item so we can track your progress!
After you have completed the action items, add and commit the changes to your new branch using the commands
+
```
git add .
git commit -m "your commit message"
-```
-
+```
## **Part 4: How to create pull requests**
+
### **4.1 Push changes to your forked repository**
-Before pushing code, always pull down the latest changes from the `development` branch by using the command
-```
-git pull vrms development
-```
-Once you are satisfied with your changes, push them to the feature branch you made within your remote repository.
-```
-git push --set-upstream origin your-branch-name
-```
+
+1. Before pushing code, always pull down the latest changes from the `development` branch by using the command
+ ```
+ git pull vrms development
+ ```
+2. Once you are satisfied with your changes, push them to the feature branch you made within your remote repository.
+ ```
+ git push --set-upstream origin your-branch-name
+ ```
+
### **4.2 Create a pull request on the VRMS repository**
+
1. Go to your fork of the VRMS repository on GitHub and click on the `Compare & pull request` button. See screenshot
2. Be sure to title your pull request by summarizing the changes you made
3. Be sure to add your issue number where the template says `Fixes #replace_this_text_with_the_issue_number`
4. Fill out the "What changes did you make and why?" section of the pull request template
5. Include before & after images with your pull request if there are visual changes to the user interface
6. Request a review from another developer on the team
-7. Review another developers pull request while you are waiting for your pull request to be reviewed
\ No newline at end of file
+7. Review another developer's pull request while you are waiting for your pull request to be reviewed
+
+## **Part 5: How to review pull requests**
+
+Reviewing pull requests is an important part of maintaining code quality and helping team members improve their contributions. Here’s how to review a pull request on the VRMS repository:
+
+1. **Navigate to the Pull Requests tab**
+ Go to the [Pull Requests](https://github.com/hackforla/VRMS/pulls) section of the repository to see open pull requests.
+
+2. **Select a pull request to review**
+ Choose a pull request that is ready for review (look for those assigned to you or marked as "Ready for review").
+
+3. **Read the pull request description**
+ Review the summary, linked issue(s), and any screenshots or documentation provided by the author.
+
+4. **Check the code changes**
+
+ - Click on the "Files changed" tab to see the code diff.
+ - Look for code quality, readability, and adherence to project conventions.
+ - Ensure the code addresses the issue and does not introduce bugs.
+
+5. **Run the code locally (optional but recommended)**
+
+ - Pull the branch to your local machine.
+ - Follow the setup instructions to test the changes.
+ - Run tests to verify nothing is broken.
+
+6. **Leave feedback**
+
+ - Use GitHub’s review tools to comment on specific lines or leave general feedback.
+ - Be constructive and respectful in your comments.
+
+7. **Approve or request changes**
+
+ - If the pull request is ready, click "Approve".
+ - If changes are needed, click "Request changes" and specify what needs to be addressed.
+
+8. **Merge the pull request (if authorized)**
+ - If you have permission and the pull request meets all requirements, you may merge it.
+ - Otherwise, notify the author or a maintainer that it is ready to merge.
+
+**Tip:** You can use the following helpful git alias to quickly check out a pull request locally:
+
+```sh
+# This git alias allows you to quickly check out a pull request by its number.
+# eg "git pr 1820 vrms"
+git config alias.pr '!f() { \
+ # If no arguments are provided, print usage instructions
+ if [ $# -lt 1 ]; then \
+ echo "Usage: git pr [] # assuming [=origin] is on GitHub"; \
+ echo " eg: git pr 1340 upstream"; \
+ else \
+ # Save current HEAD (optional, for safety)
+ git checkout -q "$(git rev-parse --verify HEAD)" && \
+ # Fetch the pull request from the specified remote (default: origin) into a local branch
+ git fetch -fv "${2:-origin}" pull/"$1"/head:pr/"$1" && \
+ # Check out the fetched PR branch
+ git checkout pr/"$1"; \
+ fi; \
+}; f'
+```
+
+This allows you to run `git pr ` to fetch and check out a pull request by its number. For example: `git pr 1234 upstream`.
+
+**Resources:**
+
+- [GitHub: Reviewing proposed changes in a pull request](https://docs.github.com/en/github/collaborating-with-pull-requests/reviewing-changes-in-pull-requests)
diff --git a/README.md b/README.md
index c88dd982a..b7321200d 100644
--- a/README.md
+++ b/README.md
@@ -1,26 +1,164 @@
+
+
+
+
# Volunteer Relationship Management System
-Live Site: [https://vrms.io](https://vrms.io)
+
+
+
+
+
+
+[![GitHub commit activity][commit-activity-shield]][commit-activity-url]
+[![Contributors][contributors-shield]][contributors-url]
+[![Forks][forks-shield]][forks-url]
+[![Stargazers][stars-shield]][stars-url]
+[![Issues][issues-shield]][issues-url]
+[![MIT License][license-shield]][license-url]
VRMS is a tool used for the engagement, support, and retention of a network of volunteers.
This is an ambitious project to create a system that will help us measure our human capital development, reduce repetitive tasks and processes for leadership, and improve outcomes for both volunteers and the projects they contribute to.
+
+
+ Table of Contents
+
+
+ About The Project
+
+
+ Contributing
+ Wiki Highlights
+
+ Roadmap
+ License
+ Acknowledgments
+
+
+
+
+
+## About The Project
+
+VRMS is a tool originally developed to track membership attendance of meetings and evets. It's built in-house, deployed with an express backend and React frontend on AWS, and uses service workers to manage event creation and opening/closing of events.
+
+(back to top )
+
+### Built With
+
+
+
+
+
+[![React][react-shield]][React-url]
+[![AWS][aws-shield]][aws-url]
+[![GitHub Actions][github-actions-shield]][github-actions-url]
+[![Yarn][yarn-shield]][yarn-url]
+[![Vite][vite-shield]][vite-url]
+[![Docker][docker-shield]][docker-url]
+
+
+
+
+(back to top )
+
+## Contributing
+
+Contributions are welcome once you've been onboarded to Hack for LA through an onboarding event:
-## How to Contribute
-We require all code contributors to
-1. Join our organization by going through [Hack for LA onboarding](https://www.hackforla.org/getting-started). It's free to join!
-2. Read our [CONTRIBUTING.md](https://github.com/hackforla/VRMS/blob/development/CONTRIBUTING.md) document.
+1. Join our organization by going through [Hack for LA onboarding](https://www.hackforla.org/getting-started)
+ - Joining is free, and opens up access to contribute to many projects, including VRMS
+2. From there, read our [CONTRIBUTING.md](https://github.com/hackforla/VRMS/blob/development/CONTRIBUTING.md) document to get started!
+
+(back to top )
## Wiki Highlights
+
1. [Introduction](https://github.com/hackforla/VRMS/wiki/Introduction)
2. [Technology Stack](https://github.com/hackforla/VRMS/wiki/Technology)
-3. [Team Meetigns](https://github.com/hackforla/VRMS/wiki/Team-Meetings)
+3. [Team Meetings](https://github.com/hackforla/VRMS/wiki/Team-Meetings)
+
+(back to top )
+
+
+
+## Roadmap
-## We Love Our Stars ⭐⭐⭐
+
-Thanks to the following people who have given us a star on our repo:
-[](https://github.com/hackforla/vrms/stargazers)
+See the [open issues](https://github.com/hackforla/VRMS/issues) and [VRMS project board](https://github.com/orgs/hackforla/projects/72) for a full list of proposed features (and known issues).
-### Licensing
+(back to top )
+
+## License
[AGPL-3.0 License](https://github.com/hackforla/VRMS/blob/development/LICENSE)
+
+(back to top )
+
+
+
+
+
+
+
+
+
+
+## Acknowledgments
+
+While GitHub provides a lot of recognition of the code commits that exist within this project, often the contributions of the organizational structure go without credit. We'd like to thank all the designers, project managers, usability testers, and data scientists that have touched this project over the years.
+
+Thanks for all your hard work!
+
+(back to top )
+
+
+
+
+[commit-activity-url]: https://github.com/hackforla/VRMS/commits/development/
+[commit-activity-shield]: https://img.shields.io/github/commit-activity/m/hackforla/vrms?style=for-the-badge
+[contributors-shield]: https://img.shields.io/github/contributors/hackforla/VRMS.svg?style=for-the-badge
+[contributors-url]: https://github.com/hackforla/VRMS/graphs/contributors
+[forks-shield]: https://img.shields.io/github/forks/hackforla/VRMS.svg?style=for-the-badge
+[forks-url]: https://github.com/hackforla/VRMS/network/members
+[stars-shield]: https://img.shields.io/github/stars/hackforla/VRMS.svg?style=for-the-badge
+[stars-url]: https://github.com/hackforla/VRMS/stargazers
+[issues-shield]: https://img.shields.io/github/issues/hackforla/VRMS.svg?style=for-the-badge
+[issues-url]: https://github.com/hackforla/VRMS/issues
+[license-shield]: https://img.shields.io/github/license/hackforla/VRMS.svg?style=for-the-badge
+[license-url]: https://github.com/hackforla/VRMS/blob/main/LICENSE.txt
+[linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=for-the-badge&logo=linkedin&colorB=555
+[linkedin-url]: https://linkedin.com/in/trilliumsmith
+[product-screenshot]: images/screenshot.png
+[react-shield]: https://img.shields.io/badge/React-20232A?style=for-the-badge&logo=react&logoColor=61DAFB
+[React-url]: https://reactjs.org/
+[Site-url]: https://vrms.io
+
+
+[aws-shield]: https://img.shields.io/badge/AWS-%23FF9900.svg?style=for-the-badge&logo=amazon-aws&logoColor=white
+[aws-url]: https://aws.amazon.com/
+[github-actions-shield]: https://img.shields.io/badge/github%20actions-%232671E5.svg?style=for-the-badge&logo=githubactions&logoColor=white
+[github-actions-url]: https://docs.github.com/en/actions
+[yarn-shield]: https://img.shields.io/badge/yarn-%232C8EBB.svg?style=for-the-badge&logo=yarn&logoColor=white
+[yarn-url]: https://yarnpkg.com/
+[vite-shield]: https://img.shields.io/badge/vite-%23646CFF.svg?style=for-the-badge&logo=vite&logoColor=white
+[vite-url]: https://vitejs.dev/
+[docker-shield]: https://img.shields.io/badge/docker-%230db7ed.svg?style=for-the-badge&logo=docker&logoColor=white
+[docker-url]: https://www.docker.com/
\ No newline at end of file
diff --git a/VRMS b/VRMS
new file mode 160000
index 000000000..7042af0f4
--- /dev/null
+++ b/VRMS
@@ -0,0 +1 @@
+Subproject commit 7042af0f43f0b034977c7f64c613e9d8b7199edb
diff --git a/ai/verify-stale-issues/verify-stale-issue.sh b/ai/verify-stale-issues/verify-stale-issue.sh
new file mode 100755
index 000000000..99c5d242b
--- /dev/null
+++ b/ai/verify-stale-issues/verify-stale-issue.sh
@@ -0,0 +1,181 @@
+#!/bin/bash
+
+##############################################################################
+# VRMS Stale Issue Verification Skill
+#
+# Purpose: Automatically verify open stale issues (>1 year old) and determine
+# if they should be closed, kept open, or flagged for PM triage
+#
+# Usage: ./verify-stale-issue.sh
+# ./verify-stale-issue.sh 1163
+#
+# Output: JSON object with verdict, reasoning, and action steps
+##############################################################################
+
+set -e
+
+ISSUE_NUM="${1:-}"
+
+if [ -z "$ISSUE_NUM" ]; then
+ echo "Usage: $0 "
+ echo "Example: $0 1163"
+ exit 1
+fi
+
+# Helper function to calculate days since date
+days_since() {
+ local date_str="$1"
+ local date_epoch=$(date -j -f "%Y-%m-%d" "$date_str" "+%s" 2>/dev/null || date -d "$date_str" "+%s" 2>/dev/null || echo 0)
+ local now_epoch=$(date "+%s")
+ if [ "$date_epoch" -eq 0 ]; then
+ echo "unknown"
+ else
+ echo $(( (now_epoch - date_epoch) / 86400 ))
+ fi
+}
+
+# Fetch issue data
+ISSUE_DATA=$(gh issue view "$ISSUE_NUM" --json number,title,state,labels,createdAt,updatedAt,assignees,body,milestone 2>/dev/null)
+
+if [ -z "$ISSUE_DATA" ]; then
+ echo "{\"error\": \"Issue #$ISSUE_NUM not found\"}"
+ exit 1
+fi
+
+# Extract fields
+TITLE=$(echo "$ISSUE_DATA" | jq -r '.title')
+STATE=$(echo "$ISSUE_DATA" | jq -r '.state')
+CREATED=$(echo "$ISSUE_DATA" | jq -r '.createdAt' | cut -d'T' -f1)
+UPDATED=$(echo "$ISSUE_DATA" | jq -r '.updatedAt' | cut -d'T' -f1)
+ASSIGNEES=$(echo "$ISSUE_DATA" | jq -r '.assignees[].login' | tr '\n' ',' | sed 's/,$//')
+LABELS=$(echo "$ISSUE_DATA" | jq -r '.labels[].name' | tr '\n' '|')
+BODY=$(echo "$ISSUE_DATA" | jq -r '.body // ""')
+
+# Categorize by labels/metadata
+HAS_DRAFT=$(echo "$LABELS" | grep -q "draft" && echo "true" || echo "false")
+HAS_EPIC=$(echo "$LABELS" | grep -q "Epic\|epic" && echo "true" || echo "false")
+HAS_PM_READY=$(echo "$LABELS" | grep -q "ready for product manager" && echo "true" || echo "false")
+HAS_STAKEHOLDER=$(echo "$TITLE" | grep -q "Stakeholder\|stakeholder" && echo "true" || echo "false")
+HAS_MEETING=$(echo "$LABELS" | grep -q "Agenda\|agenda\|meeting" && echo "true" || echo "false")
+HAS_SECURITY=$(echo "$LABELS" | grep -q "security\|Security" && echo "true" || echo "false")
+HAS_BLOCKED=$(echo "$LABELS" | grep -q "blocked\|Blocked" && echo "true" || echo "false")
+
+DAYS_CREATED=$(days_since "$CREATED")
+DAYS_UPDATED=$(days_since "$UPDATED")
+
+# Decision tree logic
+VERDICT=""
+CATEGORY=""
+REASONING=""
+ACTION=""
+BLOCKER=""
+CONFIDENCE=""
+
+# 1. Check if recurring meeting
+if [ "$HAS_MEETING" = "true" ]; then
+ VERDICT="KEEP_OPEN"
+ CATEGORY="recurring_process"
+ REASONING="This is a recurring meeting agenda or status item"
+ ACTION="Keep open - this is a legitimate ongoing process"
+ CONFIDENCE="high"
+
+# 2. Check draft status
+elif [ "$HAS_DRAFT" = "true" ] && [ "$HAS_PM_READY" = "false" ]; then
+ VERDICT="FLAG_PM"
+ CATEGORY="draft_status"
+ BLOCKER="DRAFT - not ready for work"
+ REASONING="Issue is in draft status and not ready for prioritization"
+ ACTION="FLAG FOR PM: Review and decide if draft should be completed or closed"
+ CONFIDENCE="high"
+
+# 3. Check if PM decision needed
+elif [ "$HAS_PM_READY" = "true" ]; then
+ VERDICT="FLAG_PM"
+ CATEGORY="stakeholder_decision"
+ BLOCKER="STAKEHOLDER - needs PM approval"
+ REASONING="Issue requires PM/product manager review for prioritization or approval"
+ ACTION="FLAG FOR PM: Prioritize this issue or decide if it should be closed"
+ CONFIDENCE="high"
+
+# 4. Check if stakeholder feedback
+elif [ "$HAS_STAKEHOLDER" = "true" ]; then
+ VERDICT="FLAG_PM"
+ CATEGORY="stakeholder_decision"
+ BLOCKER="STAKEHOLDER - awaiting feedback incorporation"
+ REASONING="Issue involves stakeholder input/feedback that needs PM triage"
+ ACTION="FLAG FOR PM: Incorporate stakeholder feedback or decide on closure"
+ CONFIDENCE="high"
+
+# 5. Check if Epic
+elif [ "$HAS_EPIC" = "true" ]; then
+ VERDICT="FLAG_PM"
+ CATEGORY="epic_scope"
+ BLOCKER="EPIC - needs scope clarification"
+ REASONING="Epic issue requires scope review and active management"
+ ACTION="FLAG FOR PM: Clarify epic scope, status, and current priority"
+ CONFIDENCE="high"
+
+# 6. Check for security issues
+elif [ "$HAS_SECURITY" = "true" ]; then
+ VERDICT="VERIFY_CODE"
+ CATEGORY="security"
+ REASONING="Security issue needs verification of current status/fix"
+ ACTION="VERIFY: Check if security concern has been addressed in code"
+ CONFIDENCE="medium"
+
+# 7. Check if blocked
+elif [ "$HAS_BLOCKED" = "true" ]; then
+ VERDICT="FLAG_PM"
+ CATEGORY="blocked"
+ BLOCKER="BLOCKED - awaiting dependencies"
+ REASONING="Issue is blocked on other work"
+ ACTION="FLAG FOR PM: Review blocking issues and unblock if ready"
+ CONFIDENCE="high"
+
+# 8. Check if unassigned and very old
+elif [ -z "$ASSIGNEES" ] && [ "$DAYS_CREATED" -gt 900 ] && [ "$DAYS_UPDATED" -gt 180 ]; then
+ VERDICT="CLOSE"
+ CATEGORY="abandoned"
+ REASONING="Unassigned, created ${DAYS_CREATED}+ days ago, no updates in ${DAYS_UPDATED}+ days"
+ ACTION="CLOSE: Issue appears abandoned with no recent activity"
+ CONFIDENCE="high"
+
+# 9. Default: needs classification
+else
+ VERDICT="VERIFY_CODE"
+ CATEGORY="needs_classification"
+ REASONING="Insufficient data in labels/metadata - requires code verification or PM input"
+ ACTION="INVESTIGATE: Check code state and/or flag for PM if still relevant"
+ CONFIDENCE="medium"
+fi
+
+# Generate JSON output
+cat <&2
+ gh issue list --state all --limit 500 --json number,title,state,createdAt,updatedAt,labels | \
+ jq '[.[] | select(.createdAt < "2024-03-17") | select(.state == "OPEN")]' > issues.json
+fi
+
+TOTAL_ISSUES=$(jq 'length' issues.json)
+echo "Processing $TOTAL_ISSUES stale open issues..." >&2
+
+# Initialize report structure
+REPORT_JSON='{
+ "metadata": {
+ "generated_at": "'$(date -u +"%Y-%m-%dT%H:%M:%SZ")'",
+ "total_issues": '$TOTAL_ISSUES'
+ },
+ "verdicts": {
+ "CLOSE": [],
+ "KEEP_OPEN": [],
+ "FLAG_PM": [],
+ "VERIFY_CODE": []
+ },
+ "by_category": {},
+ "summary": {}
+}'
+
+# Track counts
+declare -A verdict_counts category_counts
+verdict_counts[CLOSE]=0
+verdict_counts[KEEP_OPEN]=0
+verdict_counts[FLAG_PM]=0
+verdict_counts[VERIFY_CODE]=0
+
+count=0
+# Process each issue
+jq -r '.[] | .number' issues.json | while read issue_num; do
+ count=$((count + 1))
+
+ # Run verification
+ result=$(bash "$SCRIPT_DIR/verify-stale-issue.sh" "$issue_num" 2>/dev/null)
+
+ verdict=$(echo "$result" | jq -r '.verdict')
+ category=$(echo "$result" | jq -r '.category')
+
+ # Add to report by verdict
+ REPORT_JSON=$(echo "$REPORT_JSON" | jq --argjson result "$result" \
+ ".verdicts[$verdict] += [\$result]")
+
+ # Track by category
+ REPORT_JSON=$(echo "$REPORT_JSON" | jq --arg cat "$category" --argjson result "$result" \
+ ".by_category[\$cat] //= [] | .by_category[\$cat] += [\$result]")
+
+ if [ $((count % 10)) -eq 0 ]; then
+ echo " Processed $count/$TOTAL_ISSUES..." >&2
+ fi
+done
+
+# Generate summary counts
+REPORT_JSON=$(echo "$REPORT_JSON" | jq \
+ '.summary = {
+ "close_count": (.verdicts.CLOSE | length),
+ "keep_open_count": (.verdicts.KEEP_OPEN | length),
+ "flag_pm_count": (.verdicts.FLAG_PM | length),
+ "verify_code_count": (.verdicts.VERIFY_CODE | length)
+ }')
+
+# Add action items
+REPORT_JSON=$(echo "$REPORT_JSON" | jq \
+ '.action_items = {
+ "immediate_closes": (.verdicts.CLOSE | map({issue: .issue, title: .title, action: .action})),
+ "pm_flags": (.verdicts.FLAG_PM | map({issue: .issue, title: .title, blocker: .blocker, action: .action}) | sort_by(.issue)),
+ "code_verifications": (.verdicts.VERIFY_CODE | map({issue: .issue, title: .title, action: .action}))
+ }')
+
+# Write output
+echo "$REPORT_JSON" | jq '.' > "$OUTPUT_FILE"
+
+echo "" >&2
+echo "✅ Report generated: $OUTPUT_FILE" >&2
+echo "" >&2
+jq '.summary' "$OUTPUT_FILE" >&2
diff --git a/ai/verify-stale-issues/verify_stale_issue.py b/ai/verify-stale-issues/verify_stale_issue.py
new file mode 100755
index 000000000..60ceea5fb
--- /dev/null
+++ b/ai/verify-stale-issues/verify_stale_issue.py
@@ -0,0 +1,686 @@
+#!/usr/bin/env python3
+
+"""
+VRMS Stale Issue Verification Skill
+
+Automatically verifies open stale issues (>1 year old) and determines if they should be:
+- CLOSE: Issue is complete or abandoned
+- KEEP_OPEN: Issue is legitimate ongoing work
+- FLAG_PM: Issue needs stakeholder/PM decision
+- VERIFY_CODE: Needs code inspection to determine status
+
+Usage:
+ python3 verify_stale_issue.py
+ python3 verify_stale_issue.py --batch issues.json --output report.json
+ python3 verify_stale_issue.py --list (shows all stale issues)
+"""
+
+import json
+import subprocess
+import sys
+import argparse
+from datetime import datetime, timedelta
+from typing import Dict, List, Any, Optional
+import re
+
+
+class StaleIssueVerifier:
+ """Verifies stale GitHub issues and provides action recommendations"""
+
+ # Code verification patterns
+ CODE_CHECKS = {
+ "form_validation": {
+ "patterns": [
+ "frontend/src/pages",
+ "frontend/src/components",
+ "client/src/pages",
+ "client/src/components",
+ ],
+ "keywords": ["validat", "required", "error", "FormValidation"],
+ },
+ "mui_styling": {
+ "patterns": ["@mui", "material-ui", "MUI"],
+ "keywords": ["Button", "TextField", "Box", "Container"],
+ },
+ "checkin": {
+ "patterns": ["CheckIn", "check-in", "checkIn"],
+ "keywords": ["attendance", "event", "status"],
+ },
+ "google_drive": {
+ "patterns": ["docs/", "README", "CONTRIBUTING"],
+ "keywords": ["google", "drive", "docs"],
+ },
+ "502_error": {
+ "patterns": ["deployment", "nginx", "server", "error"],
+ "keywords": ["502", "gateway", "timeout"],
+ },
+ }
+
+ # Decision tree weights and patterns
+ LABEL_PATTERNS = {
+ "meeting": ["agenda", "meeting", "standup", "sync"],
+ "draft": ["draft"],
+ "pm_decision": ["ready for product manager"],
+ "stakeholder": ["stakeholder", "feedback"],
+ "epic": ["epic", "overview"],
+ "security": ["security"],
+ "blocked": ["blocked", "blocking"],
+ "recurring": ["weekly", "monthly", "recurring"],
+ "bug": ["bug"],
+ "feature": ["feature", "enhancement"],
+ }
+
+ TITLE_PATTERNS = {
+ "stakeholder": r"stakeholder|feedback",
+ "meeting": r"agenda|meeting|standup|sync",
+ "recurring": r"weekly|monthly|recurring|audit",
+ }
+
+ # Dev-related roles that indicate code verification needed
+ DEV_ROLES = [
+ "role: database",
+ "role: back end",
+ "role: front end",
+ "role: devops",
+ "role: dev lead",
+ ]
+
+ # Non-dev roles that indicate docs/process verification
+ NON_DEV_ROLES = [
+ "role: product",
+ "role: ui/ux",
+ "role: design",
+ "role: branding",
+ "role: org rep",
+ "role: missing",
+ ]
+
+ # Issue type detection patterns - determines verification scope
+ ISSUE_TYPES = {
+ "documentation": {
+ "keywords": ["documentation", "docs", "readme", "wiki", "guide", "link", "url", "guide"],
+ "verification_method": "manual_review",
+ "description": "Documentation/wiki updates - requires manual review",
+ },
+ "process": {
+ "keywords": ["process", "workflow", "procedure", "setup", "onboarding", "migration"],
+ "verification_method": "stakeholder_approval",
+ "description": "Process/workflow changes - requires stakeholder approval",
+ },
+ "infrastructure": {
+ "keywords": ["deploy", "server", "502", "500", "gateway", "docker", "kubernetes"],
+ "verification_method": "logs_monitoring",
+ "description": "Infrastructure/deployment - check logs and monitoring",
+ },
+ "feature": {
+ "keywords": ["add", "implement", "feature", "new", "create"],
+ "verification_method": "code_inspection",
+ "description": "Feature implementation - verify code exists and works",
+ },
+ "bug": {
+ "keywords": ["bug", "fix", "issue", "error", "cannot", "broken", "not working"],
+ "verification_method": "code_inspection",
+ "description": "Bug fix - verify in code and git history",
+ },
+ "refactor": {
+ "keywords": ["refactor", "cleanup", "update", "migrate", "convert"],
+ "verification_method": "code_inspection",
+ "description": "Code refactoring - verify in code changes",
+ },
+ "styling": {
+ "keywords": ["style", "ui", "design", "css", "styling", "appearance"],
+ "verification_method": "code_inspection",
+ "description": "UI/styling changes - verify in code",
+ },
+ }
+
+ def __init__(self, gh_token: Optional[str] = None):
+ """Initialize verifier with optional GitHub token"""
+ self.gh_token = gh_token
+ self.today = datetime.now().replace(tzinfo=None)
+ self.repo_root = "/Users/trilliumsmith/code/VRMS/VRMS"
+ self.issue_cache = {} # Cache exact issue titles for consistency
+
+ def fetch_issue(self, issue_num: int) -> Optional[Dict[str, Any]]:
+ """Fetch issue data from GitHub"""
+ try:
+ cmd = [
+ "gh",
+ "issue",
+ "view",
+ str(issue_num),
+ "--json",
+ "number,title,state,labels,createdAt,updatedAt,assignees,body,milestone",
+ ]
+ result = subprocess.run(
+ cmd, capture_output=True, text=True, check=True
+ )
+ issue_data = json.loads(result.stdout)
+ # Cache the exact title for consistency
+ exact_title = issue_data.get("title", "")
+ self.issue_cache[issue_num] = exact_title
+ return issue_data
+ except subprocess.CalledProcessError as e:
+ print(f"Error fetching issue #{issue_num}: {e.stderr}", file=sys.stderr)
+ return None
+
+ def get_exact_title(self, issue_num: int) -> str:
+ """Get the exact issue title (cached for consistency)"""
+ return self.issue_cache.get(issue_num, "")
+
+ def days_since(self, date_str: str) -> int:
+ """Calculate days between date string and today"""
+ try:
+ # Parse ISO format date, handle UTC timezone
+ date = datetime.fromisoformat(date_str.replace("Z", "+00:00"))
+ # Remove timezone info for comparison
+ date = date.replace(tzinfo=None)
+ delta = self.today - date
+ return delta.days
+ except (ValueError, AttributeError):
+ return -1
+
+ def extract_labels(self, labels: List[Dict]) -> set:
+ """Extract label names as lowercase set"""
+ return {label["name"].lower() for label in labels}
+
+ def matches_patterns(self, text: str, patterns: List[str]) -> bool:
+ """Check if text matches any pattern"""
+ text_lower = text.lower()
+ return any(pattern.lower() in text_lower for pattern in patterns)
+
+ def detect_label_category(self, labels: set) -> Dict[str, bool]:
+ """Detect category flags from labels"""
+ categories = {}
+ for category, patterns in self.LABEL_PATTERNS.items():
+ categories[f"has_{category}"] = any(
+ pattern in text for pattern in patterns for text in labels
+ )
+ return categories
+
+ def detect_title_category(self, title: str) -> Dict[str, bool]:
+ """Detect category flags from title"""
+ categories = {}
+ for category, pattern in self.TITLE_PATTERNS.items():
+ categories[f"has_{category}_title"] = bool(
+ re.search(pattern, title, re.IGNORECASE)
+ )
+ return categories
+
+ def verify_issue(self, issue_num: int) -> Dict[str, Any]:
+ """Run full verification on an issue"""
+ issue = self.fetch_issue(issue_num)
+ if not issue:
+ return {"error": f"Issue #{issue_num} not found"}
+
+ # Extract data
+ title = issue["title"]
+ state = issue["state"]
+ created = issue["createdAt"].split("T")[0]
+ updated = issue["updatedAt"].split("T")[0]
+ labels = self.extract_labels(issue.get("labels", []))
+ assignees = [a["login"] for a in issue.get("assignees", [])]
+ milestone = issue.get("milestone", {})
+
+ # Calculate days
+ days_created = self.days_since(issue["createdAt"])
+ days_updated = self.days_since(issue["updatedAt"])
+
+ # STEP 1: Detect issue type to determine verification scope
+ issue_type_info = self.detect_issue_type(title, labels)
+
+ # Detect categories
+ label_cats = self.detect_label_category(labels)
+ title_cats = self.detect_title_category(title)
+ all_metadata = {**label_cats, **title_cats}
+
+ # Apply decision tree
+ verdict = self._apply_decision_tree(
+ title, all_metadata, assignees, days_created, days_updated
+ )
+
+ # Find related commits
+ related_keywords = [title.split()[0:3], issue["title"]] # Use first few words as search
+ related_commits = self.find_related_commits(
+ [" ".join(related_keywords[0]).lower(), issue["title"].lower()]
+ )
+
+ return {
+ "issue": issue_num,
+ "title": title,
+ "state": state,
+ "issue_type": issue_type_info,
+ "verification_scope": {
+ "method": issue_type_info["verification_method"],
+ "description": issue_type_info["description"],
+ "confidence": issue_type_info["confidence"],
+ },
+ "verdict": verdict["verdict"],
+ "category": verdict["category"],
+ "confidence": verdict["confidence"],
+ "reasoning": verdict["reasoning"],
+ "action": verdict["action"],
+ "blocker": verdict.get("blocker"),
+ "dates": {
+ "created": created,
+ "updated": updated,
+ "days_since_created": days_created,
+ "days_since_updated": days_updated,
+ },
+ "metadata": {
+ "assigned_to": assignees if assignees else None,
+ **all_metadata,
+ },
+ "related_commits": related_commits,
+ }
+
+ def detect_issue_type(self, title: str, labels: set) -> Dict[str, Any]:
+ """
+ Detect issue type to determine appropriate verification scope.
+ This is the PRE-VERIFICATION step that decides HOW to verify.
+
+ Uses two signals:
+ 1. Title keywords (what the issue is about)
+ 2. Role labels (who should work on it)
+ """
+ title_lower = title.lower()
+ labels_lower = {label.lower() for label in labels}
+
+ # SIGNAL 1: Check role labels for dev indication
+ has_dev_role = any(role in label for role in self.DEV_ROLES for label in labels_lower)
+ has_non_dev_role = any(role in label for role in self.NON_DEV_ROLES for label in labels_lower)
+
+ # SIGNAL 2: Check title for issue type keywords
+ detected_types = []
+
+ for issue_type, config in self.ISSUE_TYPES.items():
+ keywords = config.get("keywords", [])
+ if any(keyword in title_lower for keyword in keywords):
+ detected_types.append({
+ "type": issue_type,
+ "verification_method": config["verification_method"],
+ "description": config["description"],
+ "reason": f"Keywords matched: {', '.join([k for k in keywords if k in title_lower])}",
+ })
+
+ # COMBINE SIGNALS: Labels override or confirm type
+ primary_type = None
+ verification_method = None
+ reason_parts = []
+
+ # Strong signal: Role labels indicate verification method
+ if has_dev_role and not has_non_dev_role:
+ verification_method = "code_inspection"
+ reason_parts.append("Dev role detected (code verification needed)")
+ elif has_non_dev_role and not has_dev_role:
+ verification_method = "manual_review"
+ reason_parts.append("Non-dev role detected (manual review needed)")
+
+ # Use title detection if available
+ if detected_types:
+ primary_type = detected_types[0]["type"]
+ if not verification_method:
+ verification_method = detected_types[0]["verification_method"]
+ reason_parts.append(f"Type detected: {detected_types[0]['reason']}")
+
+ # Default if nothing detected
+ if not primary_type:
+ primary_type = "unknown"
+ if not verification_method:
+ verification_method = "manual_review"
+ reason_parts.append("No detection signals - defaulting to manual review")
+
+ confidence = "high" if len(reason_parts) > 1 else "medium" if reason_parts else "low"
+
+ return {
+ "primary_type": primary_type,
+ "verification_method": verification_method,
+ "description": f"Issue type: {primary_type}, Verification: {verification_method}",
+ "confidence": confidence,
+ "detected_types": detected_types,
+ "reason": " | ".join(reason_parts) if reason_parts else "No detection signals",
+ "label_signals": {
+ "has_dev_role": has_dev_role,
+ "has_non_dev_role": has_non_dev_role,
+ },
+ }
+
+ def validate_title_consistency(self, issues: List[int]) -> Dict[int, str]:
+ """Pre-cache all issue titles for batch processing consistency"""
+ titles_by_issue = {}
+ for issue_num in issues:
+ issue = self.fetch_issue(issue_num)
+ if issue:
+ exact_title = issue.get("title", "")
+ titles_by_issue[issue_num] = exact_title
+ self.issue_cache[issue_num] = exact_title
+ return titles_by_issue
+
+ def _apply_decision_tree(
+ self,
+ title: str,
+ metadata: Dict[str, bool],
+ assignees: List[str],
+ days_created: int,
+ days_updated: int,
+ ) -> Dict[str, str]:
+ """Apply decision tree logic"""
+
+ # 1. Recurring meetings
+ if metadata.get("has_meeting") or metadata.get("has_meeting_title"):
+ return {
+ "verdict": "KEEP_OPEN",
+ "category": "recurring_process",
+ "confidence": "high",
+ "reasoning": "This is a recurring meeting or status item",
+ "action": "Keep open - legitimate ongoing process",
+ }
+
+ # 2. Draft status (not PM-ready)
+ if metadata.get("has_draft") and not metadata.get("has_pm_decision"):
+ return {
+ "verdict": "FLAG_PM",
+ "category": "draft_status",
+ "confidence": "high",
+ "blocker": "DRAFT - not ready for work",
+ "reasoning": "Issue is in draft status and not prioritized",
+ "action": "FLAG FOR PM: Review and decide if draft should be actioned or closed",
+ }
+
+ # 3. PM decision needed
+ if metadata.get("has_pm_decision"):
+ return {
+ "verdict": "FLAG_PM",
+ "category": "stakeholder_decision",
+ "confidence": "high",
+ "blocker": "STAKEHOLDER - needs PM approval",
+ "reasoning": "Issue marked as ready for PM review/prioritization",
+ "action": "FLAG FOR PM: Prioritize or decide on closure",
+ }
+
+ # 4. Stakeholder feedback
+ if metadata.get("has_stakeholder") or metadata.get("has_stakeholder_title"):
+ return {
+ "verdict": "FLAG_PM",
+ "category": "stakeholder_decision",
+ "confidence": "high",
+ "blocker": "STAKEHOLDER - awaiting feedback incorporation",
+ "reasoning": "Issue involves stakeholder input needing PM triage",
+ "action": "FLAG FOR PM: Incorporate feedback or close",
+ }
+
+ # 5. Epic issues
+ if metadata.get("has_epic"):
+ return {
+ "verdict": "FLAG_PM",
+ "category": "epic_scope",
+ "confidence": "high",
+ "blocker": "EPIC - needs scope clarification",
+ "reasoning": "Epic requires scope review and active management",
+ "action": "FLAG FOR PM: Clarify scope and status",
+ }
+
+ # 6. Security issues
+ if metadata.get("has_security"):
+ return {
+ "verdict": "VERIFY_CODE",
+ "category": "security",
+ "confidence": "medium",
+ "reasoning": "Security issue needs verification of fix status",
+ "action": "VERIFY: Check if security concern has been addressed",
+ }
+
+ # 7. Blocked issues
+ if metadata.get("has_blocked"):
+ return {
+ "verdict": "FLAG_PM",
+ "category": "blocked",
+ "confidence": "high",
+ "blocker": "BLOCKED - awaiting dependencies",
+ "reasoning": "Issue is blocked on other work",
+ "action": "FLAG FOR PM: Review blockers and unblock if ready",
+ }
+
+ # 8. Unassigned and very old
+ if not assignees and days_created > 900 and days_updated > 180:
+ return {
+ "verdict": "CLOSE",
+ "category": "abandoned",
+ "confidence": "high",
+ "reasoning": f"Unassigned {days_created}d old, no updates {days_updated}d",
+ "action": "CLOSE: Issue appears abandoned",
+ }
+
+ # 9. Default
+ return {
+ "verdict": "VERIFY_CODE",
+ "category": "needs_classification",
+ "confidence": "medium",
+ "reasoning": "Insufficient label data - needs code inspection",
+ "action": "INVESTIGATE: Check code state or flag for PM",
+ }
+
+ def find_related_commits(self, keywords: List[str], file_paths: List[str] = None) -> List[Dict[str, str]]:
+ """Find commits related to an issue by keywords or files"""
+ commits = []
+ try:
+ # Search commit messages for keywords
+ for keyword in keywords:
+ cmd = [
+ "git",
+ "log",
+ "--oneline",
+ "--all",
+ "-S",
+ keyword,
+ "-i",
+ ]
+ if file_paths:
+ cmd.extend(file_paths)
+
+ result = subprocess.run(cmd, capture_output=True, text=True, cwd=self.repo_root)
+
+ if result.stdout:
+ for line in result.stdout.strip().split("\n"):
+ if not line:
+ continue
+ parts = line.split(" ", 1)
+ if len(parts) == 2:
+ commit_hash = parts[0]
+ message = parts[1]
+ commits.append(
+ {
+ "hash": commit_hash,
+ "message": message,
+ "keyword": keyword,
+ }
+ )
+
+ # Search for PR references in commit messages
+ pr_pattern = r"#(\d+)"
+ for commit in commits:
+ match = re.search(pr_pattern, commit["message"])
+ if match:
+ commit["pr_number"] = match.group(1)
+
+ # Remove duplicates, keep first occurrence
+ seen = set()
+ unique_commits = []
+ for commit in commits:
+ if commit["hash"] not in seen:
+ seen.add(commit["hash"])
+ unique_commits.append(commit)
+
+ return unique_commits[:5] # Return top 5 most recent
+ except Exception as e:
+ print(f"Error finding commits: {e}", file=sys.stderr)
+ return []
+
+ def verify_code(self, check_type: str) -> Dict[str, Any]:
+ """Verify if code has been implemented/fixed"""
+ import os
+ import re
+
+ if check_type not in self.CODE_CHECKS:
+ return {"found": False, "evidence": "Check type not recognized"}
+
+ check_config = self.CODE_CHECKS[check_type]
+ patterns = check_config.get("patterns", [])
+ keywords = check_config.get("keywords", [])
+
+ evidence = {"files_found": [], "code_snippets": []}
+
+ try:
+ for pattern in patterns:
+ search_path = os.path.join(self.repo_root, pattern)
+ if not os.path.exists(search_path):
+ continue
+
+ for root, dirs, files in os.walk(search_path):
+ # Skip node_modules and common irrelevant dirs
+ dirs[:] = [d for d in dirs if d not in ["node_modules", ".git", "dist", "build"]]
+
+ for file in files:
+ if not file.endswith((".tsx", ".ts", ".jsx", ".js", ".md")):
+ continue
+
+ filepath = os.path.join(root, file)
+ try:
+ with open(filepath, "r", encoding="utf-8", errors="ignore") as f:
+ content = f.read()
+
+ # Check for keywords
+ for keyword in keywords:
+ if re.search(keyword, content, re.IGNORECASE):
+ rel_path = filepath.replace(self.repo_root, "").lstrip("/")
+ evidence["files_found"].append(rel_path)
+ # Extract snippet
+ for match in re.finditer(
+ f".{{0,50}}{keyword}.{{0,50}}", content, re.IGNORECASE
+ ):
+ evidence["code_snippets"].append(match.group(0).strip())
+ break
+ except (IOError, UnicodeDecodeError):
+ pass
+ except Exception as e:
+ return {"found": False, "error": str(e), "evidence": evidence}
+
+ found = len(evidence["files_found"]) > 0
+ return {"found": found, "evidence": evidence}
+
+ def verify_batch(self, issues: List[int]) -> Dict[str, Any]:
+ """Verify multiple issues and generate report"""
+ results = {
+ "metadata": {
+ "generated_at": datetime.now().isoformat(),
+ "total_issues": len(issues),
+ },
+ "verdicts": {"CLOSE": [], "KEEP_OPEN": [], "FLAG_PM": [], "VERIFY_CODE": []},
+ "by_category": {},
+ }
+
+ for i, issue_num in enumerate(issues, 1):
+ print(f" [{i}/{len(issues)}] Processing #{issue_num}...", file=sys.stderr)
+ result = self.verify_issue(issue_num)
+
+ if "error" in result:
+ continue
+
+ verdict = result["verdict"]
+ category = result["category"]
+
+ results["verdicts"][verdict].append(result)
+
+ if category not in results["by_category"]:
+ results["by_category"][category] = []
+ results["by_category"][category].append(result)
+
+ # Add summary
+ results["summary"] = {
+ "close_count": len(results["verdicts"]["CLOSE"]),
+ "keep_open_count": len(results["verdicts"]["KEEP_OPEN"]),
+ "flag_pm_count": len(results["verdicts"]["FLAG_PM"]),
+ "verify_code_count": len(results["verdicts"]["VERIFY_CODE"]),
+ }
+
+ return results
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description="Verify stale GitHub issues and generate recommendations"
+ )
+ parser.add_argument("--issue", type=int, help="Verify single issue number")
+ parser.add_argument(
+ "--batch", help="Batch process from issues.json file"
+ )
+ parser.add_argument(
+ "--output", default="stale_report.json", help="Output file for batch results"
+ )
+ parser.add_argument(
+ "--list", action="store_true", help="List all stale open issues"
+ )
+
+ args = parser.parse_args()
+
+ verifier = StaleIssueVerifier()
+
+ if args.issue:
+ # Single issue verification
+ result = verifier.verify_issue(args.issue)
+ print(json.dumps(result, indent=2))
+
+ elif args.batch:
+ # Batch verification
+ print(f"Loading issues from {args.batch}...", file=sys.stderr)
+ with open(args.batch) as f:
+ issues = json.load(f)
+
+ issue_nums = [issue["number"] for issue in issues]
+ print(f"Verifying {len(issue_nums)} stale issues...", file=sys.stderr)
+
+ # Pre-cache all titles for consistency
+ print(f"Pre-caching issue titles for consistency...", file=sys.stderr)
+ verifier.validate_title_consistency(issue_nums)
+
+ report = verifier.verify_batch(issue_nums)
+
+ with open(args.output, "w") as f:
+ json.dump(report, f, indent=2)
+
+ print(f"✅ Report saved to {args.output}", file=sys.stderr)
+ print(json.dumps(report["summary"], indent=2), file=sys.stderr)
+
+ elif args.list:
+ # List stale issues
+ try:
+ result = subprocess.run(
+ [
+ "gh",
+ "issue",
+ "list",
+ "--state",
+ "all",
+ "--limit",
+ "500",
+ "--json",
+ "number,title,state,createdAt",
+ "--jq",
+ r'.[] | select(.createdAt < "2024-03-17") | select(.state == "OPEN") | "\(.number): \(.title)"',
+ ],
+ capture_output=True,
+ text=True,
+ check=True,
+ )
+ print(result.stdout)
+ except subprocess.CalledProcessError as e:
+ print(f"Error fetching issues: {e.stderr}", file=sys.stderr)
+ sys.exit(1)
+
+ else:
+ parser.print_help()
+ sys.exit(1)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/backend/.eslintrc.json b/backend/.eslintrc.json
index 10b43be28..1e459e7b2 100644
--- a/backend/.eslintrc.json
+++ b/backend/.eslintrc.json
@@ -1,9 +1,11 @@
{
+ "root": true,
"parser": "@babel/eslint-parser",
"parserOptions": {
"sourceType": "module",
"allowImportExportEverywhere": false,
- "codeFrame": false
+ "codeFrame": false,
+ "requireConfigFile": false
},
"plugins": ["@babel"],
"extends": ["airbnb", "prettier"],
diff --git a/backend/.prettierrc b/backend/.prettierrc
deleted file mode 100644
index e09edeb67..000000000
--- a/backend/.prettierrc
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "printWidth": 100,
- "trailingComma": "all",
- "tabWidth": 2,
- "semi": true,
- "singleQuote": true
-}
diff --git a/backend/Dockerfile.api b/backend/Dockerfile.api
index fcaaf6294..6b2f7576b 100644
--- a/backend/Dockerfile.api
+++ b/backend/Dockerfile.api
@@ -1,4 +1,4 @@
-FROM node:14.11.0 AS api-development
+FROM node:18.12.0 AS api-development
RUN mkdir /srv/backend
WORKDIR /srv/backend
RUN mkdir -p node_modules
@@ -6,14 +6,14 @@ COPY package.json yarn.lock ./
RUN yarn install --pure-lockfile
COPY . .
-FROM node:14.11.0 AS api-test
+FROM node:18.12.0 AS api-test
RUN mkdir /srv/backend
WORKDIR /srv/backend
COPY package.json yarn.lock ./
RUN yarn install --silent
RUN mkdir -p node_modules
-FROM node:14.11.0-slim AS api-production
+FROM node:18.12.0-slim AS api-production
EXPOSE 4000
USER node
WORKDIR /srv/backend
diff --git a/backend/Dockerfile.dev b/backend/Dockerfile.dev
index bcffe803f..c1afb2054 100644
--- a/backend/Dockerfile.dev
+++ b/backend/Dockerfile.dev
@@ -1,4 +1,4 @@
-FROM node:14.11.0 AS api-development
+FROM node:18.12.0 AS api-development
RUN mkdir /srv/backend
WORKDIR /srv/backend
RUN mkdir -p node_modules
diff --git a/backend/Dockerfile.prod b/backend/Dockerfile.prod
index 7bccfd0df..b80ccf41c 100644
--- a/backend/Dockerfile.prod
+++ b/backend/Dockerfile.prod
@@ -1,4 +1,4 @@
-FROM node:14.11.0 AS api-builder
+FROM node:18.12.0 AS api-builder
RUN mkdir /srv/backend
WORKDIR /srv/backend
RUN mkdir -p node_modules
@@ -6,10 +6,13 @@ COPY package.json yarn.lock ./
RUN yarn install --pure-lockfile
COPY . .
-FROM node:14.11.0-slim AS api-production
+FROM node:18.12.0-slim AS api-production
+ARG BUILD_SHA
EXPOSE 4000
-USER node
WORKDIR /srv/backend
COPY --from=api-builder /srv/backend/node_modules ./node_modules
COPY . .
+ENV BUILD_SHA=${BUILD_SHA}
+RUN echo "${BUILD_SHA:-unknown}" > BUILD_INFO
+USER node
CMD ["npm", "run", "start"]
diff --git a/backend/app.js b/backend/app.js
index cf0017064..6a6e3cbf0 100644
--- a/backend/app.js
+++ b/backend/app.js
@@ -1,11 +1,11 @@
+
// app.js - Entry point for our application
// Load in all of our node modules. Their uses are explained below as they are called.
const express = require('express');
-const bodyParser = require('body-parser');
+const morgan = require('morgan');
const cron = require('node-cron');
const fetch = require('node-fetch');
-const morgan = require('morgan');
const cookieParser = require('cookie-parser');
const customRequestHeaderName = 'x-customrequired-header';
@@ -43,8 +43,8 @@ require('assert-env')([
const app = express();
// Required to view Request Body (req.body) in JSON
-app.use(bodyParser.json());
-app.use(bodyParser.urlencoded({ extended: true }));
+app.use(express.json());
+app.use(express.urlencoded({ extended: true }));
// Used to save JWT token from MagicLink
app.use(cookieParser());
@@ -53,11 +53,20 @@ app.use(cookieParser());
app.use(morgan('dev'));
// WORKERS
-const runOpenCheckinWorker = require('./workers/openCheckins')(cron, fetch);
-const runCloseCheckinWorker = require('./workers/closeCheckins')(cron, fetch);
-const runCreateRecurringEventsWorker = require('./workers/createRecurringEvents')(cron, fetch);
+const runOpenCheckinWorker = require('./workers/openCheckins');
+runOpenCheckinWorker(cron, fetch);
+
+const runCloseCheckinWorker = require('./workers/closeCheckins');
+runCloseCheckinWorker(cron, fetch);
+
+const { createRecurringEvents } = require('./workers/createRecurringEvents');
+createRecurringEvents(cron, fetch);
// const runSlackBot = require("./workers/slackbot")(fetch);
+// Run cleanup expired refresh token(s) on startup
+const { cleanupExpiredTokens } = require('./workers/tokenCleanup');
+cleanupExpiredTokens();
+
// MIDDLEWARE
const errorhandler = require('./middleware/errorhandler.middleware');
diff --git a/backend/config/auth.config.js b/backend/config/auth.config.js
index fb968cd21..45c6f966b 100644
--- a/backend/config/auth.config.js
+++ b/backend/config/auth.config.js
@@ -1,8 +1,8 @@
-/*eslint-disable */
module.exports = {
- SECRET:
- 'c0d7d0716e4cecffe9dcc77ff90476d98f5aace08ea40f5516bd982b06401021191f0f24cd6759f7d8ca41b64f68d0b3ad19417453bddfd1dbe8fcb197245079',
+ JWT_SECRET: process.env.JWT_SECRET || 'placeholder_secret_key_for_development_only',
CUSTOM_REQUEST_HEADER: process.env.CUSTOM_REQUEST_HEADER,
- TOKEN_EXPIRATION_SEC: 900,
+ // 15 minutes as a string for JWT expiration
+ ACCESS_TOKEN_EXPIRATION: '15m',
+ // 30 days in milliseconds for refresh token expiration
+ REFRESH_TOKEN_EXPIRATION_MS: 30 * 24 * 60 * 60 * 1000,
};
-/* eslint-enable */
\ No newline at end of file
diff --git a/backend/controllers/email.controller.test.js b/backend/controllers/email.controller.test.js
index 515a99a59..ecad2417a 100644
--- a/backend/controllers/email.controller.test.js
+++ b/backend/controllers/email.controller.test.js
@@ -1,8 +1,5 @@
const EmailController = require('./email.controller');
-const { setupDB } = require('../setup-test');
-setupDB('conrtoller-email');
-
test('Can import the email controller', async () => {
expect(EmailController).not.toBeUndefined();
});
diff --git a/backend/controllers/event.controller.js b/backend/controllers/event.controller.js
index e0e52af2d..ca6b275ba 100644
--- a/backend/controllers/event.controller.js
+++ b/backend/controllers/event.controller.js
@@ -9,6 +9,7 @@ EventController.event_list = async function (req, res) {
const events = await Event.find(query).populate('project');
return res.status(200).send(events);
} catch (err) {
+ console.error('[event.controller]', err);
return res.sendStatus(400);
}
};
@@ -20,6 +21,7 @@ EventController.event_by_id = async function (req, res) {
const events = await Event.findById(EventId).populate('project');
return res.status(200).send(events);
} catch (err) {
+ console.error('[event.controller]', err);
return res.sendStatus(400);
}
};
@@ -28,9 +30,15 @@ EventController.create = async function (req, res) {
const { body } = req;
try {
- const event = await Event.create(body);
- return res.status(201).send(event);
+ if (Array.isArray(body)) {
+ const events = await Event.insertMany(body);
+ return res.status(201).send(events);
+ } else {
+ const event = await Event.create(body);
+ return res.status(201).send(event);
+ }
} catch (err) {
+ console.error('[event.controller]', err);
return res.sendStatus(400);
}
};
@@ -42,17 +50,31 @@ EventController.destroy = async function (req, res) {
const event = await Event.findByIdAndDelete(EventId);
return res.status(200).send(event);
} catch (err) {
+ console.error('[event.controller]', err);
return res.sendStatus(400);
}
};
EventController.update = async function (req, res) {
- const { EventId } = req.params;
+ const { body } = req;
try {
- const event = await Event.findByIdAndUpdate(EventId, req.body);
- return res.status(200).send(event);
+ if (Array.isArray(body)) {
+ const ops = body.map((e) => ({
+ updateOne: {
+ filter: { _id: e._id },
+ update: { $set: { checkInReady: e.checkInReady } },
+ },
+ }));
+ const events = await Event.bulkWrite(ops);
+ return res.status(200).send(events);
+ } else {
+ const { EventId } = req.params;
+ const event = await Event.findByIdAndUpdate(EventId, req.body);
+ return res.status(200).send(event);
+ }
} catch (err) {
+ console.error('[event.controller]', err);
return res.sendStatus(400);
}
};
diff --git a/backend/controllers/event.controller.test.js b/backend/controllers/event.controller.test.js
index 0c3555f61..bbfe625b5 100644
--- a/backend/controllers/event.controller.test.js
+++ b/backend/controllers/event.controller.test.js
@@ -1,6 +1,3 @@
-const { setupDB } = require('../setup-test');
-setupDB('event-controller');
-
const EventController = require('./event.controller');
test('Can import the email controller', async () => {
diff --git a/backend/controllers/healthCheck.controller.js b/backend/controllers/healthCheck.controller.js
index 068d5557e..8be9bbed9 100644
--- a/backend/controllers/healthCheck.controller.js
+++ b/backend/controllers/healthCheck.controller.js
@@ -1,7 +1,53 @@
+const { execSync } = require('child_process');
+const fs = require('fs');
+
const HealthCheckController = {};
-HealthCheckController.isAlive = (_, res) => {
- res.status(200).send("I'm Alive!");
+function getBuildInfo() {
+ // Method 1: BUILD_SHA environment variable (from Docker build arg) - PRIORITY
+ if (
+ process.env.BUILD_SHA &&
+ process.env.BUILD_SHA !== 'undefined' &&
+ process.env.BUILD_SHA !== ''
+ ) {
+ return process.env.BUILD_SHA;
+ }
+
+ // Method 2: Check BUILD_INFO file (created during Docker build)
+ try {
+ if (fs.existsSync('/srv/backend/BUILD_INFO')) {
+ const buildInfo = fs.readFileSync('/srv/backend/BUILD_INFO', 'utf8').trim();
+ if (buildInfo && buildInfo !== 'unknown' && buildInfo !== '') {
+ return buildInfo;
+ }
+ }
+ } catch {
+ // BUILD_INFO file not available
+ }
+
+ // Method 3: Try git command (for local development)
+ try {
+ const gitSha = execSync('git rev-parse --short HEAD 2>/dev/null', { encoding: 'utf8', shell: true }).trim();
+ if (gitSha && gitSha !== '' && gitSha !== 'unknown') {
+ return gitSha;
+ }
+ } catch {
+ // Git not available
+ }
+
+ return 'unknown';
}
+// Cache build info at startup to avoid repeated file I/O and Git calls
+const cachedBuildInfo = getBuildInfo();
+const buildTimestamp = new Date().toISOString();
+
+HealthCheckController.isAlive = (_, res) => {
+ res
+ .status(200)
+ .send(
+ `I'm Alive! Build: ${cachedBuildInfo} | Built: ${buildTimestamp} | Checked: ${new Date().toISOString()}`,
+ );
+};
+
module.exports = HealthCheckController;
diff --git a/backend/controllers/project.controller.js b/backend/controllers/project.controller.js
index dfbf51928..d45a398b9 100644
--- a/backend/controllers/project.controller.js
+++ b/backend/controllers/project.controller.js
@@ -1,4 +1,5 @@
-const { Project } = require('../models');
+const { Project, User } = require('../models');
+const { ObjectId } = require('mongodb');
const ProjectController = {};
@@ -13,6 +14,16 @@ ProjectController.project_list = async function (req, res) {
}
};
+ProjectController.pm_filtered_projects = async function (req, res) {
+ try {
+ const projectList = await Project.find({});
+ const projects = projectList.filter((proj) => req.body.includes(proj._id.toString()));
+ return res.status(200).send(projects);
+ } catch (e) {
+ return res.sendStatus(400);
+ }
+};
+
ProjectController.create = async function (req, res) {
const { body } = req;
@@ -38,7 +49,7 @@ ProjectController.project_by_id = async function (req, res) {
ProjectController.update = async function (req, res) {
const { ProjectId } = req.params;
try {
- const project = await Project.findOneAndUpdate({_id: ProjectId}, req.body, {new: true});
+ const project = await Project.findOneAndUpdate({ _id: ProjectId }, req.body, { new: true });
return res.status(200).send(project);
} catch (err) {
return res.sendStatus(400);
@@ -56,5 +67,89 @@ ProjectController.destroy = async function (req, res) {
}
};
+ProjectController.updateManagedByUsers = async function (req, res) {
+ const { ProjectId } = req.params;
+ const { action, userId } = req.body; // action - 'add' or 'remove'
+
+ try {
+ // Update project's managedByUsers and the user's managedProjects
+ const project = await Project.findById(ProjectId);
+ let managedByUsers = project.managedByUsers || [];
+
+ const user = await User.findById(userId);
+ let managedProjects = user.managedProjects || [];
+
+ if (action === 'add') {
+ managedByUsers = [...new Set([...managedByUsers, userId])];
+ managedProjects = [...new Set([...managedProjects, ProjectId])];
+ } else {
+ // remove case
+ managedByUsers = managedByUsers.filter((id) => id !== userId);
+ managedProjects = managedProjects.filter((id) => id !== ProjectId);
+ }
+
+ // Update project's managedByUsers
+ project.managedByUsers = managedByUsers;
+ await project.save({ validateBeforeSave: false });
+
+ // Update user's managedProjects
+ user.managedProjects = managedProjects;
+ await user.save({ validateBeforeSave: false });
+
+ return res.status(200).send({ project, user });
+ } catch (err) {
+ console.log(err);
+ return res.sendStatus(400);
+ }
+};
+
+ProjectController.bulkUpdateManagedByUsers = async function (req, res) {
+ const { bulkOps } = req.body;
+
+ // Convert string IDs to ObjectId in bulkOps
+ bulkOps.forEach((op) => {
+ if (op?.updateOne?.filter._id) {
+ op.updateOne.filter._id = new ObjectId(op.updateOne.filter._id);
+ }
+ if (op?.updateOne?.update) {
+ const update = op.updateOne.update;
+ if (update?.$addToSet?.managedByUsers) {
+ update.$addToSet.managedByUsers = new ObjectId(update.$addToSet.managedByUsers);
+ }
+ if (update?.$pull?.managedByUsers) {
+ update.$pull.managedByUsers = new ObjectId(update.$pull.managedByUsers);
+ }
+ }
+ });
+
+ try {
+ const result = await Project.bulkWrite(bulkOps);
+ res.status(200).json(result);
+ } catch (err) {
+ res.status(500).json({ error: err.message });
+ }
+};
+
+// Update onboard/offboard visibility for a project
+ProjectController.updateOnboardOffboardVisibility = async function (req, res) {
+ const { ProjectId } = req.params;
+ const { onboardOffboardVisible } = req.body;
+
+ try {
+ const project = await Project.findByIdAndUpdate(
+ ProjectId,
+ { onboardOffboardVisible },
+ { new: true }
+ );
+
+ if (!project) {
+ return res.status(404).send({ message: 'Project not found' });
+ }
+
+ return res.status(200).send(project);
+ } catch (err) {
+ return res.status(400).send({ message: 'Error updating visibility', error: err.message });
+ }
+};
module.exports = ProjectController;
diff --git a/backend/controllers/project.controller.test.js b/backend/controllers/project.controller.test.js
index 47cc470da..180525343 100644
--- a/backend/controllers/project.controller.test.js
+++ b/backend/controllers/project.controller.test.js
@@ -1,8 +1,5 @@
const ProjectController = require('./project.controller');
-const { setupDB } = require('../setup-test');
-setupDB('project-controller');
-
test('Can import the project controller', async () => {
expect(ProjectController).not.toBeUndefined();
});
diff --git a/backend/controllers/user.controller.js b/backend/controllers/user.controller.js
index e9764765d..a2ed742bc 100644
--- a/backend/controllers/user.controller.js
+++ b/backend/controllers/user.controller.js
@@ -1,16 +1,23 @@
const jwt = require('jsonwebtoken');
+const { ObjectId } = require('mongodb');
const EmailController = require('./email.controller');
const { CONFIG_AUTH } = require('../config');
-const { User } = require('../models');
+const { User, Project, RefreshToken } = require('../models');
+const {
+ generateRefreshToken,
+ getClientIp,
+ hashToken,
+ generateAccessToken,
+} = require('../middleware/auth.middleware');
const expectedHeader = process.env.CUSTOM_REQUEST_HEADER;
const UserController = {};
// Get list of Users with GET
-UserController.user_list = async function (req, res) {
+UserController.user_list = async (req, res) => {
const { headers } = req;
const { query } = req;
@@ -22,12 +29,100 @@ UserController.user_list = async function (req, res) {
const user = await User.find(query);
return res.status(200).send(user);
} catch (err) {
+ console.error(err);
+ return res.sendStatus(400);
+ }
+};
+
+UserController.user_by_email = async (req, res) => {
+ const { headers } = req;
+ const { email } = req.params;
+
+ console.log('email: ', email);
+
+ if (headers['x-customrequired-header'] !== expectedHeader) {
+ return res.sendStatus(403);
+ }
+
+ try {
+ const user = await User.find({ email });
+ return res.status(200).send(user);
+ } catch (err) {
+ console.log(err);
+ return res.sendStatus(400);
+ }
+};
+
+// Get list of Users with accessLevel 'admin' or 'superadmin' with GET
+UserController.admin_list = async (req, res) => {
+ const { headers } = req;
+
+ if (headers['x-customrequired-header'] !== expectedHeader) {
+ return res.sendStatus(403);
+ }
+
+ try {
+ const admins = await User.find({
+ accessLevel: { $in: ['admin', 'superadmin'] },
+ });
+ return res.status(200).send(admins);
+ } catch (err) {
+ console.error(err);
+ return res.sendStatus(400);
+ }
+};
+
+UserController.projectManager_list = async (req, res) => {
+ const { headers } = req;
+
+ if (headers['x-customrequired-header'] !== expectedHeader) {
+ return res.sendStatus(403);
+ }
+
+ try {
+ const projectManagers = await User.find({
+ managedProjects: { $exists: true, $type: 'array', $ne: [] },
+ });
+
+ // Collect all unique project IDs
+ const allProjectIds = [
+ ...new Set(
+ projectManagers
+ .flatMap((pm) => pm.managedProjects)
+ .filter((id) => typeof id === 'string' && id.match(/^[a-f\d]{24}$/i)),
+ ),
+ ];
+
+ // Fetch all projects in one query
+ const projects = await Project.find(
+ { _id: { $in: allProjectIds } },
+ { _id: 1, name: 1 }, // projection
+ );
+
+ const projectIdToName = {};
+ for (const project of projects) {
+ projectIdToName[project._id.toString()] = project.name;
+ }
+
+ const updatedProjectManagers = projectManagers.map((pm) => {
+ const pmObj = pm.toObject();
+ pmObj.isProjectLead = true;
+ pmObj.managedProjectNames = (pmObj.managedProjects || [])
+ .map((pid) => projectIdToName[pid.toString()] || null)
+ .filter(Boolean);
+ return pmObj;
+ });
+
+ return res.status(200).send(updatedProjectManagers);
+ } catch (err) {
+ console.error(err);
+ console.log('Projectlead error', err);
return res.sendStatus(400);
}
};
// Get User by id with GET
-UserController.user_by_id = async function (req, res) {
+UserController.user_by_id = async (req, res) => {
const { headers } = req;
const { UserId } = req.params;
@@ -39,24 +134,24 @@ UserController.user_by_id = async function (req, res) {
const user = await User.findById(UserId);
return res.status(200).send(user);
} catch (err) {
+ console.error(err);
return res.sendStatus(400);
}
};
// Add User with POST
-UserController.create = async function (req, res) {
+UserController.create = async (req, res) => {
const { headers } = req;
if (headers['x-customrequired-header'] !== expectedHeader) {
return res.sendStatus(403);
}
-
try {
- const newUser = {
- ...req.body,
- email: req.body.email.toLowerCase()
- }
+ const newUser = {
+ ...req.body,
+ email: req.body.email.toLowerCase(),
+ };
const user = await User.create(newUser);
return res.status(201).send(user);
} catch (error) {
@@ -71,7 +166,7 @@ UserController.create = async function (req, res) {
};
// Update User with PATCH
-UserController.update = async function (req, res) {
+UserController.update = async (req, res) => {
const { headers } = req;
const { UserId } = req.params;
@@ -80,15 +175,18 @@ UserController.update = async function (req, res) {
}
try {
- const user = await User.findOneAndUpdate({_id: UserId}, req.body, { new: true });
+ const user = await User.findOneAndUpdate({ _id: UserId }, req.body, {
+ new: true,
+ });
return res.status(200).send(user);
} catch (err) {
+ console.error(err);
return res.sendStatus(400);
}
};
// Add User with POST
-UserController.delete = async function (req, res) {
+UserController.delete = async (req, res) => {
const { headers } = req;
const { UserId } = req.params;
@@ -100,22 +198,12 @@ UserController.delete = async function (req, res) {
const user = await User.findByIdAndDelete(UserId);
return res.status(200).send(user);
} catch (err) {
+ console.error(err);
return res.sendStatus(400);
}
};
-function generateAccessToken(user, auth_origin) {
- // expires after half and hour (1800 seconds = 30 minutes)
- return jwt.sign(
- { id: user.id, role: user.accessLevel, auth_origin: auth_origin },
- CONFIG_AUTH.SECRET,
- {
- expiresIn: `${CONFIG_AUTH.TOKEN_EXPIRATION_SEC}s`,
- },
- );
-}
-
-UserController.createUser = function (req, res) {
+UserController.createUser = async (req, res) => {
const { firstName, lastName, email } = req.body;
const { origin } = req.headers;
@@ -128,20 +216,19 @@ UserController.createUser = function (req, res) {
accessLevel: 'user',
});
- // eslint-disable-next-line
- user.save((err, usr) => {
- if (err) {
- res.sendStatus(400);
- }
+ try {
+ await user.save();
res.sendStatus(201);
- });
+ } catch (err) {
+ res.sendStatus(400);
+ }
const jsonToken = generateAccessToken(user);
EmailController.sendLoginLink(req.body.email, user.name.firstName, jsonToken, req.cookie, origin);
};
-UserController.signin = function (req, res) {
+UserController.signin = (req, res) => {
const { email, auth_origin } = req.body;
const { origin } = req.headers;
@@ -168,8 +255,7 @@ UserController.signin = function (req, res) {
});
};
-UserController.verifySignIn = async function (req, res) {
- // eslint-disable-next-line dot-notation
+UserController.verifySignIn = async (req, res) => {
let token = req.headers['x-access-token'] || req.headers['authorization'];
if (token.startsWith('Bearer ')) {
// Remove Bearer from string
@@ -177,25 +263,127 @@ UserController.verifySignIn = async function (req, res) {
}
try {
- const payload = jwt.verify(token, CONFIG_AUTH.SECRET);
+ const payload = jwt.verify(token, CONFIG_AUTH.JWT_SECRET);
const user = await User.findById(payload.id);
- res.cookie('token', token, { httpOnly: true });
- return res.send(user);
+ const refreshToken = generateRefreshToken();
+ const accessToken = generateAccessToken(user, payload.auth_origin);
+ const ipAddress = getClientIp(req);
+
+ await RefreshToken.create({
+ userId: user._id,
+ hash: hashToken(refreshToken),
+ deviceInfo: {
+ deviceType: req.headers['user-agent'],
+ ipAddress: ipAddress,
+ },
+ });
+
+ res.cookie('token', accessToken, { httpOnly: true });
+ res.cookie('refresh_token', refreshToken, { httpOnly: true });
+
+ return res.send({
+ user: user,
+ expiresAt: accessToken.exp * 1000, // Convert JWT exp (seconds) to milliseconds
+ });
} catch (err) {
+ console.error(err);
return res.status(403);
}
};
-UserController.verifyMe = async function (req, res) {
- const user = await User.findById(req.userId);
- return res.status(200).send(user);
+UserController.verifyMe = async (req, res) => res.status(200).send(req.user);
+
+UserController.logout = async (req, res) => {
+ try {
+ await RefreshToken.deleteOne({ _id: req.refreshToken._id });
+ return res.clearCookie('token').status(200).send('Successfully logged out.');
+ } catch (err) {
+ console.error(err);
+ return res.status(500).send('Error occurred while logging out.');
+ }
};
-UserController.logout = async function (req, res) {
+UserController.refreshAccessToken = async (req, res) => {
+ const accessToken = generateAccessToken(req.user, req.auth_origin);
+ const decoded = jwt.decode(accessToken);
+
return res
- .clearCookie('token')
+ .cookie('token', accessToken, { httpOnly: true })
.status(200)
- .send('Successfully logged out.');
-}
+ .json({
+ user: req.user,
+ expiresAt: decoded.exp * 1000, // Convert JWT exp (seconds) to milliseconds
+ });
+};
+
+// Update user's managedProjects
+UserController.updateManagedProjects = async (req, res) => {
+ const { headers } = req;
+ const { UserId } = req.params;
+ const { action, projectId } = req.body; // action - 'add' or 'remove'
+ // console.log('action:', action, 'projectId:', projectId);
+
+ if (headers['x-customrequired-header'] !== expectedHeader) {
+ return res.sendStatus(403);
+ }
+
+ try {
+ // Update user's managedProjects and the project's managedByUsers
+ const user = await User.findById(UserId);
+ let managedProjects = user.managedProjects || [];
+
+ const project = await Project.findById(projectId);
+ let managedByUsers = project.managedByUsers || [];
+
+ if (action === 'add') {
+ managedProjects = [...managedProjects, projectId];
+ managedByUsers = [...managedByUsers, UserId];
+ } else {
+ // remove case
+ managedProjects = managedProjects.filter((id) => id !== projectId);
+ managedByUsers = managedByUsers.filter((id) => id !== UserId);
+ }
+
+ // Update user's managedProjects
+ user.managedProjects = managedProjects;
+ await user.save({ validateBeforeSave: false });
+
+ // Update project's managedByUsers
+ project.managedByUsers = managedByUsers;
+ await project.save({ validateBeforeSave: false });
+
+ return res.status(200).send({ user, project });
+ } catch (err) {
+ console.log(err);
+ return res.sendStatus(400);
+ }
+};
+
+UserController.bulkUpdateManagedProjects = async (req, res) => {
+ const { bulkOps } = req.body;
+
+ // Convert string IDs to ObjectId in bulkOps
+ bulkOps.forEach((op) => {
+ if (op?.updateOne?.filter._id) {
+ op.updateOne.filter._id = new ObjectId(op.updateOne.filter._id);
+ }
+ if (op?.updateOne?.update) {
+ const update = op.updateOne.update;
+ if (update?.$addToSet?.managedProjects) {
+ update.$addToSet.managedProjects = new ObjectId(update.$addToSet.managedProjects);
+ }
+ if (update?.$pull?.managedProjects) {
+ update.$pull.managedProjects = new ObjectId(update.$pull.managedProjects);
+ }
+ }
+ });
+
+ try {
+ const result = await User.bulkWrite(bulkOps);
+ res.status(200).json(result);
+ } catch (err) {
+ res.status(500).json({ error: err.message });
+ }
+};
module.exports = UserController;
diff --git a/backend/controllers/user.controller.test.js b/backend/controllers/user.controller.test.js
index 7d89aa599..fdf59cde1 100644
--- a/backend/controllers/user.controller.test.js
+++ b/backend/controllers/user.controller.test.js
@@ -1,7 +1,3 @@
-const { setupDB } = require('../setup-test');
-setupDB('conrtoller-user');
-
-jest.mock('../models/user.model');
const userContoller = require('./user.controller');
test('Can import the email controller', async () => {
diff --git a/backend/globalConfig.json b/backend/globalConfig.json
index 0c381857a..4f59459b4 100644
--- a/backend/globalConfig.json
+++ b/backend/globalConfig.json
@@ -1 +1 @@
-{"mongoUri":"mongodb://127.0.0.1:63857/jest?","mongoDBName":"jest"}
\ No newline at end of file
+{"mongoUri":"mongodb://127.0.0.1:43943/jest?","mongoDBName":"jest"}
diff --git a/backend/jest-mongodb-config.js b/backend/jest-mongodb-config.js
deleted file mode 100644
index 796000caa..000000000
--- a/backend/jest-mongodb-config.js
+++ /dev/null
@@ -1,12 +0,0 @@
-module.exports = {
- mongodbMemoryServerOptions: {
- binary: {
- version: "4.0.14",
- skipMD5: true,
- },
- instance: {
- dbName: "jest",
- },
- autoStart: false,
- },
-};
diff --git a/backend/jest.config.js b/backend/jest.config.js
index dddcd91b2..eb16998a0 100644
--- a/backend/jest.config.js
+++ b/backend/jest.config.js
@@ -1,5 +1,6 @@
module.exports = {
- preset: '@shelf/jest-mongodb',
+ testEnvironment: 'node',
setupFilesAfterEnv: ['./jest.setup.js'],
watchPathIgnorePatterns: ['globalConfig'],
+ testPathIgnorePatterns: ['/test/old-tests/'],
};
diff --git a/backend/middleware/auth.middleware.js b/backend/middleware/auth.middleware.js
index f10869183..c9351d18f 100644
--- a/backend/middleware/auth.middleware.js
+++ b/backend/middleware/auth.middleware.js
@@ -1,30 +1,158 @@
const jwt = require('jsonwebtoken');
const { CONFIG_AUTH } = require('../config');
-function verifyToken(req, res, next) {
- // Allow users to set token
- // eslint-disable-next-line dot-notation
- let token = req.headers['x-access-token'] || req.headers['authorization'];
- if (token.startsWith('Bearer ')) {
- // Remove Bearer from string
- token = token.slice(7, token.length);
+const { RefreshToken, User } = require('../models');
+const crypto = require('crypto');
+const AuthUtils = require('../../shared/authorizationUtils');
+
+const SECRET = CONFIG_AUTH.JWT_SECRET;
+
+// Utility functions
+
+function generateAccessToken(user, auth_origin) {
+ return jwt.sign(
+ {
+ id: user._id,
+ email: user.email,
+ role: user.accessLevel,
+ accessLevel: user.accessLevel,
+ auth_origin: auth_origin,
+ },
+ SECRET,
+ { expiresIn: CONFIG_AUTH.ACCESS_TOKEN_EXPIRATION },
+ );
+}
+
+function generateRefreshToken() {
+ return crypto.randomBytes(32).toString('hex');
+}
+
+function hashToken(token) {
+ return crypto.createHash('sha256').update(token).digest('hex');
+}
+
+function getClientIp(req) {
+ // Check X-Forwarded-For header (most common)
+ const forwarded = req.headers['x-forwarded-for'];
+ if (forwarded) {
+ // Takes the first IP if there are multiple
+ return forwarded.split(',')[0].trim();
}
- if (!token) {
- return res.sendStatus(403);
+
+ // Check other common headers
+ return (
+ req.headers['x-real-ip'] || req.connection.remoteAddress || req.socket.remoteAddress || req.ip
+ );
+}
+
+async function authenticateAccessToken(req, res, next) {
+ try {
+ // Extract token from Authorization header
+ let accessToken =
+ req.cookies.token || req.headers['x-access-token'] || req.headers['authorization'];
+
+ if (!accessToken) {
+ return res.status(401).json({ error: 'Access token required' });
+ }
+
+ if (accessToken.startsWith('Bearer ')) {
+ accessToken = accessToken.slice(7, accessToken.length);
+ }
+
+ const decoded = jwt.verify(accessToken, SECRET);
+ // Attach user info to request
+ req.user = decoded;
+
+ next();
+ } catch (error) {
+ if (error.name === 'TokenExpiredError') {
+ return res.status(401).json({ error: 'Token expired' });
+ }
+
+ if (error.name === 'JsonWebTokenError') {
+ return res.status(401).json({ error: 'Invalid token' });
+ }
+
+ return res.status(401).json({ error: 'Authentication failed' });
}
+}
+
+// shorthand for authenticateAccessToken
+const authUser = authenticateAccessToken;
+async function authenticateRefreshToken(req, res, next) {
try {
- const decoded = jwt.verify(token, CONFIG_AUTH.SECRET);
- res.cookie('token', token, { httpOnly: true });
- req.userId = decoded.id;
- return next();
- } catch (err) {
- return res.sendStatus(401);
+ const refreshToken = req.cookies?.refresh_token;
+
+ if (!refreshToken) {
+ return res.status(401).json({ error: 'Refresh token required' });
+ }
+
+ const tokenHash = hashToken(refreshToken);
+
+ const tokenDoc = await RefreshToken.findOne({
+ hash: tokenHash,
+ expiresAt: { $gt: new Date() },
+ });
+
+ if (!tokenDoc) {
+ return res.status(401).json({ error: 'Invalid or expired refresh token' });
+ }
+
+ const user = await User.findById(tokenDoc.userId);
+ if (!user) {
+ return res.status(401).json({ error: 'User not found for this token' });
+ }
+
+ // Attach user & refresh token to request for downstream handlers
+ req.user = user;
+ req.refreshToken = tokenDoc;
+
+ next();
+ } catch (error) {
+ console.error('Refresh token validation error:', error);
+ return res.status(401).json({ error: 'Authentication failed' });
}
}
+function requireRole(...roles) {
+ return (req, res, next) => {
+ if (!req.user) {
+ return res.status(401).json({ error: 'Authentication required' });
+ }
+
+ if (!AuthUtils.hasAnyRole(req.user, roles)) {
+ return res.status(403).json({
+ error: 'Insufficient permissions',
+ required_role: roles,
+ your_role: req.user.accessLevel,
+ });
+ }
+
+ next();
+ };
+}
+
+function requireMinimumRole(role) {
+ return (req, res, next) => {
+ if (!req.user) {
+ return res.status(401).json({ error: 'Authentication required' });
+ }
+
+ const user = req.user;
+ if (!AuthUtils.hasMinimumRole(user, role)) {
+ return res.status(403).json({
+ error: 'Insufficient permissions',
+ required_minimum_role: role,
+ your_role: req.user.accessLevel,
+ });
+ }
+ next();
+ };
+}
+
function verifyCookie(req, res, next) {
- jwt.verify(req.cookies.token, CONFIG_AUTH.SECRET, (err, decoded) => {
+ jwt.verify(req.cookies.token, SECRET, (err, decoded) => {
if (err) {
return res.sendStatus(401);
}
@@ -35,8 +163,15 @@ function verifyCookie(req, res, next) {
});
}
-const AuthUtil = {
- verifyToken,
+module.exports = {
+ authenticateAccessToken,
+ authUser,
+ authenticateRefreshToken,
+ requireRole,
+ requireMinimumRole,
+ generateAccessToken,
+ generateRefreshToken,
+ getClientIp,
+ hashToken,
verifyCookie,
};
-module.exports = AuthUtil;
diff --git a/backend/middleware/index.js b/backend/middleware/index.js
index 397b37c3f..16194a369 100644
--- a/backend/middleware/index.js
+++ b/backend/middleware/index.js
@@ -1,9 +1,7 @@
-const AuthUtil = require('./auth.middleware');
+const Auth = require('./auth.middleware');
const verifyUser = require('./user.middleware');
-const verifyToken = require('./token.middleware');
module.exports = {
- AuthUtil,
+ Auth,
verifyUser,
- verifyToken,
};
diff --git a/backend/middleware/token.middleware.js b/backend/middleware/token.middleware.js
deleted file mode 100644
index 640873a5f..000000000
--- a/backend/middleware/token.middleware.js
+++ /dev/null
@@ -1,27 +0,0 @@
-const jwt = require('jsonwebtoken');
-const { CONFIG_AUTH } = require('../config');
-
-async function isTokenValid(req, res, next) {
- let token = req.headers['x-access-token'] || req.headers['authorization'];
- if (token.startsWith('Bearer ')) {
- // Remove Bearer from string
- token = token.slice(7, token.length);
- }
-
- if (!token) {
- return res.sendStatus(400);
- }
-
- try {
- jwt.verify(token, CONFIG_AUTH.SECRET);
- next();
- } catch (err) {
- return res.sendStatus(403);
- }
-}
-
-const verifyToken = {
- isTokenValid,
-};
-
-module.exports = verifyToken;
diff --git a/backend/middleware/user.middleware.js b/backend/middleware/user.middleware.js
index 6e675313c..ac9c1c81d 100644
--- a/backend/middleware/user.middleware.js
+++ b/backend/middleware/user.middleware.js
@@ -15,7 +15,7 @@ function isAdminByEmail(req, res, next) {
return res.sendStatus(400);
} else {
const role = user.accessLevel;
- if (req.get('origin').includes('3001') || role === 'admin' || user.managedProjects.length > 0) {
+ if (role === 'admin' || role === 'superadmin' || user.managedProjects.length > 0) {
next();
} else {
next(res.sendStatus(401));
diff --git a/backend/models/checkin.test.js b/backend/models/checkin.test.js
deleted file mode 100644
index cbb12811e..000000000
--- a/backend/models/checkin.test.js
+++ /dev/null
@@ -1,23 +0,0 @@
-const { CheckIn } = require('./checkIn.model');
-
-const { setupDB } = require("../setup-test");
-setupDB("checkin-model");
-
-describe("Checkin Model saves the correct values", () => {
- test("Save a model instance and then read from the db", async (done) => {
- const submittedCheckinData = {
- userId: "1",
- eventId: "23",
- checkedIn: true,
- createdDate: 1594023390039,
- };
-
- await CheckIn.create(submittedCheckinData);
- const savedCheckinDataArray = await CheckIn.find();
- const savedCheckinData = savedCheckinDataArray[0];
- expect(savedCheckinData.userId).toBe(submittedCheckinData.userId);
- expect(savedCheckinData.eventId).toBe(submittedCheckinData.eventId);
- expect(savedCheckinData.createdDate.getTime()).toBe(submittedCheckinData.createdDate);
- done();
- });
-});
diff --git a/backend/models/event.test.js b/backend/models/event.test.js
deleted file mode 100644
index 3a5ef54c8..000000000
--- a/backend/models/event.test.js
+++ /dev/null
@@ -1,50 +0,0 @@
-const { setupDB } = require('../setup-test');
-setupDB("event-model");
-
-const { Event } = require('./event.model');
-
-describe('CREATE', () => {
- test('Can create a complex Event', async (done) => {
- const submittedData = {
- name: 'eventName',
- location: {
- // should we include address here?
- city: 'Los Angeles',
- state: 'California',
- country: 'USA',
- },
- hacknight: 'Online', // DTLA, Westside, South LA, Online
- eventType: 'Workshop', // Project Meeting, Orientation, Workshop
- description: 'A workshop to do stuff',
- date: 1594023390039,
- startTime: 1594023390039, // start date and time of the event
- endTime: 1594023390039, // end date and time of the event
- hours: 2, // length of the event in hours
- createdDate: 1594023390039, // date/time event was created
- updatedDate: 1594023390039, // date/time event was last updated
- checkInReady: false, // is the event open for check-ins?
- owner: {
- ownerId: 33, // id of user who created event
- },
- };
-
- await Event.create(submittedData);
- const savedDataArray = await Event.find();
- const savedData = savedDataArray[0];
- expect(savedData.name).toBe( submittedData.name);
- expect(savedData.location.city).toBe(submittedData.location.city);
- expect(savedData.startTime.getTime()).toBe(submittedData.startTime);
- done();
- });
-
- test('Can create a simple Event', async (done) => {
- const submittedData = { name: 'testEvent' };
- await Event.create(submittedData);
- const savedDataArray = await Event.find();
- const savedData = savedDataArray[0];
- expect(savedData.name).toBe('testEvent');
- done();
- });
-});
-
-describe('Cannot save simple data', () => {});
diff --git a/backend/models/index.js b/backend/models/index.js
index 5d4e41315..d9e3752cd 100644
--- a/backend/models/index.js
+++ b/backend/models/index.js
@@ -6,8 +6,9 @@ const { Question } = require('./question.model');
const { RecurringEvent } = require('./recurringEvent.model');
const { Role } = require('./role.model');
const { User } = require('./user.model');
+const { RefreshToken } = require('./refreshToken.model');
-const mongoose = require("mongoose");
+const mongoose = require('mongoose');
mongoose.Promise = global.Promise;
module.exports = {
@@ -19,4 +20,5 @@ module.exports = {
RecurringEvent,
Role,
User,
+ RefreshToken,
};
diff --git a/backend/models/project.model.js b/backend/models/project.model.js
index e345b2c4f..10a65627b 100644
--- a/backend/models/project.model.js
+++ b/backend/models/project.model.js
@@ -16,49 +16,51 @@ Idea for the future: programmingLanguages, numberGithubContributions (pull these
*/
const projectSchema = mongoose.Schema({
- name: { type: String },
- description: { type: String },
- githubIdentifier: { type: String },
- projectStatus: { type: String }, // Active, Completed, or Paused
- location: { type: String }, // DTLA, Westside, South LA, or Remote (hacknight)
- //teamMembers: { type: String }, // commented since we should be able to get this from Project Team Members table
- createdDate: { type: Date, default: Date.now }, // date/time project was created
- completedDate: { type: Date }, // only if Status = Completed, date/time completed
- githubUrl: { type: String }, // link to main repo
- slackUrl: { type: String }, // link to Slack channel
- googleDriveUrl: { type: String },
- googleDriveId: { type: String },
- hflaWebsiteUrl: { type: String },
- videoConferenceLink: { type: String },
- lookingDescription: { type: String }, // narrative on what the project is looking for
- recruitingCategories: [{ type: String }], // same as global Skills picklist
- partners: [{ type: String }], // any third-party partners on the project, e.g. City of LA
- managedByUsers: [{ type: String }] // Which users may manage this project.
+ name: { type: String, trim: true },
+ description: { type: String, trim: true },
+ githubIdentifier: { type: String, trim: true },
+ projectStatus: { type: String }, // Active, Completed, or Paused
+ location: { type: String, trim: true }, // DTLA, Westside, South LA, or Remote (hacknight)
+ //teamMembers: { type: String }, // commented since we should be able to get this from Project Team Members table
+ createdDate: { type: Date, default: Date.now }, // date/time project was created
+ completedDate: { type: Date }, // only if Status = Completed, date/time completed
+ githubUrl: { type: String, trim: true }, // link to main repo
+ slackUrl: { type: String, trim: true }, // link to Slack channel
+ googleDriveUrl: { type: String, trim: true },
+ googleDriveId: { type: String },
+ hflaWebsiteUrl: { type: String, trim: true },
+ videoConferenceLink: { type: String },
+ lookingDescription: { type: String }, // narrative on what the project is looking for
+ recruitingCategories: [{ type: String }], // same as global Skills picklist
+ partners: [{ type: String }], // any third-party partners on the project, e.g. City of LA
+ managedByUsers: [{ type: String }], // Which users may manage this project.
+ onboardOffboardVisible: { type: Boolean, default: true }, // Whether onboarding/offboarding forms are visible on the project page
});
-projectSchema.methods.serialize = function() {
- return {
- id: this._id,
- name: this.name,
- description: this.description,
- githubIdentifier: this.githubIdentifier,
- // owner: this.owner,
- projectStatus: this.projectStatus,
- location: this.location,
- //teamMembers: this.teamMembers,
- createdDate: this.createdDate,
- completedDate: this.completedDate,
- githubUrl: this.githubUrl,
- slackUrl: this.slackUrl,
- googleDriveUrl: this.googleDriveUrl,
- googleDriveId: this.googleDriveId,
- hflaWebsiteUrl: this.hflaWebsiteUrl,
- videoConferenceLink: this.videoConferenceLink,
- lookingDescription: this.lookingDescription,
- recruitingCategories: this.recruitingCategories,
- partners: this.partners,
- managedByUsers: this.managedByUsers
- };
+projectSchema.methods.serialize = function () {
+ return {
+ id: this._id,
+ name: this.name,
+ description: this.description,
+ githubIdentifier: this.githubIdentifier,
+ // owner: this.owner,
+ projectStatus: this.projectStatus,
+ location: this.location,
+ //teamMembers: this.teamMembers,
+ createdDate: this.createdDate,
+ completedDate: this.completedDate,
+ githubUrl: this.githubUrl,
+ slackUrl: this.slackUrl,
+ googleDriveUrl: this.googleDriveUrl,
+ googleDriveId: this.googleDriveId,
+ hflaWebsiteUrl: this.hflaWebsiteUrl,
+ videoConferenceLink: this.videoConferenceLink,
+ lookingDescription: this.lookingDescription,
+ recruitingCategories: this.recruitingCategories,
+ partners: this.partners,
+ managedByUsers: this.managedByUsers,
+ onboardOffboardVisible: this.onboardOffboardVisible,
+ };
};
const Project = mongoose.model('Project', projectSchema);
diff --git a/backend/models/project.test.js b/backend/models/project.test.js
deleted file mode 100644
index 8cefa3138..000000000
--- a/backend/models/project.test.js
+++ /dev/null
@@ -1,82 +0,0 @@
-const { Project } = require('./project.model');
-
-const { setupDB } = require("../setup-test");
-setupDB("project-model");
-
-describe("Project Model saves the correct values", () => {
- test("Save a model instance and then read from the db", async (done) => {
- const submittedData = {
- name: "projectTest",
- description: "An instance of a Project model",
- githubIdentifier: "VRMS",
- projectStatus: "Active", // Active, Completed, or Paused
- location: "Remote", // DTLA, Westside, South LA, or Remote (hacknight)
- createdDate: 1594023390039, // date/time project was created
- completedDate: 1594023390039, // only if Status = Completed, date/time completed
- githubUrl: "https://github.com/hackforla/VRMS", // link to main repo
- slackUrl: "hackforla.slack.com", // link to Slack channel
- };
-
- await Project.create(submittedData);
- const savedDataArray = await Project.find();
- const savedData = savedDataArray[0];
- expect(savedData.name).toBe(submittedData.name);
- expect(savedData.githubIdentifier).toBe(submittedData.githubIdentifier);
- expect(savedData.githubUrl).toBe(submittedData.githubUrl);
-
-
- done();
- });
-});
-
-describe('CREATE/READ', () => {
- test('Create Project with Mongoose model', async (done) => {
- const submittedData = {
- name: 'projectTest',
- };
-
- await Project.create(submittedData);
- const savedDataArray = await Project.find();
- const savedData = savedDataArray[0];
- expect(savedData.name).toBe(submittedData.name);
- done();
- });
-});
-
-describe('UPDATE', () => {
- test('Update Project with Mongoose model', async (done) => {
- const submittedData = {
- name: 'projectTest',
- };
-
- await Project.create(submittedData);
- const savedDataArray = await Project.find().exec();
- const savedData = savedDataArray[0];
- expect(savedData.name).toBe(submittedData.name);
-
- const updatedData = { name: 'updatedEventName' };
-
- const updatedProject = await Project.findOneAndUpdate({_id: savedData._id}, updatedData,
- {new: true});
-
- expect(updatedProject.name).toBe(updatedData.name);
- done();
- });
-});
-
-describe('DELETE', () => {
- test('Delete Project with Mongoose model', async (done) => {
- const submittedData = {
- name: 'projectTest',
- };
-
- await Project.create(submittedData);
- const savedDataArray = await Project.find().exec();
- const savedData = savedDataArray[0];
- expect(savedData.name).toBe(submittedData.name);
-
- const deleteData = await Project.deleteOne(submittedData);
- expect(deleteData.ok).toBe(1);
- done();
- });
-});
diff --git a/backend/models/projectTeamMemer.test.js b/backend/models/projectTeamMemer.test.js
deleted file mode 100644
index 85a624dd3..000000000
--- a/backend/models/projectTeamMemer.test.js
+++ /dev/null
@@ -1,31 +0,0 @@
-const { ProjectTeamMember } = require('./projectTeamMember.model');
-
-const { setupDB } = require("../setup-test");
-setupDB("projectTeamMember-model");
-
-// Please add and expand on this simple test.
-describe("ProjectTeamMember Model saves the correct values", () => {
- test("Save a model instance and then read from the db", async (done) => {
- const submittedData = {
- teamMemberStatus: "Inactive", // Active or Inactive
- vrmsProjectAdmin: true, // does this team member have admin rights to the project in VRMS?
- roleOnProject: "Developer", // Developer, Project Manager, UX, Data Science
- joinedDate: 1594023390039, // date/time joined project
- leftDate: 1594023390039, // only if Status = Inactive, date/time went inactive
- leftReason: "other", // project completed, project paused, switched projects, no-show, other
- githubPermissionLevel: "Write", // Write, Triage, Read, Maintainer, or Admin; pull from Github API?
- onProjectGithub: true, // added to the project team on github? pull from github api?
- onProjectGoogleDrive: false, // added to the project team's google drive folder?
- };
-
- await ProjectTeamMember.create(submittedData);
- const savedDataArray = await ProjectTeamMember.find();
- const savedData = savedDataArray[0];
- expect(savedData.teamMemberStatus).toBe(submittedData.teamMemberStatus);
- expect(savedData.joinedDate.getTime()).toBe(submittedData.joinedDate);
- expect(
- savedData.githubPermissionLevel).toBe(submittedData.githubPermissionLevel
- );
- done();
- });
-});
diff --git a/backend/models/question.test.js b/backend/models/question.test.js
deleted file mode 100644
index 0c53b8af0..000000000
--- a/backend/models/question.test.js
+++ /dev/null
@@ -1,34 +0,0 @@
-const { Question } = require('./question.model');
-
-const { setupDB } = require("../setup-test");
-setupDB("question-model");
-
-// Please add and expand on this simple test.
-describe("Question Model saves the correct values", () => {
- test("Save a model instance and then read from the db", async (done) => {
- const submittedData = {
- questionText: "Is this a test?",
- htmlName: "html_name_test",
- // inputType: { type: String, default: "text" },
- answers: {
- answerOneText: "test answer one",
- answerTwoText: "test answer two",
- answerThreeText: "test answer three",
- answerFourText: "test answer four",
- },
- };
-
- await Question.create(submittedData);
- const savedDataArray = await Question.find();
- const savedData = savedDataArray[0];
- expect(savedData.questionText).toBe(submittedData.questionText);
- expect(
- savedData.answers.answerOneText).toBe(submittedData.answers.answerOneText
- );
- expect(
- savedData.answers.answerThreeText).toBe(
- submittedData.answers.answerThreeText
- );
- done();
- });
-});
diff --git a/backend/models/recurringEvent.test.js b/backend/models/recurringEvent.test.js
deleted file mode 100644
index 2975cbd56..000000000
--- a/backend/models/recurringEvent.test.js
+++ /dev/null
@@ -1,38 +0,0 @@
-const { RecurringEvent } = require('./recurringEvent.model');
-
-const { setupDB } = require("../setup-test");
-setupDB("recurringEvent-model");
-
-// Please add and expand on this simple test.
-describe("Question Model saves the correct values", () => {
- test("Save a model instance and then read from the db", async (done) => {
- const submittedData = {
- name: "testRecurringEvent",
- location: {
- // should we include address here?
- city: "Los Angeles",
- state: "California",
- country: "USA",
- },
- hacknight: "Online", // DTLA, Westside, South LA, Online
- brigade: "Hack for LA",
- eventType: "Workshop", // Project Meeting, Orientation, Workshop
- description: "A test instance",
- date: 1594023390039,
- startTime: 1594023390039, // start date and time of the event
- endTime: 1594023390039, // end date and time of the event
- hours: 1594023390039, // length of the event in hours
- createdDate: 1594023390039, // date/time event was created
- updatedDate: 1594023390039, // date/time event was last updated
- checkInReady: true, // is the event open for check-ins?
- };
-
- await RecurringEvent.create(submittedData);
- const savedDataArray = await RecurringEvent.find();
- const savedData = savedDataArray[0];
- expect(savedData.name === submittedData.name);
- expect(savedData.location.country === submittedData.location.country);
- expect(savedData.description === submittedData.description);
- done();
- });
-});
diff --git a/backend/models/refreshToken.model.js b/backend/models/refreshToken.model.js
new file mode 100644
index 000000000..ca66f0164
--- /dev/null
+++ b/backend/models/refreshToken.model.js
@@ -0,0 +1,42 @@
+const mongoose = require('mongoose');
+const { CONFIG_AUTH } = require('../config');
+
+mongoose.Promise = global.Promise;
+
+const refreshTokenSchema = mongoose.Schema({
+ userId: {
+ type: mongoose.Schema.Types.ObjectId,
+ ref: 'User',
+ immutable: true,
+ index: true,
+ },
+ hash: { type: String, required: true, unique: true, immutable: true },
+ createdAt: { type: Date, required: true, default: () => Date.now(), immutable: true },
+ expiresAt: {
+ type: Date,
+ required: true,
+ default: () => Date.now() + CONFIG_AUTH.REFRESH_TOKEN_EXPIRATION_MS,
+ },
+ deviceInfo: {
+ ipAddress: String,
+ deviceType: String,
+ },
+});
+
+refreshTokenSchema.methods.serialize = function () {
+ return {
+ id: this._id,
+ createdAt: this.createdAt,
+ expiresAt: this.expiresAt,
+ deviceInfo: {
+ ipAddress: this.ipAddress,
+ deviceType: this.deviceType,
+ },
+ };
+};
+
+refreshTokenSchema.index({ expires_at: 1 }, { expiresAfterSeconds: 0 });
+
+const RefreshToken = mongoose.model('RefreshToken', refreshTokenSchema);
+
+module.exports = { RefreshToken };
diff --git a/backend/models/user.model.js b/backend/models/user.model.js
index 0534d6107..ebaff0a25 100644
--- a/backend/models/user.model.js
+++ b/backend/models/user.model.js
@@ -1,4 +1,4 @@
-const mongoose = require("mongoose");
+const mongoose = require('mongoose');
// const bcrypt = require('bcrypt-nodejs');
mongoose.Promise = global.Promise;
@@ -8,8 +8,13 @@ const userSchema = mongoose.Schema({
firstName: { type: String },
lastName: { type: String },
},
- email: { type: String, unique: true },
- accessLevel: { type: String, default: "user" },
+ email: { type: String, unique: true, lowercase: true },
+ accessLevel: {
+ type: String,
+ enum: ['user', 'admin', 'superadmin'], // restricts values to "user", "admin" and "superadmin"
+ default: 'user',
+ },
+ role: { type: String },
createdDate: { type: Date, default: Date.now },
currentRole: { type: String }, // will remove but need to update check-in form
desiredRole: { type: String }, // will remove but need to update check-in form
@@ -19,11 +24,10 @@ const userSchema = mongoose.Schema({
skillsToMatch: [{ type: String }], // skills the user either has or wants to learn - will use to match to projects
firstAttended: { type: String },
attendanceReason: { type: String },
- githubHandle: { type: String },
projects: [
{
type: mongoose.Schema.Types.ObjectId,
- ref: "Project",
+ ref: 'Project',
},
],
githubHandle: { type: String }, // handle not including @, not the URL
@@ -33,9 +37,10 @@ const userSchema = mongoose.Schema({
isHflaGithubMember: { type: Boolean }, // pull from API once github handle in place?
githubPublic2FA: { type: Boolean }, // does the user have 2FA enabled on their github and membership set to public?
availability: { type: String }, // availability to meet outside of hacknight times; string for now, more structured in future
- managedProjects: [{ type: String}] // Which projects managed by user.
+ managedProjects: [{ type: String }], // Which projects managed by user.
//currentProject: { type: String } // no longer need this as we can get it from Project Team Member table
// password: { type: String, required: true }
+ isActive: { type: Boolean, default: true },
});
userSchema.methods.serialize = function () {
@@ -56,7 +61,6 @@ userSchema.methods.serialize = function () {
skillsToMatch: this.skillsToMatch,
firstAttended: this.firstAttended,
attendanceReason: this.attendanceReason,
- githubHandle: this.githubHandle,
projects: this.projects,
//currentProject: this.currentProject
phone: this.phone,
@@ -65,10 +69,11 @@ userSchema.methods.serialize = function () {
isHflaGithubMember: this.isHflaGithubMember,
githubPublic2FA: this.githubPublic2FA,
availability: this.availability,
- managedProjects: this.managedProjects
+ managedProjects: this.managedProjects,
+ isActive: this.isActive,
};
};
-const User = mongoose.model("User", userSchema);
+const User = mongoose.model('User', userSchema);
module.exports = { User };
diff --git a/backend/models/user.model.test.js b/backend/models/user.model.test.js
new file mode 100644
index 000000000..4a32e35be
--- /dev/null
+++ b/backend/models/user.model.test.js
@@ -0,0 +1,135 @@
+// import necessary modules
+const mongoose = require('mongoose');
+const { User } = require('./user.model');
+
+describe('Unit tests for User Model', () => {
+ // Clears all mocks after each test
+ afterEach(() => {
+ jest.restoreAllMocks();
+ });
+
+ describe('Serialization test', () => {
+ it('should return the correct serialized user object', async () => {
+ // Create mock user data
+ const userObj = {
+ _id: new mongoose.Types.ObjectId(),
+ name: {
+ firstName: 'mock',
+ lastName: 'user',
+ },
+ email: 'mock.user@example.com',
+ accessLevel: 'user',
+ createdDate: new Date(),
+ currentRole: 'developer',
+ desiredRole: 'lead developer',
+ newMember: false,
+ currentJobTitle: 'Software Engineer',
+ desiredJobTitle: 'Senior Software Engineer',
+ skillsToMatch: ['Jest', 'Node.js'],
+ firstAttended: '2025-01-01',
+ attendanceReason: 'To learn and contribute',
+ projects: ['ProjectId1', 'ProjectId2'],
+ phone: '123-456-7890',
+ textingOk: true,
+ slackName: 'mockuser',
+ isHflaGithubMember: true,
+ githubPublic2FA: true,
+ availability: 'Weekdays',
+ managedProjects: ['Project1', 'Project2'],
+ isActive: true,
+ };
+
+ // Create a mock user instance
+ const mockUser = new User(userObj);
+ const serializedUser = mockUser.serialize();
+
+ // Test
+ expect(serializedUser).toEqual({
+ id: mockUser._id,
+ name: {
+ firstName: mockUser.name.firstName,
+ lastName: mockUser.name.lastName,
+ },
+ email: mockUser.email,
+ accessLevel: mockUser.accessLevel,
+ createdDate: mockUser.createdDate,
+ currentRole: mockUser.currentRole,
+ desiredRole: mockUser.desiredRole,
+ newMember: mockUser.newMember,
+ currentJobTitle: mockUser.currentRole,
+ desiredJobTitle: mockUser.desiredRole,
+ skillsToMatch: mockUser.skillsToMatch,
+ firstAttended: mockUser.firstAttended,
+ attendanceReason: mockUser.attendanceReason,
+ projects: mockUser.projects,
+ phone: mockUser.phone,
+ textingOk: mockUser.textingOk,
+ slackName: mockUser.slackName,
+ isHflaGithubMember: mockUser.isHflaGithubMember,
+ githubPublic2FA: mockUser.githubPublic2FA,
+ availability: mockUser.availability,
+ managedProjects: mockUser.managedProjects,
+ isActive: mockUser.isActive,
+ });
+ });
+ });
+
+ describe('Validation test', () => {
+ it('should fail validation check if accessLevel is invalid', async () => {
+ // Create a mock user with an invalid accessLevel
+ const mockuser = new User({
+ accessLevel: 'projectleader', // not 'user', 'admin', or 'superadmin'
+ });
+
+ // Attempt to validate the mock user by checking for valid accessLevel
+ let error;
+ try {
+ await mockuser.validate();
+ } catch (err) {
+ error = err;
+ }
+
+ // Tests
+ expect(error).toBeDefined();
+ expect(error.errors.accessLevel).toBeDefined();
+ });
+
+ it('should enforce that emails are stored in lowercase', async () => {
+ // Create a mock user with an uppercase email
+ const uppercaseEmail = 'TEST@test.com';
+ const mockUser = new User({
+ email: uppercaseEmail,
+ });
+
+ mockUser.validate();
+ // Tests
+ expect(mockUser.email).toBe(uppercaseEmail.toLowerCase());
+ });
+
+ it('should pass validation with valid user data', async () => {
+ // Create a mock user with valid data
+ const mockUser = new User({
+ name: {
+ firstName: 'Valid',
+ lastName: 'User',
+ },
+ email: 'mockuser@gmail.com',
+ accessLevel: 'user',
+ });
+
+ // Attempt to save the mock user
+ let error;
+ try {
+ await mockUser.validate();
+ } catch (err) {
+ error = err;
+ }
+
+ // Tests
+ expect(error).toBeUndefined();
+ expect(mockUser.email).toBe('mockuser@gmail.com');
+ expect(mockUser.accessLevel).toBe('user');
+ await expect(mockUser.validate()).resolves.toBeUndefined(); // async validation check
+ });
+ });
+});
diff --git a/backend/models/user.test.js b/backend/models/user.test.js
index 97ef50d35..1ac16f083 100644
--- a/backend/models/user.test.js
+++ b/backend/models/user.test.js
@@ -1,11 +1,11 @@
+const mongoose = require('mongoose');
const { User } = require('./user.model');
+const { setupIntegrationDB } = require('../setup-test');
-const { setupDB } = require("../setup-test");
-setupDB("user-model");
+setupIntegrationDB('user-model');
-// Please add and expand on this simple test.
-describe("Question Model saves the correct values", () => {
- test('Save a model instance and then read from the db', async (done) => {
+describe('User Model - Create and Read', () => {
+ test('Save a model instance and then read from the db', async () => {
const submittedData = {
name: {
firstName: 'Test',
@@ -22,37 +22,107 @@ describe("Question Model saves the correct values", () => {
skillsToMatch: ['marketing assistant'],
firstAttended: 'year 0',
attendanceReason: 'training',
- githubHandle: '@testuser',
phone: '867-5309',
textingOk: true,
slackName: 'slacktestuser',
};
await User.create(submittedData);
- const savedDataArray = await User.find();
- const savedData = savedDataArray[0];
+ const savedData = await User.findOne({ email: submittedData.email });
expect(savedData.name.firstName).toBe(submittedData.name.firstName);
expect(savedData.currentRole).toBe(submittedData.currentRole);
expect(savedData.desiredJobTitle).toBe(submittedData.desiredJobTitle);
- done();
});
- test('Create a simple user', async (done) => {
- // Test Data
+ test('Create a simple user', async () => {
const submittedData = {
name: {
- firstName: 'test',
- lastName: 'user',
+ firstName: 'Simple',
+ lastName: 'User',
},
- email: 'newtest@test.com',
+ email: 'simple@test.com',
};
await User.create(submittedData);
- const savedDataArray = await User.find();
- const savedData = savedDataArray[0];
+ const savedData = await User.findOne({ email: submittedData.email });
expect(savedData.name.firstName).toBe(submittedData.name.firstName);
- expect(savedData.currentRole).toBe(submittedData.currentRole);
- expect(savedData.desiredJobTitle).toBe(submittedData.desiredJobTitle);
- done();
+ expect(savedData.email).toBe(submittedData.email);
+ });
+});
+
+describe('User Model - Serialization', () => {
+ test('should serialize user data correctly', async () => {
+ const userData = {
+ name: { firstName: 'Serialize', lastName: 'Test' },
+ email: 'serialize.test@example.com',
+ accessLevel: 'admin',
+ currentRole: 'Backend Developer',
+ desiredRole: 'Lead Developer',
+ newMember: false,
+ currentJobTitle: 'Actual Current Job Title',
+ desiredJobTitle: 'Actual Desired Job Title',
+ skillsToMatch: ['JavaScript', 'MongoDB'],
+ phone: '555-0101',
+ textingOk: true,
+ slackName: 'serializeslack',
+ isHflaGithubMember: true,
+ githubPublic2FA: false,
+ availability: 'Evenings',
+ managedProjects: ['ProjectGamma'],
+ isActive: true,
+ createdDate: new Date(2023, 0, 15),
+ };
+
+ const user = await User.create(userData);
+ const serializedUser = user.serialize();
+
+ expect(serializedUser.id.toString()).toBe(user._id.toString());
+ expect(serializedUser.name.firstName).toBe(userData.name.firstName);
+ expect(serializedUser.name.lastName).toBe(userData.name.lastName);
+ expect(serializedUser.email).toBe(userData.email);
+ expect(serializedUser.accessLevel).toBe(userData.accessLevel);
+ expect(serializedUser.createdDate).toEqual(userData.createdDate);
+ expect(serializedUser.currentRole).toBe(userData.currentRole);
+ expect(serializedUser.desiredRole).toBe(userData.desiredRole);
+ expect(serializedUser.newMember).toBe(userData.newMember);
+ expect([...serializedUser.skillsToMatch]).toEqual(userData.skillsToMatch);
+ expect(serializedUser.phone).toBe(userData.phone);
+ expect(serializedUser.textingOk).toBe(userData.textingOk);
+ expect(serializedUser.slackName).toBe(userData.slackName);
+ expect(serializedUser.isHflaGithubMember).toBe(userData.isHflaGithubMember);
+ expect(serializedUser.githubPublic2FA).toBe(userData.githubPublic2FA);
+ expect(serializedUser.availability).toBe(userData.availability);
+ expect([...serializedUser.managedProjects]).toEqual(userData.managedProjects);
+ expect(serializedUser.isActive).toBe(userData.isActive);
+ });
+});
+
+describe('User Model - Validation', () => {
+ test('should fail if email is not unique', async () => {
+ const email = 'unique.validation@example.com';
+ await User.create({
+ name: { firstName: 'First', lastName: 'User' },
+ email: email,
+ accessLevel: 'user',
+ });
+
+ await expect(
+ User.create({
+ name: { firstName: 'Second', lastName: 'User' },
+ email: email,
+ accessLevel: 'user',
+ }),
+ ).rejects.toMatchObject({ code: 11000 });
+ });
+
+ test('should fail if accessLevel is not in enum', async () => {
+ const userData = {
+ name: { firstName: 'Enum', lastName: 'Test' },
+ email: 'enum.test@example.com',
+ accessLevel: 'invalid_access_level',
+ };
+
+ const user = new User(userData);
+ await expect(user.save()).rejects.toBeInstanceOf(mongoose.Error.ValidationError);
});
});
diff --git a/backend/package.json b/backend/package.json
index ff91edf63..bf6234611 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -4,35 +4,25 @@
"description": "VRMS Backend",
"main": "server.js",
"scripts": {
- "lint": "eslint .",
- "format": "prettier --check .",
+ "lint": "biome lint .",
+ "format": "biome format --check .",
"test": "jest",
"test:watch": "jest --watch",
"start": "node server.js",
"dev": "nodemon server.js",
"client": "npm run start --prefix client",
- "heroku-postbuild": "cd client && npm install && npm run build"
+ "heroku-postbuild": "cd client && npm install && npm run build",
+ "clone-or-sync-projects": "node ./scripts/cloneOrSyncCollections.js",
+ "clear-dev-collections": "node ./scripts/clearDevCollections.js"
},
"author": "sarL3y",
"license": "ISC",
"devDependencies": {
- "@babel/core": "^7.15.0",
- "@babel/eslint-parser": "^7.15.0",
- "@babel/eslint-plugin": "^7.14.5",
- "@shelf/jest-mongodb": "^1.2.3",
"concurrently": "^5.1.0",
- "debug": "^4.1.1",
- "eslint": "^7.9.0",
- "eslint-config-airbnb": "^18.2.0",
- "eslint-config-airbnb-base": "^14.2.0",
- "eslint-config-prettier": "^6.11.0",
- "eslint-plugin-import": "^2.22.0",
- "eslint-plugin-jsx-a11y": "^6.3.1",
- "eslint-plugin-react": "^7.20.6",
- "jest": "^26.4.0",
+ "debug": "^4.3.1",
+ "jest": "^29.7.0",
+ "mockdate": "^3.0.5",
"nodemon": "^2.0.2",
- "prettier": "^2.1.1",
- "pretty-quick": "^3.0.2",
"supertest": "^4.0.2",
"why-is-node-running": "^2.2.0"
},
@@ -40,22 +30,21 @@
"@slack/bolt": "^2.2.3",
"assert-env": "^0.6.0",
"async": "^3.2.2",
- "body-parser": "^1.19.0",
"cookie-parser": "^1.4.5",
"cors": "^2.8.5",
"dotenv": "^8.2.0",
"dotenv-expand": "^5.1.0",
- "express": "^4.17.1",
+ "express": "^4.20.0",
"express-validator": "^6.6.1",
"googleapis": "^59.0.0",
"helmet": "^3.22.0",
"jsonwebtoken": "^8.5.1",
- "mongodb-memory-server": "^6.9.0",
- "mongoose": "^5.10.0",
+ "mongodb-memory-server": "^8.2.3",
+ "mongoose": "^8.9.5",
"morgan": "^1.10.0",
"node-cron": "^2.0.3",
"node-fetch": "^2.6.7",
- "nodemailer": "^6.6.1"
+ "nodemailer": "^7.0.11"
},
"directories": {
"test": "test"
diff --git a/backend/routers/auth.router.js b/backend/routers/auth.router.js
index 4eb83b5f8..e6ac40d14 100644
--- a/backend/routers/auth.router.js
+++ b/backend/routers/auth.router.js
@@ -1,11 +1,11 @@
const express = require('express');
-const { AuthUtil, verifyUser, verifyToken } = require('../middleware');
+const { Auth, verifyUser } = require('../middleware');
const { UserController } = require('../controllers/');
const { authApiValidator } = require('../validators');
+const { authenticateRefreshToken } = require('../middleware/auth.middleware');
const router = express.Router();
-// eslint-disable-next-line func-names
router.use(function (req, res, next) {
res.header('Access-Control-Allow-Headers', 'x-access-token, Origin, Content-Type, Accept');
next();
@@ -18,16 +18,14 @@ router.post(
UserController.createUser,
);
-router.post(
- '/signin',
- [authApiValidator.validateSigninUserAPICall, verifyUser.isAdminByEmail],
- UserController.signin,
-);
+router.post('/refresh-access-token', [authenticateRefreshToken], UserController.refreshAccessToken);
+
+router.post('/signin', [authApiValidator.validateSigninUserAPICall], UserController.signin);
-router.post('/verify-signin', [verifyToken.isTokenValid], UserController.verifySignIn);
+router.post('/verify-signin', [Auth.authUser], UserController.verifySignIn);
-router.post('/me', [AuthUtil.verifyCookie], UserController.verifyMe);
+router.post('/me', [Auth.authUser], UserController.verifyMe);
-router.post('/logout', [AuthUtil.verifyCookie], UserController.logout);
+router.post('/logout', [authenticateRefreshToken], UserController.logout);
module.exports = router;
diff --git a/backend/routers/auth.router.test.js b/backend/routers/auth.router.test.js
index f7942272d..bc719da02 100644
--- a/backend/routers/auth.router.test.js
+++ b/backend/routers/auth.router.test.js
@@ -1,205 +1,208 @@
-const supertest = require('supertest');
-const app = require('../app');
-const request = supertest(app);
-
-const { setupDB } = require('../setup-test');
-setupDB('api-auth');
-
-const { CONFIG_AUTH } = require('../config/');
-const { User } = require('../models');
-
-
-// Create mock for EmailController
-const sendMailMock = jest.fn()
+// Set up mocks for User model and controller
+jest.mock('../controllers/user.controller');
jest.mock('../controllers/email.controller');
-const mockEmailController = require('../controllers/email.controller');
-mockEmailController.sendLoginLink.mockReturnValue({ sendMail: sendMailMock });
-
-beforeEach(() => {
- sendMailMock.mockClear();
- mockEmailController.sendLoginLink.mockClear();
-});
-
-const headers = {};
-headers['x-customrequired-header'] = CONFIG_AUTH.CUSTOM_REQUEST_HEADER;
-headers.Accept = 'application/json';
-
-// API Tests
-describe('CREATE User', () => {
- test('Create user with POST to /users', async () => {
- // Test Data
- const submittedData = {
- name: { firstName: 'test_first', lastName: 'test_last' },
- email: 'test@test.com',
- };
-
- // Add a user using the API.
- const res = await request.post('/api/users').send(submittedData).set(headers);
-
- expect(res.status).toBe(201);
-
- // Retrieve and compare the the User values using the DB.
- const databaseUserQuery = await User.find();
-
- const databaseUser = databaseUserQuery[0];
-
- expect(databaseUserQuery.length).toBeGreaterThanOrEqual(1);
- expect(databaseUser.name.firstName).toBe(submittedData.name.firstName);
- expect(databaseUser.name.lastName).toBe(submittedData.name.lastName);
-
- // Retrieve and compare the User values using the API.
- const response = await request.get('/api/users').set(headers);
- expect(response.statusCode).toBe(200);
- const APIData = response.body[0];
- expect(APIData.name.firstName).toBe(submittedData.name.firstName);
- expect(APIData.name.lastName).toBe(submittedData.name.lastName);
-
- });
-
- test('Create user with POST to /auth/signup', async () => {
- // setupDBRoles();
- // Test Data
- const goodUserData = {
- name: { firstName: 'testname', lastName: 'testlast' },
- email: 'test@test.com',
- };
-
- const res = await request
- .post('/api/auth/signup')
- .send(goodUserData)
- .set(headers);
-
- expect(res.status).toBe(201);
- });
-});
-
-describe('SIGNUP Validation', () => {
- test('Invalid data to /api/auth/signup returns 403', async () => {
- // Test Data
- const badUserData = {
- firstName: 'test_first',
- lastName: 'test_last',
- email: 'test@test.com',
- };
-
- const res = await request
- .post('/api/auth/signup')
- .send(badUserData)
- .set(headers);
-
- expect(res.status).toBe(403);
- const errorMessage = JSON.parse(res.text);
-
- expect(errorMessage.errors).toEqual([
- { msg: 'Invalid value', param: 'name.firstName', location: 'body' },
- { msg: 'Invalid value', param: 'name.lastName', location: 'body' },
- ]);
+jest.mock('../models/user.model');
+// Set up mocks for middleware
+jest.mock('../middleware', () => ({
+ AuthUtil: {
+ verifyCookie: jest.fn((req, res, next) => next()),
+ },
+ verifyUser: {
+ checkDuplicateEmail: jest.fn((req, res, next) => next()),
+ isAdminByEmail: jest.fn((req, res, next) => next()),
+ },
+ verifyToken: {
+ isTokenValid: jest.fn((req, res, next) => next()),
+ },
+}));
+// Set up mocks for authApiValidator
+jest.mock('../validators/user.api.validator', () => ({
+ validateCreateUserAPICall: jest.fn((req, res, next) => next()),
+ validateSigninUserAPICall: jest.fn((req, res, next) => next()),
+}));
+
+// Import User model and controller
+const { User } = require('../models/user.model');
+const { UserController, EmailController } = require('../controllers');
+
+// Import auth router
+const express = require('express');
+const supertest = require('supertest');
+const authRouter = require('../routers/auth.router');
+const { verifyToken, verifyUser, AuthUtil } = require('../middleware');
+const { authApiValidator } = require('../validators');
+
+// Create a new Express application for testing
+const testapp = express();
+// Use body parser to extract params in API calls
+testapp.use(express.json());
+testapp.use('/api/auth', authRouter);
+const request = supertest(testapp);
+
+
+describe('Unit tests for auth router', () => {
+ // Clear all mocks after each test
+ afterEach(() => {
+ jest.clearAllMocks();
});
- test('Existing user returns 400', async () => {
- // Test Data
- const userOneWithSameEmail = {
- name: { firstName: 'one', lastName: 'two' },
- email: 'test@test.com',
- };
-
- const userTwoWithSameEmail = {
- name: { firstName: 'three', lastName: 'four' },
- email: 'test@test.com',
- };
-
- await request
- .post('/api/auth/signup')
- .send(userOneWithSameEmail)
- .set(headers);
-
- const res2 = await request
- .post('/api/auth/signup')
- .send(userTwoWithSameEmail)
- .set(headers);
-
- expect(res2.status).toBe(400);
+ // Mocker user for test
+ const mockUser = {
+ id: 1,
+ name: {
+ firstName: 'mock',
+ lastName: 'user',
+ },
+ email: 'mockUser@test.com',
+ accessLevel: 'user',
+ };
+
+ describe('CREATE', () => {
+ it('should sign up new user with POST /api/auth/signup', async () => {
+ // Mock implementation of UserController.createUser
+ UserController.createUser.mockImplementationOnce((req, res) => {
+ res.status(201).send({ message: 'User created successfully' });
+ });
+
+ // Mock POST API call
+ const response = await request.post('/api/auth/signup').send({
+ name: {
+ firstName: mockUser.firstName,
+ lastName: mockUser.lastName,
+ },
+ email: mockUser.email.toLowerCase(),
+ });
+
+ // Tests
+ expect(authApiValidator.validateCreateUserAPICall).toHaveBeenCalled();
+ expect(verifyUser.checkDuplicateEmail).toHaveBeenCalled();
+ expect(UserController.createUser).toHaveBeenCalled();
+ expect(response.status).toBe(201);
+ expect(response.body).toEqual({ message: 'User created successfully' });
+
+ // Marks completion of tests
+ });
+
+ it('should sign in existing user with POST /api/auth/signin', async () => {
+ // Mock implementation for UserController.signin
+ const jsonToken = 'mockedToken';
+ const email = mockUser.email.toLowerCase();
+ const auth_origin = 'web';
+ const cookie = 'mockedCookie';
+ const headers = {
+ origin: 'http://localhost:3000',
+ };
+
+ UserController.signin.mockImplementation((req, res) => {
+ // Set a cookie in the response
+ res.cookie('token', cookie, { httpOnly: true });
+
+ // Set custom headers in the response
+ res.set('origin', headers.origin);
+
+ EmailController.sendLoginLink(
+ req.body.email,
+ req.body.auth_origin,
+ mockUser.name.firstName,
+ jsonToken,
+ cookie,
+ headers.origin,
+ );
+
+ res.status(200).send('Signin successful');
+ });
+
+ // Mock implementation for EmailController.sendLoginLink
+ EmailController.sendLoginLink.mockImplementation(() => {
+ console.log('Mocked EmailController.sendLoginLink called');
+ });
+
+ const response = await request.post('/api/auth/signin').send({
+ email: email,
+ auth_origin: auth_origin,
+ });
+
+ // Tests
+ expect(authApiValidator.validateSigninUserAPICall).toHaveBeenCalled();
+ expect(verifyUser.isAdminByEmail).toHaveBeenCalled();
+ expect(UserController.signin).toHaveBeenCalled();
+ expect(EmailController.sendLoginLink).toHaveBeenCalledWith(
+ email,
+ auth_origin,
+ mockUser.name.firstName,
+ jsonToken,
+ cookie,
+ headers.origin,
+ );
+ expect(response.status).toBe(200);
+ // Verify that the cookie is set
+ expect(response.headers['set-cookie']).toBeDefined();
+ expect(response.headers['set-cookie'][0]).toContain(`token=${cookie}`);
+ expect(response.text).toBe('Signin successful');
+
+ // Marks completion of tests
+ });
+
+ it('should verify sign in with POST /api/auth/verify-signin', async () => {
+ // Mock implementation for UserController.verifySignIn
+ UserController.verifySignIn.mockImplementation((req, res) => {
+ res.status(200).send(mockUser);
+ });
+
+ // Mock POST API call
+ const response = await request.post('/api/auth/verify-signin').send({
+ token: 'mockedToken',
+ });
+
+ // Tests
+ expect(verifyToken.isTokenValid).toHaveBeenCalled();
+ expect(UserController.verifySignIn).toHaveBeenCalled();
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual(mockUser);
+
+ // Marks completion of tests
+ });
+
+ it('should verify me with POST /api/auth/me', async () => {
+ // Mock implementation for UserController.verifyMe
+ UserController.verifyMe.mockImplementation((req, res) => {
+ res.status(200).send(mockUser);
+ });
+
+ // Mock POST API call
+ const response = await request.post('/api/auth/me').send({
+ token: 'mockedToken',
+ });
+
+ // Tests
+ expect(AuthUtil.verifyCookie).toHaveBeenCalled();
+ expect(UserController.verifyMe).toHaveBeenCalled();
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual(mockUser);
+
+ // Marks completion of tests
+ });
+
+ it('should log out with POST /api/auth/logout', async () => {
+ const token = 'token';
+ // Mock implementation for UserController.logout
+ UserController.logout.mockImplementation((req, res) => {
+ res.clearCookie(token);
+ res.status(200).send('Successfully logged out.');
+ });
+
+ // Mock POST API call
+ const response = await request.post('/api/auth/logout').set('Cookie', token);
+
+ // Tests
+ expect(AuthUtil.verifyCookie).toHaveBeenCalled();
+ expect(UserController.logout).toHaveBeenCalled();
+ expect(response.headers['set-cookie'][0]).toMatch(/token=;/);
+ expect(response.status).toBe(200);
+ expect(response.text).toBe('Successfully logged out.');
+
+ // Marks completion of tests
+ });
});
});
-
-describe('SIGNIN User', () => {
- test('User can signin and returns 200', async () => {
- // Create user in DB
- const goodUserData = {
- name: {
- firstName: 'Free',
- lastName: 'Mason',
- },
- email: 'test@test.com',
- accessLevel: 'admin',
- };
- await User.create(goodUserData);
-
- // POST to the DB with that same data.
- const res = await request
- .post('/api/auth/signin')
- .send(goodUserData)
- .set(headers)
- .set('Origin', 'localhost');
-
- expect(res.status).toBe(200);
- });
-});
-
-describe('SIGNIN Validation', () => {
- test('Non admin user returns 401', async () => {
- // Test Data
-
- // Create user in DB
- const notValidPermission = {
- name: {
- firstName: 'Free',
- lastName: 'Mason',
- },
- email: 'test@test.com',
- accessLevel: 'user',
- };
- await User.create(notValidPermission);
-
- // POST to the DB with that same data.
- const res = await request
- .post('/api/auth/signin')
- .send(notValidPermission)
- .set(headers)
- .set('Origin', 'localhost');
-
- expect(res.status).toBe(401);
- });
-
- test('A non-valid email return 403', async () => {
- // Create user in DB
- const notValidEmailPayload = {
- name: {
- firstName: 'Free',
- lastName: 'Mason',
- },
- email: 'test',
- accessLevel: 'admin',
- };
- await User.create(notValidEmailPayload);
-
- // POST to the DB with that same data.
- const res = await request
- .post('/api/auth/signin')
- .send(notValidEmailPayload)
- .set(headers);
-
- expect(res.status).toBe(403);
- const errorMessage = JSON.parse(res.text);
-
- expect(errorMessage.errors).toEqual([
- {
- value: 'test',
- msg: 'Invalid email',
- param: 'email',
- location: 'body',
- },
- ]);
- });
-})
diff --git a/backend/routers/checkIns.router.test.js b/backend/routers/checkIns.router.test.js
new file mode 100644
index 000000000..f7ad72ce0
--- /dev/null
+++ b/backend/routers/checkIns.router.test.js
@@ -0,0 +1,106 @@
+// Mock and import CheckIn model
+jest.mock('../models/checkIn.model');
+const { CheckIn } = require('../models');
+
+// Import the check-ins router
+const express = require('express');
+const supertest = require('supertest');
+const checkInsRouter = require('./checkIns.router');
+
+// Create a new Express application for testing
+const testapp = express();
+// Allows for body parsing
+testapp.use(express.json());
+testapp.use('/api/checkins', checkInsRouter);
+const request = supertest(testapp);
+
+describe('Unit tests for checkIns router', () => {
+ // Mock data for check-ins
+ const mockCheckIns = [
+ { id: 1, eventId: 'event1', userId: 'user1', checkedIn: true, createdDate: String(new Date()) },
+ { id: 2, eventId: 'event2', userId: 'user2', checkedIn: true, createdDate: String(new Date()) },
+ ];
+
+ // Clear mocks after each test
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ describe('READ', () => {
+ it('should return a list of check-ins with GET /api/checkins', async () => {
+ // Mock Mongoose method
+ CheckIn.find.mockResolvedValue(mockCheckIns);
+
+ const response = await request.get('/api/checkins');
+
+ // Tests
+ expect(CheckIn.find).toHaveBeenCalled();
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual(mockCheckIns);
+
+ // Marks completion of test
+ });
+
+ it('should return a single check-in by id with GET /api/checkins/:id', async () => {
+ // Mock Mongoose method
+ CheckIn.findById.mockResolvedValue(mockCheckIns[0]);
+
+ const response = await request.get('/api/checkins/1');
+
+ // Tests
+ expect(CheckIn.findById).toHaveBeenCalledWith('1');
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual(mockCheckIns[0]);
+
+ // Marks completion of test
+ });
+
+ it('should return a list of users who have checked into a specific event with GET /api/checkins/findEvent/:id', async () => {
+ // Mock specific checkIn
+ const mockCheckIn = mockCheckIns[1];
+ const { eventId } = mockCheckIn;
+
+ // Mock Mongoose methods
+ CheckIn.find.mockReturnValue({
+ populate: jest.fn().mockResolvedValue(mockCheckIn),
+ });
+
+ const response = await request.get(`/api/checkins/findEvent/${eventId}`);
+
+ // Tests
+ expect(CheckIn.find).toHaveBeenCalledWith({
+ eventId: eventId,
+ userId: { $ne: 'undefined' },
+ });
+ expect(CheckIn.find().populate).toHaveBeenCalledWith({ path: 'userId', model: 'User' });
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual(mockCheckIn);
+
+ // Marks completion of test
+ });
+ });
+
+ describe('CREATE', () => {
+ it('should create a new check-in with POST /api/checkins', async () => {
+ // Mock new check-in data
+ const newCheckIn = {
+ id: 3,
+ eventId: 'event3',
+ userId: 'user3',
+ checkedIn: true,
+ createdDate: String(new Date()),
+ };
+
+ // Mock create method
+ CheckIn.create.mockResolvedValue(newCheckIn);
+
+ const response = await request.post('/api/checkins').send(newCheckIn);
+
+ // Tests
+ expect(CheckIn.create).toHaveBeenCalledWith(newCheckIn);
+ expect(response.status).toBe(201);
+
+ // Marks completion of test
+ });
+ });
+});
diff --git a/backend/routers/checkUser.router.test.js b/backend/routers/checkUser.router.test.js
new file mode 100644
index 000000000..a7bfe2cf2
--- /dev/null
+++ b/backend/routers/checkUser.router.test.js
@@ -0,0 +1,82 @@
+// Mock and import User Model
+jest.mock('../models/user.model');
+const { User } = require('../models');
+
+// Import checkUser router
+const express = require('express');
+const supertest = require('supertest');
+const checkUserRouter = require('./checkUser.router');
+
+// Create a new Express application for testing
+const testapp = express();
+// express.json() is a body parser needed for POST API requests
+testapp.use(express.json());
+testapp.use('/api/checkuser', checkUserRouter);
+const request = supertest(testapp);
+
+describe('Unit tests for checkUser router', () => {
+ // Mock user for test
+ const id = '123';
+ const mockUser = {
+ id,
+ name: {
+ firstName: 'mock',
+ lastName: 'user',
+ },
+ accessLevel: 'user',
+ skillsToMatch: [],
+ projects: [],
+ textingOk: false,
+ managedProjects: [],
+ isActive: true,
+ email: 'mockuser@gmail.com',
+ currentRole: 'Product Owner',
+ desiredRole: 'Product Owner',
+ newMember: false,
+ firstAttended: 'NOV 2015',
+ createdDate: '2020-01-14T02:14:22.407Z',
+ attendanceReason: 'Civic Engagement',
+ currentProject: 'Undebate',
+ };
+
+ const auth_origin = 'test-origin';
+
+ // Clear all mocks after each test
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ describe('CREATE', () => {
+ it('should authenticate user with POST /api/checkuser', async () => {
+ // Mock Mongoose method
+ User.findOne.mockResolvedValue(mockUser);
+
+ const response = await request
+ .post('/api/checkuser')
+ .send({ email: 'mockuser@gmail.com', auth_origin });
+
+ // Tests
+ expect(User.findOne).toHaveBeenCalledWith({ email: 'mockuser@gmail.com' });
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual({ user: mockUser, auth_origin: auth_origin });
+
+ // Marks completion of tests
+ });
+ });
+
+ describe('READ', () => {
+ it('should return a user by id with GET /api/checkuser/:id', async () => {
+ // Mock Mongoose method
+ User.findById.mockResolvedValue(mockUser);
+
+ const response = await request.get(`/api/checkuser/${id}`);
+
+ // Tests
+ expect(User.findById).toHaveBeenCalledWith(id);
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual(mockUser);
+
+ // Marks completion of tests
+ });
+ });
+});
diff --git a/backend/routers/events.router.js b/backend/routers/events.router.js
index 7614b2012..7a81e1938 100644
--- a/backend/routers/events.router.js
+++ b/backend/routers/events.router.js
@@ -1,4 +1,4 @@
-const express = require("express");
+const express = require('express');
const router = express.Router();
const { Event } = require('../models/event.model');
@@ -13,12 +13,14 @@ router.get('/:EventId', EventController.event_by_id);
router.delete('/:EventId', EventController.destroy);
+router.patch('/batchUpdate', EventController.update);
+
router.patch('/:EventId', EventController.update);
// TODO: Refactor and remove
-router.get("/nexteventbyproject/:id", (req, res) => {
+router.get('/nexteventbyproject/:id', (req, res) => {
Event.find({ project: req.params.id })
- .populate("project")
+ .populate('project')
.then((events) => {
res.status(200).json(events[events.length - 1]);
})
diff --git a/backend/routers/events.router.test.js b/backend/routers/events.router.test.js
index 5c55a32a0..0ae2b9127 100644
--- a/backend/routers/events.router.test.js
+++ b/backend/routers/events.router.test.js
@@ -1,153 +1,240 @@
-const supertest = require("supertest");
-const app = require("../app");
-const CONFIG = require('../config/auth.config');
-
-const request = supertest(app);
-
-const { setupDB } = require("../setup-test");
-setupDB("api-events");
-
-const { Event } = require('../models');
-
-const headers = {};
-headers['x-customrequired-header'] = CONFIG.CUSTOM_REQUEST_HEADER;
-headers.Accept = 'application/json';
-
-// API Tests
-describe('CREATE', () => {
- test('Create Event', async (done) => {
- // Test Data
- const submittedData = {
- name: 'eventName',
- };
-
- // Submit an event
- const res = await request
- .post('/api/events/')
- .set(headers)
- .send(submittedData);
- expect(res.status).toBe(201);
-
- // Retrieve that event
- const databaseEventQuery = await Event.find();
- const databaseEvent = databaseEventQuery[0];
- expect(databaseEventQuery.length).toBeGreaterThanOrEqual(1);
- expect(databaseEvent.name).toBe(submittedData.name);
- done();
+const express = require('express');
+
+const supertest = require('supertest');
+// Mock the Mongoose Event model
+jest.mock('../models/event.model', () => ({
+ Event: {
+ find: jest.fn(),
+ },
+}));
+
+const { Event } = require('../models/event.model');
+//Mock the EventController to isolate router tests router tests fro controller logic
+jest.mock('../controllers', () => ({
+ EventController: {
+ event_list: jest.fn(),
+
+ create: jest.fn(),
+
+ event_by_id: jest.fn(),
+
+ destroy: jest.fn(),
+
+ update: jest.fn(),
+ },
+}));
+
+const { EventController } = require('../controllers');
+
+const eventsRouter = require('./events.router');
+//Setup a test application
+const testapp = express();
+
+testapp.use(express.json());
+
+testapp.use(express.urlencoded({ extended: false }));
+
+testapp.use('/api/events', eventsRouter);
+
+const request = supertest(testapp);
+
+describe('Unit Tests for events.router.js', () => {
+ //Mock data
+ const mockEvent = {
+ _id: 'event123',
+
+ name: 'Test Event',
+
+ project: 'projectABC',
+
+ date: '2025-01-01T10:00:00Z',
+ };
+
+ const mockEventId = 'event123';
+
+ const mockProjectId = 'projectABC';
+
+ const mockUpdatedEventData = {
+ name: 'Updated Test Event Name',
+ };
+
+ afterEach(() => {
+ jest.clearAllMocks();
});
-});
+ // Test suite for GET /api/events (event_list)
+
+ describe('GET /api/events (event_list)', () => {
+ it('should call EventController.event_list and return a list of events', async () => {
+ EventController.event_list.mockImplementationOnce((req, res) =>
+ res.status(200).send([mockEvent]),
+ );
+
+ const response = await request.get('/api/events');
+
+ expect(EventController.event_list).toHaveBeenCalledWith(
+ expect.anything(),
+
+ expect.anything(),
-describe('READ', () => {
- test('GET Events list', async (done) => {
- // Test Data
- const submittedData = {
- createdDate: '2020-05-20T21:16:44.498Z',
- checkinReady: true,
- };
-
- // Add an event with a project using the API.
- const res = await request.post("/api/events").send(submittedData).set(headers);
-
- // Retrieve and compare the the Event values using the DB.
- const databaseEventQuery = await Event.find();
- const databaseEvent = databaseEventQuery[0];
- expect(databaseEventQuery.length).toBeGreaterThanOrEqual(1);
- expect(databaseEvent.createdDate).toStrictEqual(new Date(submittedData.createdDate));
-
- // Retrieve and compare the the values using the API.
- const response = await request.get('/api/events/').set(headers);
- expect(response.statusCode).toBe(200);
- const APIData = response.body[0];
- expect(APIData.createdDate).toBe(submittedData.createdDate);
- done();
+ expect.anything(),
+ );
+
+ expect(response.status).toBe(200);
+
+ expect(response.body).toEqual([mockEvent]);
+
+ });
});
- test('GET Event by ID', async (done) => {
- // Test Data
- const submittedData = {
- name: 'eventName',
- location: {
- // should we include address here?
- city: 'Los Angeles',
- state: 'California',
- country: 'USA',
- },
- hacknight: 'Online', // DTLA, Westside, South LA, Online
- eventType: 'Workshop', // Project Meeting, Orientation, Workshop
- description: 'A workshop to do stuff',
- date: 1594023390039,
- startTime: 1594023390039, // start date and time of the event
- endTime: 1594023390039, // end date and time of the event
- hours: 2, // length of the event in hours
- createdDate: 1594023390039, // date/time event was created
- updatedDate: 1594023390039, // date/time event was last updated
- checkInReady: false, // is the event open for check-ins?
- owner: {
- ownerId: 33, // id of user who created event
- },
- };
-
- // Create Event by DB
- const dbCreatedevent = await Event.create(submittedData);
- const dbCreatedeventId = dbCreatedevent.id;
- const dbCreatedEventIdURL = `/api/events/${dbCreatedeventId}`;
-
- // Retrieve and compare the the values using the API.
- const response = await request.get(dbCreatedEventIdURL).set(headers);
- expect(response.statusCode).toBe(200);
- const apiRetrievedEvent = await response.body;
- expect(apiRetrievedEvent._id).toBe(dbCreatedeventId);
-
- done();
+ // Test suite for POST /api/events (create)
+ describe('POST /api/events (create)', () => {
+ it('should call EventController.create and return the created event', async () => {
+ EventController.create.mockImplementationOnce((req, res) => res.status(201).send(mockEvent));
+
+ const newEventData = {
+ name: mockEvent.name,
+
+ project: mockEvent.project,
+
+ date: mockEvent.date,
+ };
+
+ const response = await request.post('/api/events/').send(newEventData);
+
+ expect(EventController.create).toHaveBeenCalledWith(
+ expect.objectContaining({ body: newEventData }),
+
+ expect.anything(),
+
+ expect.anything(),
+ );
+
+ expect(response.status).toBe(201);
+
+ expect(response.body).toEqual(mockEvent);
+
+ });
});
-});
+ // Test suite for GET /api/events/:EventId (event_by_id)
+ describe('GET /api/events/:EventId (event_by_id)', () => {
+ it('should call EventController.event_by_id and return a specific event', async () => {
+ EventController.event_by_id.mockImplementationOnce((req, res) =>
+ res.status(200).send(mockEvent),
+ );
+
+ const response = await request.get(`/api/events/${mockEventId}`);
-describe('UPDATE', () => {
- test('Update Event by ID with PATCH', async (done) => {
- // Test Data
- const submittedData = {
- name: 'originalEventName',
- };
-
- // Submit an event
- const res = await request
- .post('/api/events/')
- .set(headers)
- .send(submittedData);
- expect(res.status).toBe(201);
-
- const updatedDataPayload = {
- name: 'updateEventName',
- };
-
- // Update the event
- const res2 = await request
- .patch(`/api/events/${res.body._id}`)
- .set(headers)
- .send(updatedDataPayload);
- expect(res2.status).toBe(200);
-
- done();
+ expect(EventController.event_by_id).toHaveBeenCalledWith(
+ expect.objectContaining({ params: { EventId: mockEventId } }),
+
+ expect.anything(),
+
+ expect.anything(),
+ );
+
+ expect(response.status).toBe(200);
+
+ expect(response.body).toEqual(mockEvent);
+
+ });
});
-});
+ // Test suite for DELETE /api/events/:EventId (destroy)
+ describe('DELETE /api/events/:EventId (destroy)', () => {
+ it('should call EventController.destroy and return 204 No Content', async () => {
+ EventController.destroy.mockImplementationOnce((req, res) => res.status(204).send());
+
+ const response = await request.delete(`/api/events/${mockEventId}`);
+
+ expect(EventController.destroy).toHaveBeenCalledWith(
+ expect.objectContaining({ params: { EventId: mockEventId } }),
+
+ expect.anything(),
+
+ expect.anything(),
+ );
+
+ expect(response.status).toBe(204);
+
+ expect(response.body).toEqual({});
+
+ });
+ });
+ // Test suite for PATCH /api/events/:EventId (update)
+ describe('PATCH /api/events/:EventId (update)', () => {
+ it('should call EventController.update and return the updated event', async () => {
+ EventController.update.mockImplementationOnce((req, res) =>
+ res.status(200).send({ ...mockEvent, ...mockUpdatedEventData }),
+ );
+
+ const response = await request.patch(`/api/events/${mockEventId}`).send(mockUpdatedEventData);
+
+ expect(EventController.update).toHaveBeenCalledWith(
+ expect.objectContaining({
+ params: { EventId: mockEventId },
+
+ body: mockUpdatedEventData,
+ }),
+
+ expect.anything(),
+
+ expect.anything(),
+ );
+
+ expect(response.status).toBe(200);
+
+ expect(response.body).toEqual({ ...mockEvent, ...mockUpdatedEventData });
+
+ });
+ });
+ // Test suite for GET /api/events/nexteventbyproject/:id
+ describe('GET /api/events/nexteventbyproject/:id', () => {
+ it('should return the last event for a given project ID directly from the router', async () => {
+ const mockEventsForProject = [
+ { _id: 'eventA', project: mockProjectId, name: 'Event A' },
+
+ { _id: 'eventB', project: mockProjectId, name: 'Event B' },
+
+ { _id: 'eventC', project: mockProjectId, name: 'Event C' },
+ ];
+
+ Event.find.mockImplementationOnce(() => ({
+ populate: jest.fn().mockReturnThis(),
+
+ then: jest.fn(function (callback) {
+ return Promise.resolve(callback(mockEventsForProject));
+ }),
+
+ catch: jest.fn(),
+ }));
+
+ const response = await request.get(`/api/events/nexteventbyproject/${mockProjectId}`);
+
+ expect(Event.find).toHaveBeenCalledWith({ project: mockProjectId });
+
+ expect(Event.find.mock.results[0].value.populate).toHaveBeenCalledWith('project');
+
+ expect(response.status).toBe(200);
+
+ expect(response.body).toEqual(mockEventsForProject[mockEventsForProject.length - 1]);
+
+ });
+ // Test case for error handling when fetching next event by project
+ it('should return 500 if an error occurs when fetching next event by project', async () => {
+ const mockError = new Error('Simulated database error for next event by project');
+
+ Event.find.mockImplementationOnce(() => ({
+ populate: jest.fn().mockReturnThis(),
+
+ then: jest.fn(() => Promise.reject(mockError)),
+
+ catch: jest.fn(function (callback) {
+ return Promise.resolve(callback(mockError));
+ }),
+ }));
+
+ const response = await request.get(`/api/events/nexteventbyproject/${mockProjectId}`);
+
+ expect(response.status).toBe(500);
-describe('DELETE', () => {
- test('Delete Event by ID with DELETE', async (done) => {
- // Test Data
- const submittedData = {
- name: 'eventName',
- };
-
- // Submit an event
- const res = await request
- .post('/api/events/')
- .set(headers)
- .send(submittedData);
- expect(res.status).toBe(201);
-
- // Delete the event
- const res2 = await request.delete(`/api/events/${res.body._id}/`).set(headers);
- expect(res2.status).toBe(200);
-
- done();
+ });
});
});
diff --git a/backend/routers/grantpermission.router.js b/backend/routers/grantpermission.router.js
index afefa6f4f..5f8858650 100644
--- a/backend/routers/grantpermission.router.js
+++ b/backend/routers/grantpermission.router.js
@@ -1,18 +1,21 @@
-const express = require("express");
+const express = require('express');
const router = express.Router();
-const fs = require("fs");
+const fs = require('fs');
-const { google } = require("googleapis");
+const { google } = require('googleapis');
const async = require('async');
-const fetch = require("node-fetch");
+const fetch = require('node-fetch');
+const { authUser } = require('../middleware/auth.middleware');
+const AuthUtils = require('../../shared/authorizationUtils');
+const { ROLES } = require('../../shared/roles');
-const SCOPES = ["https://www.googleapis.com/auth/drive"];
+const SCOPES = ['https://www.googleapis.com/auth/drive'];
// placeholder org for testing
-const githubOrganization = "testvrms";
+const githubOrganization = 'testvrms';
// GET /api/grantpermission/googleDrive
-router.post("/googleDrive", async (req, res) => {
+router.post('/googleDrive', async (req, res) => {
let credentials = JSON.parse(process.env.GOOGLECREDENTIALS);
//checks if email and file to change are in req.body
@@ -22,12 +25,8 @@ router.post("/googleDrive", async (req, res) => {
const { client_secret, client_id, redirect_uris } = credentials;
- const oAuth2Client = new google.auth.OAuth2(
- client_id,
- client_secret,
- redirect_uris[1]
- );
- console.log("AFTERCLIENT");
+ const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uris[1]);
+ console.log('AFTERCLIENT');
// if (err)
// return res.status(500).send({
// message: "Error loading client secret file:" + err.message,
@@ -36,27 +35,24 @@ router.post("/googleDrive", async (req, res) => {
const tokenObject = {
access_token: process.env.GOOGLE_ACCESS_TOKEN,
refresh_token: process.env.GOOGLE_REFRESH_TOKEN,
- scope: "https://www.googleapis.com/auth/drive",
- token_type: "Bearer",
+ scope: 'https://www.googleapis.com/auth/drive',
+ token_type: 'Bearer',
expiry_date: process.env.GOOGLE_EXPIRY_DATE,
};
oAuth2Client.setCredentials(tokenObject);
- console.log("AFTR OAUTH");
+ console.log('AFTR OAUTH');
// sends google drive grant permission from VRMS to email
try {
- const result = await grantPermission(
- oAuth2Client,
- req.body.email,
- req.body.file
- );
+ const result = await grantPermission(oAuth2Client, req.body.email, req.body.file);
if (result.success) {
- const successObject = { message: "Success!" };
+ const successObject = { message: 'Success!' };
return res.status(200).send(successObject);
} else {
return res.sendStatus(400);
}
} catch (err) {
+ console.error(err.message);
return res.sendStatus(500);
}
});
@@ -64,20 +60,21 @@ router.post("/googleDrive", async (req, res) => {
// GET /api/grantpermission/gitHub (checks if it can update the db data)
// Route accounts for onboaring admins or regular users
-router.post("/gitHub", async (req, res) => {
- const { teamName, accessLevel, handle } = req.body;
+router.post('/gitHub', authUser, async (req, res) => {
+ const { teamName, handle } = req.body;
const userHandle = handle;
const baseTeamSlug = createSlug(teamName);
- const managerTeamSlug = baseTeamSlug + "-managers";
- const adminTeamSlug = baseTeamSlug + "-admins";
+ const managerTeamSlug = baseTeamSlug + '-managers';
+ const adminTeamSlug = baseTeamSlug + '-admins';
const teamSlugs = [baseTeamSlug, managerTeamSlug];
- if (accessLevel === "admin") teamSlugs.push(adminTeamSlug);
-
+ if (AuthUtils.hasMinimumRole(req.user, ROLES.ADMIN)) {
+ teamSlugs.push(adminTeamSlug);
+ }
function createSlug(string) {
let slug = string.toLowerCase();
- return slug.split(" ").join("-");
+ return slug.split(' ').join('-');
}
try {
@@ -85,7 +82,7 @@ router.post("/gitHub", async (req, res) => {
const userStatus = await checkOrgMembershipStatus(userHandle);
const orgMembershipStatus = userStatus
? userStatus
- : (await inviteToOrg(userHandle)) && "pending";
+ : (await inviteToOrg(userHandle)) && 'pending';
// Add user to github project teams
console.log({ teamSlugs });
@@ -93,24 +90,24 @@ router.post("/gitHub", async (req, res) => {
teamSlugs.map(async (slug) => {
const result = await addToTeam(userHandle, slug);
console.log({ slug });
- if (result === "team not found") {
- throw new Error("team not found");
+ if (result === 'team not found') {
+ throw new Error('team not found');
}
if (!result) {
- throw new Error("user not added to one or more teams");
+ throw new Error('user not added to one or more teams');
}
return;
- })
+ }),
);
const result = {
orgMembershipStatus,
- teamMembershipStatus: "pending",
+ teamMembershipStatus: 'pending',
};
- if (orgMembershipStatus === "active") {
+ if (orgMembershipStatus === 'active') {
// user automatically added to team if active membership in org
- result.teamMembershipStatus = "active";
+ result.teamMembershipStatus = 'active';
// check if membership is public
result.publicMembership = await checkPublicMembership(userHandle);
@@ -121,19 +118,16 @@ router.post("/gitHub", async (req, res) => {
return res.status(200).send(result);
} catch (err) {
- return res.status(400);
+ console.error(err.message);
+ return res.status(400).send({ message: 'Error occurred while processing request' });
}
});
-router.post("/", async (req, res) => {
- fs.readFile("credentials.json", async (err, content) => {
+router.post('/', async (req, res) => {
+ fs.readFile('credentials.json', async (err, content) => {
const credentialsObject = JSON.parse(content);
const { client_secret, client_id, redirect_uris } = credentialsObject.web;
- const oAuth2Client = new google.auth.OAuth2(
- client_id,
- client_secret,
- redirect_uris[1]
- );
+ const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uris[1]);
// sends back error if credentials files cannot be read
if (err) {
@@ -155,6 +149,7 @@ router.post("/", async (req, res) => {
setToken = true;
}
} catch (err) {
+ console.error(err.message);
return res.sendStatus(400);
}
// if token is already placed into body request
@@ -165,15 +160,11 @@ router.post("/", async (req, res) => {
oAuth2Client.setCredentials(token);
try {
// another callback function that returns promises can replace this method
- console.log("TRY");
- const result = await grantPermission(
- oAuth2Client,
- req.body.email,
- req.body.file
- );
+ console.log('TRY');
+ const result = await grantPermission(oAuth2Client, req.body.email, req.body.file);
if (result.success) {
{
- const successObject = { message: "Success!" };
+ const successObject = { message: 'Success!' };
if (setToken) {
successObject.token = token;
}
@@ -201,7 +192,7 @@ router.post("/", async (req, res) => {
*/
function sendURL(oAuth2Client) {
const authUrl = oAuth2Client.generateAuthUrl({
- access_type: "offline",
+ access_type: 'offline',
scope: SCOPES,
});
return authUrl;
@@ -219,7 +210,7 @@ function sendToken(oAuth2Client, code) {
if (err)
reject({
success: false,
- message: "Error retrieving access token" + err.message,
+ message: 'Error retrieving access token' + err.message,
});
resolve({ success: true, token });
});
@@ -232,37 +223,37 @@ function sendToken(oAuth2Client, code) {
* @returns {Promise} Promise with an object that contains the boolean success to determine
* what to do in the route. Rejection objects also have a message field.
*/
-function listFiles(auth) {
- const drive = google.drive({ version: "v3", auth });
- return new Promise(function (resolve, reject) {
- drive.files.list(
- {
- pageSize: 10,
- fields: "nextPageToken, files(id, name)",
- },
- (err, res) => {
- if (err)
- reject({
- success: false,
- message: "The API returned an error: " + err.message,
- });
- const files = res.data.files;
- if (files.length) {
- console.log("Files:");
- files.map((file) => {
- console.log(`${file.name} (${file.id})`);
- });
- resolve({ success: true });
- } else {
- return reject({
- success: false,
- message: "No files found",
- });
- }
- }
- );
- });
-}
+// function listFiles(auth) {
+// const drive = google.drive({ version: 'v3', auth });
+// return new Promise(function (resolve, reject) {
+// drive.files.list(
+// {
+// pageSize: 10,
+// fields: 'nextPageToken, files(id, name)',
+// },
+// (err, res) => {
+// if (err)
+// reject({
+// success: false,
+// message: 'The API returned an error: ' + err.message,
+// });
+// const files = res.data.files;
+// if (files.length) {
+// console.log('Files:');
+// files.map((file) => {
+// console.log(`${file.name} (${file.id})`);
+// });
+// resolve({ success: true });
+// } else {
+// return reject({
+// success: false,
+// message: 'No files found',
+// });
+// }
+// },
+// );
+// });
+// }
/**
* Gives Google Drive permission to an email address for the file ID
@@ -273,39 +264,38 @@ function listFiles(auth) {
* what to do in the route. Rejection objects also have a message field.
*/
function grantPermission(auth, email, fileId) {
- console.log("GRANT PERMISSIONS");
+ console.log('GRANT PERMISSIONS');
var permissions = [
{
- type: "user",
- role: "writer",
+ type: 'user',
+ role: 'writer',
emailAddress: email,
},
];
return new Promise(function (resolve, reject) {
async.eachSeries(permissions, function (permission, permissionCallback) {
- const drive = google.drive({ version: "v3", auth });
+ const drive = google.drive({ version: 'v3', auth });
drive.permissions.create(
{
resource: permission,
fileId: fileId,
- fields: "id",
- emailMessage:
- "Hi there! You are receiving this message from the VRMS team. Enjoy!",
+ fields: 'id',
+ emailMessage: 'Hi there! You are receiving this message from the VRMS team. Enjoy!',
},
(err, res) => {
if (err) {
- console.log("PROMISE ERROR", err);
+ console.log('PROMISE ERROR', err);
reject({
success: false,
- message: "The API returned an error: " + err.message,
+ message: 'The API returned an error: ' + err.message,
});
} else {
- console.log("RES", res);
+ console.log('RES', res);
permissionCallback();
resolve({ success: true });
}
- }
+ },
);
});
});
@@ -317,24 +307,21 @@ function grantPermission(auth, email, fileId) {
* @param {str} githubHandle
*/
function checkOrgMembershipStatus(githubHandle) {
- return fetch(
- `https://api.github.com/orgs/${githubOrganization}/memberships/${githubHandle}`,
- {
- method: "GET",
- headers: {
- Authorization: `token ${process.env.GITHUB_TOKEN}`,
- },
- }
- )
+ return fetch(`https://api.github.com/orgs/${githubOrganization}/memberships/${githubHandle}`, {
+ method: 'GET',
+ headers: {
+ Authorization: `token ${process.env.GITHUB_TOKEN}`,
+ },
+ })
.then((res) => {
if (res.status === 200) return res.json();
if (res.status === 404) return false;
- return new Error("Unexpected result");
+ return new Error('Unexpected result');
})
.then((res) => {
if (res) {
- return res.state === "pending" ? "pending" : "active";
+ return res.state === 'pending' ? 'pending' : 'active';
} else {
return false;
}
@@ -349,14 +336,12 @@ function inviteToOrg(githubHandle) {
return fetch(
`https://api.github.com/orgs/${githubOrganization}/memberships/${githubHandle}?role=member`,
{
- method: "PUT",
+ method: 'PUT',
headers: {
Authorization: `token ${process.env.GITHUB_TOKEN}`,
},
- }
- ).then((res) =>
- res.status === 200 ? true : new Error("Unexpected response")
- );
+ },
+ ).then((res) => (res.status === 200 ? true : new Error('Unexpected response')));
}
/**
@@ -371,19 +356,19 @@ function addToTeam(githubHandle, teamSlug) {
return fetch(
`https://api.github.com/orgs/${githubOrganization}/teams/${teamSlug}/memberships/${githubHandle}`,
{
- method: "PUT",
+ method: 'PUT',
headers: {
Authorization: `token ${process.env.GITHUB_TOKEN}`,
},
- }
+ },
)
.then((res) => ({
result: res.json(),
status: res.status,
}))
.then((res) => {
- if (res.result.message === "Not Found") {
- return "team not found"; // how can I just throw an error here instead?
+ if (res.result.message === 'Not Found') {
+ return 'team not found'; // how can I just throw an error here instead?
} else {
console.log(res.status);
return Boolean(res.status === 200);
@@ -393,12 +378,10 @@ function addToTeam(githubHandle, teamSlug) {
function check2FA(githubHandle) {
return fetch(
- `https://api.github.com/orgs/${githubOrganization}/members?filter=2fa_disabled`
+ `https://api.github.com/orgs/${githubOrganization}/members?filter=2fa_disabled`,
).then((no2FAMembersArr) => {
if (no2FAMembersArr.length) {
- return !no2FAMembersArr.includes(
- (member) => member.login === githubHandle
- );
+ return !no2FAMembersArr.includes((member) => member.login === githubHandle);
}
return true;
@@ -407,7 +390,7 @@ function check2FA(githubHandle) {
function checkPublicMembership(githubHandle) {
return fetch(
- `https://api.github.com/orgs/${githubOrganization}/public_members/${githubHandle}`
+ `https://api.github.com/orgs/${githubOrganization}/public_members/${githubHandle}`,
).then((res) => (res.status === 204 ? true : false));
}
diff --git a/backend/routers/healthCheck.router.test.js b/backend/routers/healthCheck.router.test.js
new file mode 100644
index 000000000..3b405f90f
--- /dev/null
+++ b/backend/routers/healthCheck.router.test.js
@@ -0,0 +1,39 @@
+// Mock the healthCheck controller
+jest.mock('../controllers/healthCheck.controller.js');
+
+// Import the healthCheck router and controller
+const healthCheck = require('./healthCheck.router.js');
+const { HealthCheckController } = require('../controllers');
+
+// Create a mock test server
+const express = require('express');
+const supertest = require('supertest');
+const testapp = express();
+testapp.use('/api/healthcheck', healthCheck);
+
+// Set up mock request for the controller
+const request = supertest(testapp);
+
+describe('Unit testing for Health Check Router', () => {
+ // Clear all mocks after each test
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ describe('READ', () => {
+ it('should return status code 200 and message "I\'m Alive" with GET /api/healthcheck', async () => {
+ // Mock the controller method
+ HealthCheckController.isAlive.mockImplementationOnce((req, res) => {
+ res.status(200).send("I'm Alive!");
+ });
+
+ // Call GET API endpoint
+ const response = await request.get('/api/healthcheck');
+
+ // Test
+ expect(HealthCheckController.isAlive).toHaveBeenCalled();
+ expect(response.status).toBe(200);
+ expect(response.text).toBe("I'm Alive!");
+ });
+ });
+});
diff --git a/backend/routers/projectTeamMembers.router.js b/backend/routers/projectTeamMembers.router.js
index 8d5c28425..a06926a57 100644
--- a/backend/routers/projectTeamMembers.router.js
+++ b/backend/routers/projectTeamMembers.router.js
@@ -1,12 +1,12 @@
-const express = require("express");
+const express = require('express');
const router = express.Router();
const { ProjectTeamMember } = require('../models/projectTeamMember.model');
// GET /api/projectteammembers/
-router.get("/", (req, res) => {
+router.get('/', (req, res) => {
ProjectTeamMember.find()
- .populate("userId")
+ .populate('userId')
.then((teamMembers) => {
return res.status(200).send(teamMembers);
})
@@ -16,9 +16,9 @@ router.get("/", (req, res) => {
});
});
-router.get("/:id", (req, res) => {
+router.get('/:id', (req, res) => {
ProjectTeamMember.find({ projectId: req.params.id })
- .populate("userId")
+ .populate('userId')
.then((teamMembers) => {
return res.status(200).send(teamMembers);
})
@@ -28,14 +28,14 @@ router.get("/:id", (req, res) => {
});
});
-router.get("/project/:id/:userId", (req, res) => {
+router.get('/project/:id/:userId', (req, res) => {
ProjectTeamMember.find({
projectId: req.params.id,
userId: req.params.userId,
})
- .populate("userId")
+ .populate('userId')
.then((teamMember) => {
- if (!teamMember.length) {
+ if (!teamMember || teamMember.length === 0) {
return res.sendStatus(400);
} else {
return res.status(200).send(teamMember);
@@ -47,12 +47,12 @@ router.get("/project/:id/:userId", (req, res) => {
});
});
-router.get("/projectowner/:id", (req, res) => {
+router.get('/projectowner/:id', (req, res) => {
const id = req.params.id;
ProjectTeamMember.findOne({ userId: id })
- .populate("userId")
- .populate("projectId")
+ .populate('userId')
+ .populate('projectId')
.then((teamMember) => {
teamMember.vrmsProjectAdmin === true
? res.status(200).send(teamMember)
@@ -63,7 +63,7 @@ router.get("/projectowner/:id", (req, res) => {
});
});
-router.post("/", (req, res) => {
+router.post('/', (req, res) => {
ProjectTeamMember.create(req.body)
.then((teamMember) => {
return res.status(201).send(teamMember);
@@ -74,11 +74,10 @@ router.post("/", (req, res) => {
});
});
-router.patch("/:id", (req, res) => {
+router.patch('/:id', (req, res) => {
ProjectTeamMember.findByIdAndUpdate(req.params.id, req.body)
.then((edit) => res.json(edit))
- .catch((err) =>
- res.sendStatus(400));
+ .catch(() => res.sendStatus(400));
// };
});
diff --git a/backend/routers/projectTeamMembers.router.test.js b/backend/routers/projectTeamMembers.router.test.js
new file mode 100644
index 000000000..f6107a2d5
--- /dev/null
+++ b/backend/routers/projectTeamMembers.router.test.js
@@ -0,0 +1,257 @@
+// Mock and import project team model
+jest.mock('../models/projectTeamMember.model');
+const { ProjectTeamMember } = require('../models');
+
+// Import project team router
+const ProjectTeamRouter = require('./projectTeamMembers.router');
+
+// Create test app with express
+const express = require('express');
+const supertest = require('supertest');
+const testapp = express();
+// Allow for body parsing in test
+testapp.use(express.json());
+testapp.use('/api/projectteammembers', ProjectTeamRouter);
+const request = supertest(testapp);
+
+describe('Unit testing for project team members router', () => {
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ describe('READ', () => {
+ // Mock project team members
+ const mockMembers = [
+ {
+ id: 1,
+ userId: '1',
+ projectId: '1',
+ teamMemberStatus: 'active',
+ vrmsProjectAdmin: false,
+ roleOnProject: 'Developer',
+ joinedDate: Date.now(),
+ leftDate: Date.now(),
+ leftReason: 'n/a',
+ githubPermissionLevel: 'admin',
+ onProjectGithub: true,
+ onProjectGoogleDrive: true,
+ },
+ {
+ id: 2,
+ userId: '2',
+ projectId: '2',
+ teamMemberStatus: 'active',
+ vrmsProjectAdmin: true,
+ roleOnProject: 'Project Manager',
+ joinedDate: Date.now(),
+ leftDate: Date.now(),
+ leftReason: 'n/a',
+ githubPermissionLevel: 'admin',
+ onProjectGithub: true,
+ onProjectGoogleDrive: true,
+ },
+ ];
+
+ it('should return a list of project team members with GET /api/projectteammembers/', async () => {
+ // Mock resolved value of ProjectTeamMember.find() method
+ ProjectTeamMember.find.mockReturnValue({
+ populate: jest.fn((path) => {
+ if (path !== 'userId') throw new Error('Incorrect populate path');
+ return {
+ then: (callBack) => callBack(mockMembers),
+ };
+ }),
+ });
+
+ // Mock API response
+ const response = await request.get('/api/projectteammembers');
+
+ // Tests
+ expect(ProjectTeamMember.find().populate).toHaveBeenCalledWith('userId');
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual(mockMembers);
+
+ // Marks completion of tests
+ });
+
+ it('should return a single project team member based on projectId with GET /api/projectteammembers/:id', async () => {
+ const mockMember = mockMembers[0];
+ const projectId = mockMember.projectId;
+
+ // Mock resolved value of ProjectTeamMember.find({ projectId }) method
+ ProjectTeamMember.find.mockReturnValue({
+ populate: jest.fn((path) => {
+ if (path !== 'userId') throw new Error('Incorrect populate path');
+ return {
+ then: (callBack) => callBack(mockMember),
+ };
+ }),
+ });
+
+ // Mock GET API response
+ const response = await request.get(`/api/projectteammembers/${projectId}`);
+
+ // Tests
+ expect(ProjectTeamMember.find).toHaveBeenCalledWith({ projectId: projectId });
+ expect(ProjectTeamMember.find().populate).toHaveBeenCalledWith('userId');
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual(mockMember);
+
+ // Marks completion of tests
+ });
+
+ it('should return a specific project team member based on projectId and userId with GET /api/projectteammembers/project/:id/:userId', async () => {
+ const mockMember = mockMembers[1];
+ const projectId = mockMember.projectId;
+ const userId = mockMember.userId;
+
+ // Mock resolved value of ProjectTeamMember.find({ projectId, userId }) method
+ ProjectTeamMember.find.mockReturnValue({
+ populate: jest.fn((path) => {
+ if (path !== 'userId') throw new Error('Incorrect populate path');
+ return {
+ then: (callBack) => callBack(mockMember),
+ };
+ }),
+ });
+
+ // Mock GET API response
+ const response = await request.get(`/api/projectteammembers/project/${projectId}/${userId}`);
+
+ // Tests
+ expect(ProjectTeamMember.find).toHaveBeenCalledWith({ projectId: projectId, userId: userId });
+ expect(ProjectTeamMember.find().populate).toHaveBeenCalledWith('userId');
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual(mockMember);
+
+ // Marks completion of tests
+ });
+
+ it('should return a specific project owner that IS an vrmsProjectAdmin on userId with GET /api/projectteammembers/projectowner/:id', async () => {
+ const mockMember = mockMembers[1];
+ const userId = mockMember.userId;
+
+ // Mock resolved value of ProjectTeamMember.findOne() method
+ ProjectTeamMember.findOne.mockImplementation(() => ({
+ populate: jest.fn().mockImplementationOnce((path) => {
+ if (path !== 'userId') throw new Error('Incorrect first populate path');
+ return {
+ populate: jest.fn().mockImplementationOnce((path) => {
+ if (path !== 'projectId') throw new Error('Incorrect second populate path');
+ return {
+ then: (callback) => callback(mockMember),
+ };
+ }),
+ };
+ }),
+ }));
+
+ // Mock GET API response
+ const response = await request.get(`/api/projectteammembers/projectowner/${userId}`);
+
+ // Tests
+ expect(ProjectTeamMember.findOne).toHaveBeenCalledWith({ userId: userId });
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual(mockMember);
+ expect(response.body.vrmsProjectAdmin).toBe(true);
+
+ // Marks completion of tests
+ });
+
+ it('should return a project team member that IS NOT a vrmsProjectAdmin based on userId with GET /api/projectteammembers/projectowner/:id', async () => {
+ const mockMember = mockMembers[0];
+ const userId = mockMember.userId;
+
+ // Mock resolved value of ProjectTeamMember.findOne() method
+ ProjectTeamMember.findOne.mockImplementation(() => ({
+ populate: jest.fn().mockImplementationOnce((path) => {
+ if (path !== 'userId') throw new Error('Incorrect first populate path');
+ return {
+ populate: jest.fn().mockImplementationOnce((path) => {
+ if (path !== 'projectId') throw new Error('Incorrect second populate path');
+ return {
+ then: (callback) => callback(mockMember),
+ };
+ }),
+ };
+ }),
+ }));
+
+ // Mock GET API response
+ const response = await request.get(`/api/projectteammembers/projectowner/${userId}`);
+
+ // Tests
+ expect(ProjectTeamMember.findOne).toHaveBeenCalledWith({ userId: userId });
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual(false);
+
+ // Marks completion of tests
+ });
+ });
+
+ describe('CREATE', () => {
+ // New mock member
+ const newMember = {
+ id: 3,
+ userId: '3',
+ projectId: '3',
+ teamMemberStatus: 'active',
+ vrmsProjectAdmin: true,
+ roleOnProject: 'UX',
+ joinedDate: Date.now(),
+ leftDate: Date.now(),
+ leftReason: 'project completed',
+ githubPermissionLevel: 'Read',
+ onProjectGithub: true,
+ onProjectGoogleDrive: true,
+ };
+
+ it('should create and return a new project team member with POST /api/projectteammember/', async () => {
+ // Mock ProjectTeamMember.create() method
+ ProjectTeamMember.create.mockResolvedValue(newMember);
+
+ // Mock POST API response
+ const response = await request.post('/api/projectteammembers/').send(newMember);
+
+ // Tests
+ expect(response.status).toBe(201);
+ expect(response.body).toEqual(newMember);
+
+ // Marks completion of tests
+ });
+ });
+
+ describe('UPDATE', () => {
+ // Updated mock member
+ const updatedMember = {
+ id: '3',
+ userId: '1',
+ projectId: '2',
+ teamMemberStatus: 'active',
+ vrmsProjectAdmin: true,
+ roleOnProject: 'Data Science',
+ joinedDate: Date.now(),
+ leftDate: Date.now(),
+ leftReason: 'project paused',
+ githubPermissionLevel: 'Triage',
+ onProjectGithub: true,
+ onProjectGoogleDrive: false,
+ };
+ const id = updatedMember.id;
+
+ it('should update the updated project team member with PATCH /api/projectteammember/:id', async () => {
+ // Mock ProjectTeamMember.create() method
+ ProjectTeamMember.findByIdAndUpdate.mockResolvedValue(updatedMember);
+
+ // Call PATCH API response
+ const response = await request.patch(`/api/projectteammembers/${id}`).send(updatedMember);
+
+ // Tests
+ expect(ProjectTeamMember.findByIdAndUpdate).toHaveBeenCalledWith(id, updatedMember);
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual(updatedMember);
+
+ // Marks completion of tests
+ });
+ });
+});
diff --git a/backend/routers/projects.router.js b/backend/routers/projects.router.js
index 8991000cb..336057df6 100644
--- a/backend/routers/projects.router.js
+++ b/backend/routers/projects.router.js
@@ -1,16 +1,38 @@
-const express = require("express");
+const express = require('express');
const router = express.Router();
const { ProjectController } = require('../controllers');
+// const { Auth } = require('../middleware');
+// const { ROLES } = require('../../shared/roles');
+// Require user to be project manager or higher (commented out for now for current app to work succesfully without auth, will re-enable when auth is ready)
+// router.use(Auth.authUser, Auth.requireMinimumRole(ROLES.PROJECT_MANAGER));
// The base is /api/projects
+
+const AuthUtil = require('../middleware/auth.middleware');
+
router.get('/', ProjectController.project_list);
+// Its a put because we have to send the PM projects to be filtered here
+router.put('/', ProjectController.pm_filtered_projects);
+
router.post('/', ProjectController.create);
router.get('/:ProjectId', ProjectController.project_by_id);
-router.patch('/:ProjectId', ProjectController.update);
+router.put('/:ProjectId', ProjectController.update);
+
+// Update project's managedByUsers in db
+router.patch('/:ProjectId', ProjectController.updateManagedByUsers);
+
+// Bulk update for editing project members
+router.post('/bulk-updates', ProjectController.bulkUpdateManagedByUsers);
+// Update onboard/offboard visibility for a project
+router.patch(
+ '/:ProjectId/visibility',
+ AuthUtil.verifyCookie,
+ ProjectController.updateOnboardOffboardVisibility,
+);
module.exports = router;
diff --git a/backend/routers/projects.router.test.js b/backend/routers/projects.router.test.js
index 5233247b9..7e6f58f59 100644
--- a/backend/routers/projects.router.test.js
+++ b/backend/routers/projects.router.test.js
@@ -1,110 +1,361 @@
+// Mock for Project controller
+jest.mock('../controllers/project.controller');
+
+// Mock Auth.verifyCookie middleware
+const mockVerifyCookie = jest.fn((req, res, next) => next());
+jest.mock('../middleware/auth.middleware', () => ({
+ verifyCookie: mockVerifyCookie,
+}));
+
+// Import Projects router and controller
+const ProjectController = require('../controllers/project.controller');
+const projectsRouter = require('./projects.router');
+const express = require('express');
const supertest = require('supertest');
-const app = require('../app');
-const request = supertest(app);
-const { setupDB } = require('../setup-test');
-setupDB('api-projects');
+// Set up testapp for testing Projects router
+const testapp = express();
+// Allows for body parsing of JSON data
+testapp.use(express.json());
+// Allows for body parsing of HTML data
+testapp.use(express.urlencoded({ extended: false }));
+testapp.use('/api/projects/', projectsRouter);
+const request = supertest(testapp);
+
+describe('Unit testing for Projects router', () => {
+ // Clear all mocks after each test
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ describe('READ', () => {
+ // Mock list of projects
+ const mockProjects = [
+ {
+ id: '1',
+ name: 'mockProject1',
+ description: 'first testing',
+ githubIdentifier: 'gitHubTest1',
+ projectStatus: 'Active',
+ location: 'South LA',
+ createdDate: Date.now(),
+ completedDate: Date.now(),
+ githubUrl: 'https://github.com/mockProject1',
+ slackUrl: 'https://slack.com/mockProject1',
+ googleDriveUrl: 'https://drive.google.com/mockProject1',
+ googleDriveId: '1',
+ hflaWebsiteUrl: 'mockHFLAurl',
+ videoConferenceLink: 'mockVideoLink',
+ lookingDescription: 'n/a',
+ recruitingCategories: ['n/a'],
+ partners: ['n/a'],
+ managedByUsers: ['n/a'],
+ },
+ {
+ id: '2',
+ name: 'mockProject2',
+ description: 'second testing',
+ githubIdentifier: 'gitHubTest2',
+ projectStatus: 'Inactive',
+ location: 'Bay Area',
+ createdDate: Date.now(),
+ completedDate: Date.now(),
+ githubUrl: 'https://github.com/mockProject2',
+ slackUrl: 'https://slack.com/mockProject2',
+ googleDriveUrl: 'https://drive.google.com/mockProject2',
+ googleDriveId: '2',
+ hflaWebsiteUrl: 'mockHFLAurl2',
+ videoConferenceLink: 'mockVideoLink2',
+ lookingDescription: 'n/a',
+ recruitingCategories: ['n/a'],
+ partners: ['n/a'],
+ managedByUsers: ['n/a'],
+ },
+ ];
+
+ it('should return a list of projects based on query with GET /api/projects/', async () => {
+ // Mock ProjectController.project_list method when this route is called
+ ProjectController.project_list.mockImplementationOnce((req, res) => {
+ res.status(200).send(mockProjects);
+ });
+
+ // Mock GET API call
+ const response = await request.get('/api/projects');
+
+ // Tests
+ expect(ProjectController.project_list).toHaveBeenCalled();
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual(mockProjects);
+
+ // Marks completion of tests
+ });
+
+ it('should return a single project with GET /api/projects/:ProjectId', async () => {
+ const mockProject = mockProjects[0];
+ const ProjectId = mockProject.id;
-const { Project } = require('../models');
-const CONFIG = require('../config/auth.config');
+ // Mock ProjectController.project_list method when this route is called
+ ProjectController.project_by_id.mockImplementationOnce((req, res) => {
+ res.status(200).send(mockProject);
+ });
-const headers = {};
-headers['x-customrequired-header'] = CONFIG.CUSTOM_REQUEST_HEADER;
-headers.Accept = 'application/json';
+ // Mock GET API call
+ const response = await request.get(`/api/projects/${ProjectId}`);
-describe('CREATE', () => {
- test('Create a Project with POST to /api/projects/', async (done) => {
- // Test Data
- const submittedData = {
- name: 'projectName',
+ // Tests
+ expect(ProjectController.project_by_id).toHaveBeenCalledWith(
+ expect.objectContaining({ params: { ProjectId } }),
+ expect.anything(), // Mock response
+ expect.anything(), // Mock next
+ );
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual(mockProject);
+
+ // Marks completion of tests
+ });
+ });
+
+ describe('CREATE', () => {
+ // Mock new project
+ const newProject = {
+ id: '3',
+ name: 'mockProject3',
+ description: 'first testing',
+ githubIdentifier: 'gitHubTest3',
+ projectStatus: 'Active',
+ location: 'LA',
+ createdDate: Date.now(),
+ completedDate: Date.now(),
+ githubUrl: 'https://github.com/mockProject3',
+ slackUrl: 'https://slack.com/mockProject3',
+ googleDriveUrl: 'https://drive.google.com/mockProject3',
+ googleDriveId: '3',
+ hflaWebsiteUrl: 'mockHFLAurl',
+ videoConferenceLink: 'mockVideoLink',
+ lookingDescription: 'n/a',
+ recruitingCategories: ['n/a'],
+ partners: ['n/a'],
+ managedByUsers: ['n/a'],
};
- // Submit a project
- const res = await request
- .post('/api/projects/')
- .set(headers)
- .send(submittedData);
- expect(res.status).toBe(201);
- done();
+ it('should create a new project with POST /api/projects', async () => {
+ // Mock ProjectController.create method when this route is called
+ ProjectController.create.mockImplementationOnce((req, res) => {
+ res.status(201).send(newProject);
+ });
+
+ // Mock API POST req
+ const response = await request.post('/api/projects').send(newProject);
+
+ // Middlware assertions
+ expect(mockVerifyCookie).toHaveBeenCalledWith(
+ expect.any(Object),
+ expect.any(Object),
+ expect.any(Function),
+ );
+
+ // Tests
+ expect(ProjectController.create).toHaveBeenCalledWith(
+ expect.objectContaining({ body: newProject }), // Check if newProject in body is parsed
+ expect.anything(), // Mock response
+ expect.anything(), // Mock next
+ );
+ expect(response.status).toBe(201);
+ expect(response.body).toEqual(newProject);
+
+ // Marks completion of tests
+ });
});
-});
-describe('READ', () => {
- test('Get all projects with GET to /api/projects/', async (done) => {
- // Test Data
- const submittedData = {
- name: 'projectName',
- };
-
- // Submit a project
- const res = await request
- .post('/api/projects/')
- .set(headers)
- .send(submittedData);
- expect(res.status).toBe(201);
-
- // Get all projects
- const res2 = await request.get('/api/projects/').set(headers);
- expect(res2.status).toBe(200);
-
- const APIData = res2.body[0];
- expect(APIData.name).toBe(submittedData.name);
- done();
- });;
-});
+ describe('UPDATE', () => {
+ // Mock filtered projects
+ const filteredProjects = [
+ {
+ id: '1',
+ name: 'Filtered Project 1',
+ description: 'Filtered description 1',
+ projectStatus: 'Active',
+ githubIdentifier: 'gitHubTest1',
+ location: 'South LA',
+ createdDate: Date.now(),
+ completedDate: Date.now(),
+ githubUrl: 'https://github.com/mockProject1',
+ slackUrl: 'https://slack.com/mockProject1',
+ googleDriveUrl: 'https://drive.google.com/mockProject1',
+ googleDriveId: '1',
+ hflaWebsiteUrl: 'mockHFLAurl',
+ videoConferenceLink: 'mockVideoLink',
+ lookingDescription: 'n/a',
+ recruitingCategories: ['n/a'],
+ partners: ['n/a'],
+ managedByUsers: ['n/a'],
+ },
+ {
+ id: '2',
+ name: 'Filtered Project 2',
+ description: 'Filtered description 2',
+ projectStatus: 'Inactive',
+ githubIdentifier: 'gitHubTest1',
+ location: 'South LA',
+ createdDate: Date.now(),
+ completedDate: Date.now(),
+ githubUrl: 'https://github.com/mockProject1',
+ slackUrl: 'https://slack.com/mockProject1',
+ googleDriveUrl: 'https://drive.google.com/mockProject1',
+ googleDriveId: '1',
+ hflaWebsiteUrl: 'mockHFLAurl',
+ videoConferenceLink: 'mockVideoLink',
+ lookingDescription: 'n/a',
+ recruitingCategories: ['n/a'],
+ partners: ['n/a'],
+ managedByUsers: ['n/a'],
+ },
+ ];
-describe('UPDATE', () => {
- test('Update a project with PATCH to /api/projects/:id', async (done) => {
- // Test Data
- const submittedData = {
- name: 'projectName',
- };
+ it('should return a filed list of projects for PMs with PUT /api/projects', async () => {
+ // Mock ProjectController.pm_filtered_projects method when this route is called
+ ProjectController.pm_filtered_projects.mockImplementationOnce((req, res) => {
+ res.status(200).send(filteredProjects);
+ });
- // Submit a project
- const res = await request
- .post('/api/projects/')
- .set(headers)
- .send(submittedData);
- expect(res.status).toBe(201);
+ // Mock PUT API call
+ const response = await request.put('/api/projects');
- const updatedDataPayload = {
- name: 'updatedProjectName',
+ // Tests
+ expect(ProjectController.pm_filtered_projects).toHaveBeenCalled();
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual(filteredProjects);
+
+ // Marks completion of tests
+ });
+
+ const updatedProject = {
+ id: 'projectId1',
+ name: 'updated project1',
+ description: 'updated testing',
+ githubIdentifier: 'gitHubTest3',
+ projectStatus: 'Active',
+ location: 'New York',
+ createdDate: Date.now(),
+ completedDate: Date.now(),
+ githubUrl: 'https://github.com/updateProject',
+ slackUrl: 'https://slack.com/updateProject',
+ googleDriveUrl: 'https://drive.google.com/updateProject',
+ googleDriveId: '2',
+ hflaWebsiteUrl: 'updatedURL',
+ videoConferenceLink: 'updatedURL',
+ lookingDescription: 'n/a',
+ recruitingCategories: ['n/a'],
+ partners: ['n/a'],
+ managedByUsers: ['userId1'],
};
- // Update project
- const res2 = await request
- .patch(`/api/projects/${res.body._id}`)
- .set(headers)
- .send(updatedDataPayload);
- expect(res2.status).toBe(200);
+ const ProjectId = updatedProject.id;
- // Get project
- const res3 = await request.get(`/api/projects/${res.body._id}`).set(headers);
- expect(res3.status).toBe(200);
+ it('should return an updated project with PUT /api/projects/:ProjectId', async () => {
+ // Mock ProjectController.update method when this route is called
+ ProjectController.update.mockImplementationOnce((req, res) => {
+ res.status(200).send(updatedProject);
+ });
- const APIData = res3.body;
- expect(APIData.name).toBe(updatedDataPayload.name);
- done();
- });
-});
+ // Mock PUT API call
+ const response = await request.put(`/api/projects/${ProjectId}`).send(updatedProject);
+
+ // Middlware assertions
+ expect(mockVerifyCookie).toHaveBeenCalledWith(
+ expect.any(Object),
+ expect.any(Object),
+ expect.any(Function),
+ );
-describe('DELETE', () => {
- test('Delete a project with POST to /api/projects/:id', async (done) => {
- // Test Data
- const submittedData = {
- name: 'projectName',
+ // Tests
+ expect(ProjectController.update).toHaveBeenCalledWith(
+ expect.objectContaining({ params: { ProjectId } }), // Check if ProjectId is parsed from params
+ expect.anything(), // Mock response
+ expect.anything(), // Mock next
+ );
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual(updatedProject);
+
+ // Marks completion of tests
+ });
+
+ const updatedUser = {
+ id: 'userId1',
+ name: 'Updated User',
+ email: 'mockuser@example.com',
+ managedProjects: ['projectId1'],
};
- // Submit a project
- const res = await request
- .post('/api/projects/')
- .set(headers)
- .send(submittedData);
- expect(res.status).toBe(201);
-
- // Delete project
- const res2 = await request.patch(`/api/projects/${res.body._id}`).set(headers);
- expect(res2.status).toBe(200);
- done();
-});
+ const userId = updatedUser.id;
+
+ it("should add to the project's managedByUsers and the user's managedProjects fields with PATCH /api/projects/:ProjectId", async () => {
+ // Mock ProjectController.updateManagedByUsers method when this route is called
+ ProjectController.updateManagedByUsers.mockImplementationOnce((req, res) => {
+ res.status(200).send({ project: updatedProject, user: updatedUser });
+ });
+
+ // Mock PUT API call
+ const response = await request
+ .patch(`/api/projects/${ProjectId}`)
+ .send({ action: 'add', userId });
+
+ // Middlware assertions
+ expect(mockVerifyCookie).toHaveBeenCalledWith(
+ expect.any(Object),
+ expect.any(Object),
+ expect.any(Function),
+ );
+
+ // Tests
+ expect(ProjectController.updateManagedByUsers).toHaveBeenCalledWith(
+ expect.objectContaining({
+ params: { ProjectId },
+ body: { action: 'add', userId },
+ }), // Check if ProjectId is parsed from params
+ expect.anything(), // Mock response
+ expect.anything(), // Mock next
+ );
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual({ project: updatedProject, user: updatedUser });
+
+ // Marks completion of tests
+ });
+
+ it("should remove user from the project's managedByUsers and remove project from the user's managedProjects fields with PATCH /api/projects/:ProjectId", async () => {
+ updatedProject.managedByUsers = [];
+ updatedUser.managedProjects = [];
+
+ // Mock ProjectController.updateManagedByUsers method when this route is called
+ ProjectController.updateManagedByUsers.mockImplementationOnce((req, res) => {
+ res.status(200).send({ project: updatedProject, user: updatedUser });
+ });
+
+ // Mock PUT API call
+ const response = await request
+ .patch(`/api/projects/${ProjectId}`)
+ .send({ action: 'remove', userId });
+
+ // Middlware assertions
+ expect(mockVerifyCookie).toHaveBeenCalledWith(
+ expect.any(Object),
+ expect.any(Object),
+ expect.any(Function),
+ );
+
+ // Tests
+ expect(ProjectController.updateManagedByUsers).toHaveBeenCalledWith(
+ expect.objectContaining({
+ params: { ProjectId },
+ body: { action: 'remove', userId },
+ }), // Check if ProjectId is parsed from params
+ expect.anything(), // Mock response
+ expect.anything(), // Mock next
+ );
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual({ project: updatedProject, user: updatedUser });
+
+ // Marks completion of tests
+ });
+ });
});
diff --git a/backend/routers/questions.router.test.js b/backend/routers/questions.router.test.js
new file mode 100644
index 000000000..e61553cee
--- /dev/null
+++ b/backend/routers/questions.router.test.js
@@ -0,0 +1,177 @@
+// Mock and import Question model, import question router
+jest.mock('../models/question.model');
+const { Question } = require('../models');
+const questionsRouter = require('./questions.router');
+
+// Create a test app with Express
+const express = require('express');
+const supertest = require('supertest');
+const testapp = express();
+// Allow for body parsing of JSON data
+testapp.use(express.json());
+// Allow for body parsing of HTML data
+testapp.use(express.urlencoded({ extended: false }));
+testapp.use('/api/questions/', questionsRouter);
+const request = supertest(testapp);
+
+describe('Unit tests for questions router', () => {
+ // Clear all mocks after each test
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ describe('READ', () => {
+ // Mock question data
+ const mockQuestions = [
+ {
+ id: 1,
+ questionText: 'What is your favorite color?',
+ htmlName: 'favoriteColor',
+ answers: {
+ answerOneText: 'Red',
+ answerTwoText: 'Blue',
+ answerThreeText: 'Green',
+ answerFourText: 'Yellow',
+ },
+ },
+ {
+ id: 2,
+ questionText: 'What is your favorite food?',
+ htmlName: 'favoriteFood',
+ answers: {
+ answerOneText: 'Pizza',
+ answerTwoText: 'Cheeseburger',
+ answerThreeText: 'Sushi',
+ answerFourText: 'Chicken',
+ },
+ },
+ ];
+
+ it('should return all questions with GET /api/questions', async () => {
+ // Mock the Question.find() method
+ Question.find.mockResolvedValue(mockQuestions);
+
+ // Mock the request to the API
+ const response = await request.get('/api/questions');
+
+ // Tests
+ expect(Question.find).toHaveBeenCalled();
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual(mockQuestions);
+
+ // Marks completion of tests
+ });
+
+ it('should return 400 status code when there is an error with GET /api/questions', async () => {
+ // Mock the error thrown when find method is called
+ const error = new Error('Database error');
+ Question.find.mockRejectedValue(error);
+
+ // Mock console log function
+ const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
+
+ // Mock the request to the API
+ const response = await request.get('/api/questions');
+
+ // Tests
+ expect(Question.find).toHaveBeenCalled();
+ expect(consoleLogSpy).toHaveBeenCalledWith(error);
+ expect(response.status).toBe(400);
+
+ // Clean up and restores original console log function
+ consoleLogSpy.mockRestore();
+ // Marks completion of tests
+ });
+
+ it('should return a specific question with GET /api/questions/:id', async () => {
+ // Mock the Question.findById() method
+ const mockQuestion = mockQuestions[0];
+ const { id } = mockQuestion;
+ Question.findById.mockResolvedValue(mockQuestion);
+
+ // Mock the request to the API
+ const response = await request.get(`/api/questions/${id}`);
+
+ // Tests
+ expect(Question.findById).toHaveBeenCalledWith(`${id}`);
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual(mockQuestion);
+
+ // Marks completion of tests
+ });
+
+ it('should return 400 status code when there is an error with GET /api/questions/:id', async () => {
+ // Mock user id
+ const id = mockQuestions[0].id;
+
+ // Mock the error when findById method is called
+ const error = new Error('Database error');
+ Question.findById.mockRejectedValue(error);
+
+ // Mock console log function
+ const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
+
+ // Mock the request to the API
+ const response = await request.get(`/api/questions/${id}`);
+
+ // Tests
+ expect(Question.findById).toHaveBeenCalledWith(`${id}`);
+ expect(consoleLogSpy).toHaveBeenCalledWith(error);
+ expect(response.status).toBe(400);
+
+ // Clean up and restores original console log function
+ consoleLogSpy.mockRestore();
+ // Marks completion of tests
+ });
+ });
+
+ describe('CREATE', () => {
+ it('should create a new question with POST /api/questions/', async () => {
+ // Mock the Question.create() method
+ const newQuestion = {
+ id: 3,
+ questionText: 'What is your favorite animal?',
+ htmlName: 'favoriteAnimal',
+ answers: {
+ answerOneText: 'Dog',
+ answerTwoText: 'Cat',
+ answerThreeText: 'Bird',
+ answerFourText: 'Fish',
+ },
+ };
+
+ // Mock Question.create method
+ Question.create.mockResolvedValue(newQuestion);
+
+ // Mock the request to the API
+ const response = await request.post('/api/questions/').send(newQuestion);
+
+ // Tests
+ expect(Question.create).toHaveBeenCalledWith(newQuestion);
+ expect(response.status).toBe(201);
+
+ // Marks completion of tests
+ });
+
+ it('should return 400 status code when there is an error with POST /api/questions', async () => {
+ // Mock the error thrown when create method is called
+ const error = new Error('Database error');
+ Question.create.mockRejectedValue(error);
+
+ // Mock console log function
+ const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
+
+ // Mock the request to the API
+ const response = await request.post('/api/questions');
+
+ // Tests
+ expect(Question.create).toHaveBeenCalled();
+ expect(consoleLogSpy).toHaveBeenCalledWith(error);
+ expect(response.status).toBe(400);
+
+ // Clean up and restores original console log function
+ consoleLogSpy.mockRestore();
+ // Marks completion of tests
+ });
+ });
+});
diff --git a/backend/routers/recurringEvents.router.js b/backend/routers/recurringEvents.router.js
index 66e5ab7db..dbb4bce83 100644
--- a/backend/routers/recurringEvents.router.js
+++ b/backend/routers/recurringEvents.router.js
@@ -4,40 +4,53 @@ const cors = require('cors');
const { RecurringEvent } = require('../models/recurringEvent.model');
const { RecurringEventController } = require('../controllers/');
+const { Auth } = require('../middleware');
// GET /api/recurringevents/
router.get('/', cors(), (req, res) => {
- // const { query } = req;
-
- RecurringEvent
- // .find(query.checkInReady === 'true' ? query : undefined)
- .find()
- .populate('project')
- .then(recurringEvents => {
- return res.status(200).send(recurringEvents);
- })
- .catch(err => {
- console.log(err);
- return res.sendStatus(400);
- });
+ // const { query } = req;
+ RecurringEvent
+ // .find(query.checkInReady === 'true' ? query : undefined)
+ .find()
+ // This will deselect the video conference link field
+ .select('-videoConferenceLink')
+ .populate('project')
+ .then((recurringEvents) => {
+ return res.status(200).send(recurringEvents);
+ })
+ .catch((err) => {
+ console.log(err);
+ return res.sendStatus(400);
+ });
+});
+
+router.get('/internal', (req, res) => {
+ RecurringEvent.find()
+ .populate('project')
+ .then((recurringEvents) => {
+ return res.status(200).send(recurringEvents);
+ })
+ .catch((err) => {
+ console.log(err);
+ return res.sendStatus(400);
+ });
});
router.get('/:id', (req, res) => {
- RecurringEvent
- .findById(req.params.id)
- .then(recurringEvent => {
- return res.status(200).send(recurringEvent);
- })
- .catch(err => {
- console.log(err);
- return res.sendStatus(400);
- });
+ RecurringEvent.findById(req.params.id)
+ .then((recurringEvent) => {
+ return res.status(200).send(recurringEvent);
+ })
+ .catch((err) => {
+ console.log(err);
+ return res.sendStatus(400);
+ });
});
-router.post('/', RecurringEventController.create);
+router.post('/', Auth.verifyCookie, RecurringEventController.create);
-router.patch('/:RecurringEventId', RecurringEventController.update);
+router.patch('/:RecurringEventId', Auth.verifyCookie, RecurringEventController.update);
-router.delete('/:RecurringEventId', RecurringEventController.destroy);
+router.delete('/:RecurringEventId', Auth.verifyCookie, RecurringEventController.destroy);
module.exports = router;
diff --git a/backend/routers/recurringEvents.router.test.js b/backend/routers/recurringEvents.router.test.js
new file mode 100644
index 000000000..750f288b5
--- /dev/null
+++ b/backend/routers/recurringEvents.router.test.js
@@ -0,0 +1,264 @@
+// Mock model, controller, middleware, and cors
+jest.mock('../models/recurringEvent.model');
+jest.mock('../controllers/recurringEvent.controller');
+jest.mock('../middleware/auth.middleware', () => ({
+ verifyCookie: jest.fn((req, res, next) => next()),
+}));
+jest.mock('cors', () => () => (req, res, next) => {
+ res.header('Access-Control-Allow-Origin', '*');
+ res.header('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS');
+ res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
+ next();
+});
+
+const { RecurringEvent } = require('../models/recurringEvent.model');
+const { RecurringEventController } = require('../controllers/');
+
+// Import Recurring Events Router after setting up mocks
+const recurringEventsRouter = require('./recurringEvents.router');
+const express = require('express');
+const testapp = express();
+// Allow for body parsing of JSON data
+testapp.use(express.json());
+// Allow for body parsing of HTML data
+testapp.use(express.urlencoded({ extended: false }));
+testapp.use('/api/recurringevents', recurringEventsRouter);
+const supertest = require('supertest');
+const request = supertest(testapp);
+
+describe('Unit tests for RecurringEvents router', () => {
+ // Create mock recurring events
+ const mockEvents = [
+ {
+ id: 1,
+ name: 'mockEvent1',
+ location: {
+ city: 'city1',
+ state: 'state1',
+ country: 'country1',
+ },
+ project: 'project1',
+ videoConferenceLink: 'zoom-link1',
+ },
+ {
+ id: 2,
+ name: 'mockEvent2',
+ location: {
+ city: 'city1',
+ state: 'state1',
+ country: 'country1',
+ },
+ project: 'project2',
+ videoConferenceLink: 'zoom-link2',
+ },
+ ];
+
+ // Clear all mocks after each test
+ afterEach(() => jest.clearAllMocks());
+
+ describe('READ', () => {
+ it('should return a list of events with GET /api/recurringevents/', async () => {
+ // Mock find method with chaining of select and populate methods
+ RecurringEvent.find.mockReturnValue({
+ select: jest.fn().mockReturnValue({
+ populate: jest.fn().mockResolvedValue(mockEvents),
+ }),
+ });
+
+ const response = await request.get('/api/recurringevents/');
+
+ // Tests
+ expect(RecurringEvent.find().select).toHaveBeenCalledWith('-videoConferenceLink');
+ expect(RecurringEvent.find().select().populate).toHaveBeenCalledWith('project');
+ expect(response.headers['access-control-allow-origin']).toBe('*');
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual(mockEvents);
+ });
+
+ it('should return status code 400 when there is an error with GET /api/recurringevents/', async () => {
+ // Mock the test error
+ const error = new Error('test error');
+
+ // Mock db methods
+ RecurringEvent.find.mockImplementationOnce(() => ({
+ select: () => ({
+ populate: () => Promise.reject(error),
+ }),
+ }));
+
+ // Creates a spy on console log function to track any calls during test
+ const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
+
+ const response = await request.get('/api/recurringevents/');
+
+ // Tests
+ expect(consoleLogSpy).toHaveBeenCalledWith(error);
+ expect(response.status).toBe(400);
+
+ // Clear console log mock and restore its original function
+ consoleLogSpy.mockRestore();
+ });
+
+ it('should return a list of events with GET /api/recurringevents/internal', async () => {
+ // Mock Mongoose method
+ RecurringEvent.find.mockReturnValue({
+ populate: jest.fn().mockResolvedValue(mockEvents),
+ });
+
+ const response = await request.get('/api/recurringevents/internal');
+
+ // Tests
+ expect(RecurringEvent.find().populate).toHaveBeenCalledWith('project');
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual(mockEvents);
+ });
+
+ it('should return status code 400 when there is an error with GET /api/recurringevents/internal', async () => {
+ // Mock the test error
+ const error = new Error('test error');
+
+ // Mock error in calling find method
+ RecurringEvent.find.mockImplementationOnce(() => ({
+ populate: () => Promise.reject(error),
+ }));
+
+ // Creates a spy on console log function to track any calls during test
+ const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
+
+ const response = await request.get('/api/recurringevents/internal');
+
+ // Tests
+ expect(RecurringEvent.find).toHaveBeenCalled();
+ expect(consoleLogSpy).toHaveBeenCalledWith(error);
+ expect(response.status).toBe(400);
+
+ // Clear console log mock and restore its original function
+ consoleLogSpy.mockRestore();
+ });
+
+ it('should return a single event by id with GET /api/recurringevents/:id', async () => {
+ // Mock event
+ const mockEvent = mockEvents[0];
+ const { id } = mockEvent;
+
+ // Mock findById method
+ RecurringEvent.findById.mockResolvedValue(mockEvent);
+
+ const response = await request.get(`/api/recurringevents/${id}`);
+
+ // Tests
+ expect(RecurringEvent.findById).toHaveBeenCalledWith(`${id}`);
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual(mockEvent);
+ });
+
+ it('should return status code 400 when there is an error with GET /api/recurringevents/:id', async () => {
+ // Mock the test error
+ const error = new Error('test error');
+
+ // Mock findById method to return a rejected Promise to trigger catch block
+ RecurringEvent.findById.mockRejectedValue(error);
+
+ // Creates a spy on console log function to track any calls during test
+ const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
+
+ const response = await request.get('/api/recurringevents/123');
+
+ // Tests
+ expect(RecurringEvent.findById).toHaveBeenCalled();
+ expect(consoleLogSpy).toHaveBeenCalledWith(error);
+ expect(response.status).toBe(400);
+
+ // Clear console log mock and restore its original function
+ consoleLogSpy.mockRestore();
+ });
+ });
+
+ describe('CREATE', () => {
+ // Mock new event
+ const newEvent = {
+ id: 3,
+ name: 'mockEvent3',
+ location: {
+ city: 'city3',
+ state: 'state3',
+ country: 'country3',
+ },
+ project: 'project3',
+ videoConferenceLink: 'zoom-link3',
+ };
+
+ it('should add a new event with POST /api/recurringevents/', async () => {
+ RecurringEventController.create.mockImplementationOnce((req, res) => {
+ res.status(200).send(newEvent);
+ });
+
+ const response = await request.post('/api/recurringevents/').send(newEvent);
+
+ // Tests
+ expect(RecurringEventController.create).toHaveBeenCalledWith(
+ expect.objectContaining({ body: newEvent }), // Checks if newEvent was passed
+ expect.anything(), // Mock the response object
+ expect.anything(), // Mock the next object
+ );
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual(newEvent);
+ });
+ });
+
+ describe('UPDATE', () => {
+ it('should update a specific event by id with PATCH /api/recurringevents/:id', async () => {
+ // Update to event#1
+ const updatedEvent = {
+ id: 1,
+ name: 'updatedEvent1',
+ location: {
+ city: 'update city1',
+ state: 'update state1',
+ country: 'update country1',
+ },
+ project: 'update project1',
+ videoConferenceLink: 'new zoom-link1',
+ };
+ const { id } = updatedEvent;
+
+ RecurringEventController.update.mockImplementationOnce((req, res) => {
+ return res.status(200).send(updatedEvent);
+ });
+
+ const response = await request.patch(`/api/recurringevents/${id}`).send(updatedEvent);
+
+ // Tests
+ expect(RecurringEventController.update).toHaveBeenCalledWith(
+ expect.objectContaining({ body: updatedEvent }), // Checks if newEvent was passed
+ expect.anything(), // Mock the response object
+ expect.anything(), // Mock the next object
+ );
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual(updatedEvent);
+ });
+ });
+
+ describe('DESTROY', () => {
+ it('should delete a specific event by id with DELETE /api/recurringevents/:id', async () => {
+ // Mock event to be deleted
+ const deleteEvent = mockEvents[0];
+ const { id } = deleteEvent;
+
+ RecurringEventController.destroy.mockImplementationOnce((req, res) => {
+ return res.status(200).send(deleteEvent);
+ });
+
+ const response = await request.delete(`/api/recurringevents/${id}`);
+
+ // Tests
+ expect(RecurringEventController.destroy).toHaveBeenCalledWith(
+ expect.objectContaining({ params: { RecurringEventId: String(id) } }), // Check for parsing of RecurringEventId
+ expect.anything(), // Mock response
+ expect.anything(), // Mock next
+ );
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual(deleteEvent);
+ });
+ });
+});
diff --git a/backend/routers/users.router.js b/backend/routers/users.router.js
index db4efb231..60b5af4d5 100644
--- a/backend/routers/users.router.js
+++ b/backend/routers/users.router.js
@@ -1,17 +1,36 @@
const express = require('express');
const router = express.Router();
-
+const { Auth } = require('../middleware');
const { UserController } = require('../controllers');
+const { ROLES } = require('../../shared/roles');
// The base is /api/users
router.get('/', UserController.user_list);
+router.get('/id/:UserId', UserController.user_by_id);
+
+router.get('/email/:email', UserController.user_by_email);
+
+router.get('/admins', UserController.admin_list);
+
+router.get('/projectManagers', UserController.projectManager_list);
+
router.post('/', UserController.create);
-router.get('/:UserId', UserController.user_by_id);
+router.post('/bulk-updates', UserController.bulkUpdateManagedProjects);
+
+router.patch(
+ '/:UserId',
+ [Auth.authUser, Auth.requireMinimumRole(ROLES.ADMIN)],
+ UserController.update,
+);
-router.patch('/:UserId', UserController.update);
+router.patch('/:UserId/managedProjects', UserController.updateManagedProjects);
-router.delete('/:UserId', UserController.delete);
+router.delete(
+ '/:UserId',
+ [Auth.authUser, Auth.requireMinimumRole(ROLES.ADMIN)],
+ UserController.delete,
+);
module.exports = router;
diff --git a/backend/routers/users.router.test.js b/backend/routers/users.router.test.js
index 61d91791d..e942fdf3c 100644
--- a/backend/routers/users.router.test.js
+++ b/backend/routers/users.router.test.js
@@ -1,199 +1,249 @@
-const supertest = require('supertest');
-const app = require('../app');
-const request = supertest(app);
-
-const { setupDB } = require('../setup-test');
-setupDB('api-users');
-
-const backendHeaders = process.env.CUSTOM_REQUEST_HEADER;
-describe('CREATE', () => {
- test('Create a User with POST to /api/users/', async (done) => {
- // Test Data
- const submittedData = {
- name: {
- firstName: 'test',
- lastName: 'user',
- },
- email: 'newtest@test.com',
- };
-
- // Submit a User
- const res = await request
- .post('/api/users/')
- .set('Accept', 'application/json')
- .set('x-customrequired-header', backendHeaders)
- .send(submittedData);
- expect(res.status).toBe(201);
-
- done();
- });
-});
-
-describe('READ', () => {
- test('Get a list of Users with with GET to /api/users/', async (done) => {
- // Test Data
- const submittedData = {
- name: {
- firstName: 'test',
- lastName: 'user',
- },
- email: 'newtest@test.com',
- };
-
- // Submit a User
- const res = await request
- .post('/api/users/')
- .set('Accept', 'application/json')
- .set('x-customrequired-header', backendHeaders)
- .send(submittedData);
- expect(res.status).toBe(201);
+// Setup mocks for UserController
+jest.mock('../controllers/user.controller');
+const { UserController } = require('../controllers');
- // Get all Users
- const res2 = await request.get('/api/users/').set('x-customrequired-header', backendHeaders);
- expect(res2.status).toBe(200);
-
- const APIData = res2.body[0];
- expect(APIData.name).toMatchObject(submittedData.name);
+// Must import usersRouter after setting up mocks for UserController
+const usersRouter = require('./users.router');
+const express = require('express');
+const supertest = require('supertest');
- done();
+// Setup testapp with just usersRouter which calls mocked UserController
+const testapp = express();
+testapp.use(express.json());
+testapp.use(express.urlencoded({ extended: false }));
+testapp.use('/api/users', usersRouter);
+const request = supertest(testapp);
+
+describe('Unit Tests for userRouter', () => {
+ // Mocked user data
+ const mockUser = {
+ id: 'userId1',
+ name: {
+ firstName: 'test',
+ lastName: 'user',
+ },
+ email: 'newtest@test.com',
+ accessLevel: 'admin',
+ managedProjects: ['projectId1'],
+ };
+ const mockUserId = mockUser.id;
+ const mockUpdatedEmail = mockUser.email;
+
+ afterEach(() => {
+ jest.clearAllMocks();
});
- test('Get a specific User by param with GET to /api/users?email=', async (done) => {
- // Test Data
- const submittedData = {
- name: {
- firstName: 'test',
- lastName: 'user',
- },
- email: 'newtest@test.com',
- };
-
- // Submit a User
- const res = await request
- .post('/api/users/')
- .set('Accept', 'application/json')
- .set('x-customrequired-header', backendHeaders)
- .send(submittedData);
- expect(res.status).toBe(201);
-
- // Get all Users
- const res2 = await request
- .get('/api/users?email=newtest@test.com')
- .set('x-customrequired-header', backendHeaders);
- expect(res2.status).toBe(200);
- const APIData = res2.body[0];
- expect(APIData.name).toMatchObject(submittedData.name);
-
- done();
+ describe('CREATE', () => {
+ it('should create a User through the UserController', async () => {
+ //Mock the UserController function that this route calls with expected results
+ UserController.create.mockImplementationOnce((req, res) => {
+ return res.status(201).send(mockUser);
+ });
+
+ //Functionality
+ //Post mockUser to CREATE API Endpoint
+ const response = await request.post('/api/users/').send(mockUser);
+
+ //Test
+ expect(UserController.create).toHaveBeenCalledWith(
+ expect.objectContaining({ body: mockUser }),
+ expect.anything(), // Mock the response object
+ expect.anything(), // Mock the next function
+ );
+ expect(response.status).toBe(201);
+ expect(response.body).toEqual(mockUser);
+ });
});
- test('Get a specific User by UserId with GET to /api/users/:UserId', async (done) => {
- // Test Data
- const submittedData = {
- name: {
- firstName: 'test',
- lastName: 'user',
- },
- email: 'newtest@test.com',
- };
-
- // Submit a User
- const res = await request
- .post('/api/users/')
- .set('Accept', 'application/json')
- .set('x-customrequired-header', backendHeaders)
- .send(submittedData);
- expect(res.status).toBe(201);
-
- // Get User by UserId
- const res2 = await request
- .get(`/api/users/${res.body._id}`)
- .set('x-customrequired-header', backendHeaders);
- expect(res2.status).toBe(200);
-
- const APIData = res2.body;
- expect(APIData.email).toBe(submittedData.email);
- expect(APIData.name).toMatchObject(submittedData.name);
-
- done();
+ describe('READ', () => {
+ it('should get a list of Users with with GET to /api/users/ through UserController', async () => {
+ //Mock the UserController function that this route calls with expected results
+ UserController.user_list.mockImplementationOnce((req, res) => {
+ return res.status(200).send([mockUser]);
+ });
+
+ //Functionality
+ //Get list of all users from READ API Endpoint
+ const response = await request.get('/api/users/');
+
+ //Test
+ expect(UserController.user_list).toHaveBeenCalled();
+ expect(response.status).toBe(200);
+ expect(response.body[0]).toEqual(mockUser);
+ });
+
+ it('should get a specific User by param with GET to /api/users?email= through UserController', async () => {
+ //Mock the UserController function that this route calls with expected results
+ UserController.user_list.mockImplementationOnce((req, res) => {
+ return res.status(200).send([mockUser]);
+ });
+
+ //Functionality
+ //Get a user with a specific email using a query param to READ API Endpoint
+ const response = await request.get('/api/users?email=newtest@test.com');
+
+ //Test
+ expect(UserController.user_list).toHaveBeenCalled();
+ expect(response.status).toBe(200);
+ expect(response.body[0]).toEqual(mockUser);
+ });
+
+ it('should get a list of Users with accessLevel of admin or superadmin with GET to /api/users/admins through UserController', async () => {
+ //Mock the UserController function that this route calls with expected results
+ UserController.admin_list.mockImplementationOnce((req, res) => {
+ return res.status(200).send([mockUser]);
+ });
+
+ //Functionality
+ //Get a list of admins and superadmins from READ API Endpoint for admins
+ const response = await request.get('/api/users/admins');
+
+ //Test
+ expect(UserController.admin_list).toHaveBeenCalled();
+ expect(response.status).toBe(200);
+ expect(response.body[0]).toEqual(mockUser);
+ });
+
+ it('should get a list of Users with the ability to manage projects with GET to /api/users/projectManagers through UserController', async () => {
+ //Mock the UserController function that this route calls with expected results
+ UserController.projectManager_list.mockImplementationOnce((req, res) => {
+ return res.status(200).send([mockUser]);
+ });
+
+ //Functionality
+ //Get a list of project leads and admins from READ API Endpoint for project leads
+ const response = await request.get('/api/users/projectManagers');
+
+ //Test
+ expect(UserController.projectManager_list).toHaveBeenCalled();
+ expect(response.status).toBe(200);
+ expect(response.body[0]).toEqual(mockUser);
+ });
+
+ // @TODO: Fix failing test, require investigation. Please referece issue 2036
+ it.skip('should get a specific User by UserId with GET to /api/users/:UserId through UserController', async () => {
+ //Mock the UserController function that this route calls with expected results
+ UserController.user_by_id.mockImplementationOnce((req, res) => {
+ return res.status(200).send(mockUser);
+ });
+
+ //Functionality
+ //Get a specific user from READ API Endpoint for specific UUIDs
+ const response = await request.get(`/api/users/${mockUserId}`);
+
+ //Test
+ expect(UserController.user_by_id).toHaveBeenCalledWith(
+ expect.objectContaining({ params: { UserId: mockUserId } }),
+ expect.anything(), // Mock the response object
+ expect.anything(), // Mock the next function
+ );
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual(mockUser);
+ });
});
-});
-
-describe('UPDATE', () => {
- test('Update a User with PATCH to /api/users/:UserId', async (done) => {
- // Test Data
- const submittedData = {
- name: {
- firstName: 'test',
- lastName: 'user',
- },
- email: 'newtest@test.com',
- };
-
- // Submit a User
- const res = await request
- .post('/api/users/')
- .set('Accept', 'application/json')
- .set('x-customrequired-header', backendHeaders)
- .send(submittedData);
- expect(res.status).toBe(201);
- const updatedEmail = {
- email: 'newtest@test.com',
+ describe('UPDATE', () => {
+ it('should update a User with PATCH to /api/users/:UserId through UserController', async () => {
+ //Mock the UserController function that this route calls with expected results
+ UserController.update.mockImplementationOnce((req, res) => {
+ return res.status(200).send(mockUser);
+ });
+
+ //Functionality
+ //Patch a user with a specific id by sending new user data to UPDATE API Endpoint
+ const response = await request.patch(`/api/users/${mockUserId}`).send(mockUpdatedEmail);
+
+ //Test
+ expect(UserController.update).toHaveBeenCalledWith(
+ expect.objectContaining({ params: { UserId: mockUserId } }),
+ expect.anything(), // Mock the response object
+ expect.anything(), // Mock the next function
+ );
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual(mockUser);
+ });
+
+ // Create mock project and add userId to managedByUsers
+ const mockProject = {
+ id: 'projectId1',
+ name: 'Test Project',
+ managedByUsers: [mockUserId],
};
-
- // Update User
- const resUpdate = await request
- .patch(`/api/users/${res.body._id}`)
- .set('Accept', 'application/json')
- .set('x-customrequired-header', backendHeaders)
- .send(updatedEmail);
- expect(resUpdate.status).toBe(200);
- // TODO: The updated User call is not returning a repsonse. Uncomment below line and
- // run to see.
- // expect(resUpdate.name).toMatchObject(submittedData.name);
-
- const res2 = await request
- .get(`/api/users/${res.body._id}`)
- .set('x-customrequired-header', backendHeaders);
- expect(res2.status).toBe(200);
-
- const APIData = res2.body;
- expect(APIData.email).toBe(updatedEmail.email);
- expect(APIData.name).toMatchObject(submittedData.name);
-
- done();
+ const projectId = mockProject.id;
+
+ it("should add projectId to user's managedProjects and userId to project's managedByUsers with PATCH /api/users/:UserId/managedProjects", async () => {
+ // Mock the response of UserController.updateManagedProjects
+ UserController.updateManagedProjects.mockImplementationOnce((req, res) => {
+ return res.status(200).send({ user: mockUser, project: mockProject });
+ });
+
+ // Send PATCH request to update managedProjects
+ const response = await request.patch(`/api/users/${mockUserId}/managedProjects`).send({
+ action: 'add',
+ projectId: projectId,
+ });
+
+ // Tests
+ expect(UserController.updateManagedProjects).toHaveBeenCalledWith(
+ expect.objectContaining({
+ params: { UserId: mockUserId },
+ body: { action: 'add', projectId },
+ }),
+ expect.anything(), // Mock the response object
+ expect.anything(), // Mock the next function
+ );
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual({ user: mockUser, project: mockProject });
+ });
+
+ it("should remove projectId in user's managedProjects and userId in project's managedByUsers with PATCH /api/users/:UserId/managedProjects", async () => {
+ // Remove projectId and userId from fields
+ mockProject.managedByUsers = [];
+ mockUser.managedProjects = [];
+
+ // Mock the response of UserController.updateManagedProjects
+ UserController.updateManagedProjects.mockImplementationOnce((req, res) => {
+ return res.status(200).send({ user: mockUser, project: mockProject });
+ });
+
+ // Send PATCH request to update managedProjects
+ const response = await request.patch(`/api/users/${mockUserId}/managedProjects`).send({
+ action: 'remove',
+ projectId: projectId,
+ });
+
+ // Tests
+ expect(UserController.updateManagedProjects).toHaveBeenCalledWith(
+ expect.objectContaining({
+ params: { UserId: mockUserId },
+ body: { action: 'remove', projectId },
+ }),
+ expect.anything(), // Mock the response object
+ expect.anything(), // Mock the next function
+ );
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual({ user: mockUser, project: mockProject });
+ });
});
-});
-
-describe('DELETE', () => {
- test('Delete a specific user by Id with DELETE /api/users/:UserId', async (done) => {
- // Test Data
- const submittedData = {
- name: {
- firstName: 'test',
- lastName: 'user',
- },
- email: 'newtest@test.com',
- };
-
- // Submit a User
- const res = await request
- .post('/api/users/')
- .set('Accept', 'application/json')
- .set('x-customrequired-header', backendHeaders)
- .send(submittedData);
- expect(res.status).toBe(201);
-
- // Delete User
- const res2 = await request
- .delete(`/api/users/${res.body._id}`)
- .set('x-customrequired-header', backendHeaders);
- expect(res2.status).toBe(200);
-
- const APIData = res2.body;
- expect(APIData.name).toMatchObject(submittedData.name);
- done();
+ describe('DELETE', () => {
+ it('should delete a specific user by Id with DELETE /api/users/:UserId through UserController', async () => {
+ //Mock the UserController function that this route calls with expected results
+ UserController.delete.mockImplementationOnce((req, res) => {
+ return res.status(200).send(mockUser);
+ });
+
+ //Delete user with a specific id via a request to DELETE API Endpoint
+ const response = await request.delete(`/api/users/${mockUserId}`).send(mockUpdatedEmail);
+
+ //Test
+ expect(UserController.delete).toHaveBeenCalledWith(
+ expect.objectContaining({ params: { UserId: mockUserId } }),
+ expect.anything(), // Mock the response object
+ expect.anything(), // Mock the next function
+ );
+ expect(response.status).toBe(200);
+ expect(response.body).toEqual(mockUser);
+ });
});
});
diff --git a/backend/scripts/.gitignore b/backend/scripts/.gitignore
new file mode 100644
index 000000000..801650518
--- /dev/null
+++ b/backend/scripts/.gitignore
@@ -0,0 +1,30 @@
+# Jupyter Notebook checkpoints
+.ipynb_checkpoints/
+
+# Jupyter runtime files
+.jupyter/
+
+# Hidden notebook state (metadata, autosaves)
+*.nbconvert.ipynb
+*.nbconvert/
+
+# VSCode / PyCharm or other IDE junk (optional, but useful)
+.vscode/
+.idea/
+
+# Python cache
+__pycache__/
+*.pyc
+*.pyo
+*.pyd
+*.pkl
+
+# OS files
+.DS_Store
+Thumbs.db
+
+# Virtual environments
+venv/
+
+# Additional text files
+adjustedreqs.txt
\ No newline at end of file
diff --git a/backend/scripts/clearDevCollections.js b/backend/scripts/clearDevCollections.js
new file mode 100644
index 000000000..7e4651e7e
--- /dev/null
+++ b/backend/scripts/clearDevCollections.js
@@ -0,0 +1,112 @@
+/**
+ * Script to clear the 'projects' and/or 'recurringevents' collections from the DEV MongoDB database.
+ *
+ * Usage:
+ * node clearDevCollections.js [--projects] [--recurring-events]
+ *
+ * Arguments:
+ * --projects Only clear the 'projects' collection
+ * --recurring-events Only clear the 'recurringevents' collection
+ * --events Only clear the 'events' collection
+ * --checkins Only clear the 'checkins' collection
+ * --all Clear all supported collections
+ * --mock Only print what would be deleted, do not delete
+ * --help Show this help message and exit
+ * (no flags) Show help (safety mode)
+ *
+ * Requires environment variable:
+ * DEV_DB_URI - MongoDB URI for DEV
+ *
+ * Example:
+ * node clearDevCollections.js --projects
+ * node clearDevCollections.js --recurring-events
+ * node clearDevCollections.js
+ */
+
+const { MongoClient } = require('mongodb');
+require('dotenv').config();
+
+const DEV_DB_NAME = 'vrms-test'; // Match the DEV_DB_NAME from cloneOrSyncProjects.js
+const COLLECTIONS = {
+ projects: 'projects',
+ recurring_events: 'recurringevents',
+ events: 'events',
+ checkins: 'checkins',
+};
+
+function printHelp() {
+ console.log(
+ `\nUsage: node clearDevCollections.js [options]\n\nOptions:\n --projects Only clear the 'projects' collection\n --recurring-events Only clear the 'recurringevents' collection\n --events Only clear the 'events' collection\n --checkins Only clear the 'checkins' collection\n --all Clear all supported collections\n --mock Only print what would be deleted, do not delete\n --help Show this help message and exit\n\nExamples:\n node clearDevCollections.js --projects\n node clearDevCollections.js --recurring-events\n node clearDevCollections.js --all\n node clearDevCollections.js --mock --all\n`,
+ );
+}
+
+function checkEnv() {
+ if (!process.env.DEV_DB_URI) {
+ throw new Error('DEV_DB_URI environment variable must be set.');
+ }
+}
+
+async function clearCollection(devClient, collectionName, isMock) {
+ const devDb = devClient.db(DEV_DB_NAME);
+ if (isMock) {
+ const count = await devDb.collection(collectionName).countDocuments();
+ console.log(
+ `[MOCK] Would delete ${count} documents from ${collectionName} in DEV[${DEV_DB_NAME}].`,
+ );
+ } else {
+ const result = await devDb.collection(collectionName).deleteMany({});
+ console.log(
+ `[INFO] Cleared ${result.deletedCount} documents from ${collectionName} in DEV[${DEV_DB_NAME}].`,
+ );
+ }
+}
+
+async function main() {
+ checkEnv();
+ const doProjects = process.argv.includes('--projects');
+ const doRecurring = process.argv.includes('--recurring-events');
+ const doEvent = process.argv.includes('--events');
+ const doCheckIn = process.argv.includes('--checkins');
+ const doAll = process.argv.includes('--all');
+ const isMock = process.argv.includes('--mock');
+ const isHelp = process.argv.includes('--help');
+ const noArgs = process.argv.length <= 2;
+ if (isHelp || noArgs) {
+ printHelp();
+ return;
+ }
+ const collectionsToClear = [];
+ if (doAll) {
+ collectionsToClear.push('projects', 'recurring_events', 'events', 'checkins');
+ } else {
+ if (doProjects) collectionsToClear.push('projects');
+ if (doRecurring) collectionsToClear.push('recurring_events');
+ if (doEvent) collectionsToClear.push('events');
+ if (doCheckIn) collectionsToClear.push('checkins');
+ }
+ if (collectionsToClear.length === 0) {
+ printHelp();
+ return;
+ }
+
+ let devClient;
+ try {
+ devClient = new MongoClient(process.env.DEV_DB_URI);
+ await devClient.connect();
+ for (const key of collectionsToClear) {
+ await clearCollection(devClient, COLLECTIONS[key], isMock);
+ }
+ if (!isMock) {
+ console.log('[SUCCESS] Collections cleared.');
+ }
+ } catch (err) {
+ console.error('[ERROR]', err.message);
+ process.exitCode = 1;
+ } finally {
+ if (devClient) await devClient.close();
+ }
+}
+
+if (require.main === module) {
+ main();
+}
diff --git a/backend/scripts/cloneOrSyncCollections.js b/backend/scripts/cloneOrSyncCollections.js
new file mode 100644
index 000000000..4b60b4a28
--- /dev/null
+++ b/backend/scripts/cloneOrSyncCollections.js
@@ -0,0 +1,215 @@
+/**
+ * Script to clone or sync collections from PROD to DEV MongoDB.
+ *
+ * Usage:
+ * node cloneOrSyncProjects.js [options]
+ *
+ * Options:
+ * --projects Clone/sync the 'projects' collection
+ * --recurring-events Clone/sync the 'recurringevents' collection
+ * --all Clone/sync all supported collections
+ * --initial Initial clone (insertMany, skip duplicates)
+ * --mock Only print what would be written, do not write
+ * --help Show this help message and exit
+ * (no flags) Show help (safety mode)
+ *
+ * Requires environment variables:
+ * PROD_DB_URI - MongoDB URI for PROD
+ * DEV_DB_URI - MongoDB URI for DEV
+ *
+ * Examples:
+ * node cloneOrSyncProjects.js --projects
+ * node cloneOrSyncProjects.js --recurring-events --initial
+ * node cloneOrSyncProjects.js --all --mock
+ */
+
+const { MongoClient } = require('mongodb');
+const mongoose = require('mongoose');
+require('dotenv').config();
+
+// Print help message for CLI usage
+function printHelp() {
+ console.log(
+ `\nUsage: node cloneOrSyncProjects.js [options]\n\nOptions:\n --projects Clone/sync the 'projects' collection\n --recurring-events Clone/sync the 'recurringevents' collection\n --all Clone/sync all supported collections\n --initial Initial clone (insertMany, skip duplicates)\n --mock Only print what would be written, do not write\n --help Show this help message and exit\n\nExamples:\n node cloneOrSyncProjects.js --projects\n node cloneOrSyncProjects.js --recurring-events\n node cloneOrSyncProjects.js --all --mock\n`,
+ );
+}
+
+// ---- CONSTANTS ----
+const COLLECTIONS = {
+ projects: {
+ name: 'projects',
+ model: 'Project',
+ },
+ recurring_events: {
+ name: 'recurringevents',
+ model: 'RecurringEvent',
+ },
+};
+const PROD_DB_NAME = 'db';
+const DEV_DB_NAME = 'vrms-test';
+// ---- CONSTANTS ----
+// const COLLECTION_NAME = 'projects';
+// const PROD_DB_NAME = 'vrms-test';
+// const DEV_DB_NAME = 'vrms-test-sync';
+
+/**
+ * Throws if required environment variables are missing.
+ */
+function checkEnv() {
+ if (!process.env.PROD_DB_URI || !process.env.DEV_DB_URI) {
+ throw new Error('Both PROD_DB_URI and DEV_DB_URI environment variables must be set.');
+ }
+}
+
+/**
+ * Connects to both PROD (via Mongoose) and DEV (via MongoClient) MongoDB databases.
+ * @returns {Promise<{prod: typeof mongoose, dev: MongoClient}>}
+ */
+async function connectDbs() {
+ // Connect PROD using Mongoose
+ await mongoose.connect(process.env.PROD_DB_URI, {
+ dbName: PROD_DB_NAME,
+ });
+ // Connect DEV using MongoClient
+ const dev = new MongoClient(process.env.DEV_DB_URI);
+ await dev.connect();
+ return { prod: mongoose, dev };
+}
+
+/**
+ * Fetches all projects from the PROD database using the Project Mongoose model.
+ * @returns {Promise>}
+ */
+
+async function fetchProdCollection(collectionKey) {
+ const { Project, RecurringEvent } = require('../models');
+ let Model;
+ if (collectionKey === 'projects') Model = Project;
+ else if (collectionKey === 'recurring_events') Model = RecurringEvent;
+ else throw new Error('Unknown collection: ' + collectionKey);
+ const docs = await Model.find({});
+ console.log(
+ `[INFO] Fetched ${docs.length} ${COLLECTIONS[collectionKey].name} from PROD[${PROD_DB_NAME}].`,
+ );
+ return docs.map((doc) => doc.toObject());
+}
+
+/**
+ * Performs the initial clone: insertMany, skip duplicates.
+ * @param {MongoClient} devClient
+ * @param {Array} projects
+ * @returns {Promise}
+ */
+
+async function initialClone(devClient, collectionKey, docs) {
+ const devDb = devClient.db(DEV_DB_NAME);
+ try {
+ const result = await devDb
+ .collection(COLLECTIONS[collectionKey].name)
+ .insertMany(docs, { ordered: false });
+ console.log(
+ `[INFO] Inserted ${result.insertedCount} ${COLLECTIONS[collectionKey].name} into DEV[${DEV_DB_NAME}].`,
+ );
+ } catch (err) {
+ if (err.code === 11000 || (err.writeErrors && err.writeErrors[0]?.code === 11000)) {
+ // Duplicate key error: some docs inserted, some skipped
+ const inserted = err.result?.result?.nInserted || err.result?.insertedCount || 0;
+ console.log(
+ `[WARN] Duplicate key error. Inserted ${inserted} new ${COLLECTIONS[collectionKey].name}, skipped duplicates.`,
+ );
+ } else {
+ throw err;
+ }
+ }
+}
+
+/**
+ * Syncs projects: upsert by _id using bulkWrite.
+ * @param {MongoClient} devClient
+ * @param {Array} projects
+ * @returns {Promise}
+ */
+
+async function syncCollection(devClient, collectionKey, docs) {
+ const devDb = devClient.db(DEV_DB_NAME);
+ const ops = docs.map((doc) => ({
+ updateOne: {
+ filter: { _id: doc._id },
+ update: { $set: doc },
+ upsert: true,
+ },
+ }));
+ const result = await devDb
+ .collection(COLLECTIONS[collectionKey].name)
+ .bulkWrite(ops, { ordered: false });
+ console.log(
+ `[INFO] Upserted ${result.upsertedCount}, matched ${result.matchedCount}, modified ${result.modifiedCount} ${COLLECTIONS[collectionKey].name} in DEV.`,
+ );
+}
+
+/**
+ * Main coordinator function.
+ */
+async function main() {
+ checkEnv();
+ const isInitial = process.argv.includes('--initial');
+ const isMock = process.argv.includes('--mock');
+ const isHelp = process.argv.includes('--help');
+ const doProjects = process.argv.includes('--projects');
+ const doRecurring = process.argv.includes('--recurring-events');
+ const doAll = process.argv.includes('--all');
+ const noArgs = process.argv.length <= 2;
+ if (isHelp || noArgs) {
+ printHelp();
+ return;
+ }
+ const collectionsToClone = [];
+ if (doAll) {
+ collectionsToClone.push('projects', 'recurring_events');
+ } else {
+ if (doProjects) collectionsToClone.push('projects');
+ if (doRecurring) collectionsToClone.push('recurring_events');
+ }
+ if (collectionsToClone.length === 0) {
+ printHelp();
+ return;
+ }
+
+ let devClient;
+ try {
+ const dbs = await connectDbs();
+ devClient = dbs.dev;
+ for (const collectionKey of collectionsToClone) {
+ const docs = await fetchProdCollection(collectionKey);
+ if (isMock) {
+ if (isInitial) {
+ console.log(
+ `[MOCK] Would insert ${docs.length} ${COLLECTIONS[collectionKey].name} into DEV[${DEV_DB_NAME}].`,
+ );
+ } else {
+ console.log(
+ `[MOCK] Would upsert ${docs.length} ${COLLECTIONS[collectionKey].name} into DEV[${DEV_DB_NAME}].`,
+ );
+ }
+ } else {
+ if (isInitial) {
+ await initialClone(devClient, collectionKey, docs);
+ } else {
+ await syncCollection(devClient, collectionKey, docs);
+ }
+ }
+ }
+ if (!isMock) console.log('[SUCCESS] Operation completed.');
+ } catch (err) {
+ console.error('[ERROR]', err.message);
+ process.exitCode = 1;
+ } finally {
+ // Close mongoose connection
+ await mongoose.disconnect();
+ if (devClient) await devClient.close();
+ }
+}
+
+if (require.main === module) {
+ main();
+}
diff --git a/backend/scripts/deleteProject/README.md b/backend/scripts/deleteProject/README.md
new file mode 100644
index 000000000..ad0ed1677
--- /dev/null
+++ b/backend/scripts/deleteProject/README.md
@@ -0,0 +1,143 @@
+# Delete Project Script - Modular Structure
+
+This directory contains the modular implementation of the project deletion script.
+
+## File Structure
+
+```
+deleteProject/
+├── README.md # This file
+├── index.js # Main orchestration file (entry point)
+├── config.js # Configuration constants (database names)
+├── utils.js # Utility functions (validation, CLI parsing, help)
+├── finders.js # Database query functions (find related records)
+├── displays.js # Display functions (show detailed information)
+└── deleters.js # Deletion functions (delete/update operations)
+```
+
+## Module Descriptions
+
+### `index.js`
+
+Main orchestration file that coordinates the entire deletion process.
+
+- Parses command-line arguments
+- Validates input and environment
+- Connects to MongoDB
+- Orchestrates the find → display → delete workflow
+- Handles errors and cleanup
+
+### `config.js`
+
+Configuration constants used throughout the script.
+
+- Database names (production, development, test)
+- Can be extended with other configuration values
+
+### `utils.js`
+
+Utility and helper functions.
+
+- `checkEnv()` - Validates required environment variables
+- `isValidObjectId()` - Validates MongoDB ObjectId format
+- `getProjectIdFromArgs()` - Extracts project ID from CLI arguments
+- `printHelp()` - Displays usage information
+
+### `finders.js`
+
+Database query functions for finding related records.
+
+- `findProject()` - Find the project by ID
+- `findProjectEvents()` - Find all events for the project
+- `findEventCheckIns()` - Find all check-ins for project events
+- `findProjectTeamMembers()` - Find team members
+- `findUsersReferencingProject()` - Find users with project references
+- `findRelatedRecurringEvents()` - Find recurring events
+
+### `displays.js`
+
+Display functions for showing detailed record information.
+
+- `displayUserDetails()` - Show user information
+- `displayEventDetails()` - Show event information
+- `displayCheckInDetails()` - Show check-in information (with user lookup)
+- `displayProjectTeamMemberDetails()` - Show team member information
+- `displayRecurringEventDetails()` - Show recurring event information
+
+### `deleters.js`
+
+Deletion and update functions.
+
+- `deleteCheckIns()` - Delete check-in records
+- `deleteProjectTeamMembers()` - Delete team member records
+- `updateUsersRemoveProject()` - Remove project references from users
+- `deleteEvents()` - Delete event records
+- `deleteRecurringEvents()` - Delete recurring event records
+- `deleteProject()` - Delete the project itself
+
+## Usage
+
+### From the deleteProject directory:
+
+```bash
+node index.js --project-id= [options]
+```
+
+### From the scripts directory (backwards compatible):
+
+```bash
+node deleteProjectAndAssociatedRecords.js --project-id= [options]
+```
+
+### Options:
+
+- `--project-id=` - MongoDB ObjectId of the project to delete (REQUIRED)
+- `--prod` - Operate on production database
+- `--live` - Operate on development/staging database
+- `--test` - Operate on test database
+- `--mock` - Dry run (no actual deletion)
+- `--execute` - Execute the deletion
+- `--help` - Show help message
+
+## Examples
+
+```bash
+# Dry run on production
+node index.js --project-id=644748563212e6001fbca24a --prod --mock
+
+# Execute on test database
+node index.js --project-id=644748563212e6001fbca24a --test --execute
+
+# Execute on production (with 5-second warning)
+node index.js --project-id=644748563212e6001fbca24a --prod --execute
+```
+
+## Safety Features
+
+1. **Mock mode** - Preview all changes before executing
+2. **5-second warning** - Countdown for production/live databases
+3. **Validation** - Validates project ID and environment variables
+4. **Detailed output** - Shows exactly what will be deleted
+5. **Orphan detection** - Identifies orphaned check-ins
+
+## Deletion Order
+
+The script follows this order to maintain referential integrity:
+
+1. Check-ins
+2. Project team members
+3. User references (updated, not deleted)
+4. Events
+5. Recurring events
+6. Project
+
+## Environment Requirements
+
+- `MIGRATION_DB_URI` - MongoDB connection string
+
+Load from `backend/.env` file.
+
+## See Also
+
+- Main documentation: `tmp/GUIDE.md`
+- Manual deletion steps for MongoDB Compass users
diff --git a/backend/scripts/deleteProject/config.js b/backend/scripts/deleteProject/config.js
new file mode 100644
index 000000000..4d83786d7
--- /dev/null
+++ b/backend/scripts/deleteProject/config.js
@@ -0,0 +1,14 @@
+/**
+ * Configuration constants for project deletion script
+ */
+
+// Database names
+const PROD_DB_NAME = 'db'; // Actual production database
+const DEV_DB_NAME = 'vrms-test'; // Development/staging database
+const DEV_TEST_DB_NAME = 'vrms-populate-projects-test'; // Test database for migration
+
+module.exports = {
+ PROD_DB_NAME,
+ DEV_DB_NAME,
+ DEV_TEST_DB_NAME,
+};
diff --git a/backend/scripts/deleteProject/deleters.js b/backend/scripts/deleteProject/deleters.js
new file mode 100644
index 000000000..6fa14e602
--- /dev/null
+++ b/backend/scripts/deleteProject/deleters.js
@@ -0,0 +1,175 @@
+/**
+ * Deletion functions for removing project-related records
+ */
+
+const { ObjectId } = require('mongodb');
+
+/**
+ * Delete check-ins
+ * @param {Db} db - MongoDB database instance
+ * @param {Array} checkIns - Array of check-in documents
+ * @param {boolean} isMock - If true, only print what would be deleted
+ * @returns {Promise}
+ */
+async function deleteCheckIns(db, checkIns, isMock) {
+ if (checkIns.length === 0) {
+ console.log('[INFO] No check-ins to delete.');
+ return;
+ }
+
+ if (isMock) {
+ console.log(`[MOCK] Would delete ${checkIns.length} check-in(s).`);
+ return;
+ }
+
+ const checkInIds = checkIns.map((ci) => ci._id);
+ const result = await db.collection('checkins').deleteMany({ _id: { $in: checkInIds } });
+
+ console.log(`[SUCCESS] Deleted ${result.deletedCount} check-in(s).`);
+}
+
+/**
+ * Delete project team members
+ * @param {Db} db - MongoDB database instance
+ * @param {string} projectId - The project ID
+ * @param {boolean} isMock - If true, only print what would be deleted
+ * @returns {Promise}
+ */
+async function deleteProjectTeamMembers(db, projectId, isMock) {
+ const count = await db.collection('projectteammembers').countDocuments({ projectId: projectId });
+
+ if (count === 0) {
+ console.log('[INFO] No project team members to delete.');
+ return;
+ }
+
+ if (isMock) {
+ console.log(`[MOCK] Would delete ${count} project team member(s).`);
+ return;
+ }
+
+ const result = await db.collection('projectteammembers').deleteMany({ projectId: projectId });
+
+ console.log(`[SUCCESS] Deleted ${result.deletedCount} project team member(s).`);
+}
+
+/**
+ * Update users to remove project references
+ * @param {Db} db - MongoDB database instance
+ * @param {string} projectId - The project ID
+ * @param {Array} users - Array of user documents
+ * @param {boolean} isMock - If true, only print what would be updated
+ * @returns {Promise}
+ */
+async function updateUsersRemoveProject(db, projectId, users, isMock) {
+ if (users.length === 0) {
+ console.log('[INFO] No users to update.');
+ return;
+ }
+
+ if (isMock) {
+ console.log(`[MOCK] Would update ${users.length} user(s) to remove project references.`);
+ users.forEach((user) => {
+ const hasInProjects = user.projects?.some((pid) => String(pid) === projectId);
+ const hasInManagedProjects = user.managedProjects?.includes(projectId);
+ console.log(` - User: ${user.name?.firstName} ${user.name?.lastName} (${user.email})`);
+ if (hasInProjects) console.log(` * Remove from 'projects' array`);
+ if (hasInManagedProjects) console.log(` * Remove from 'managedProjects' array`);
+ });
+ return;
+ }
+
+ const operations = users.map((user) => ({
+ updateOne: {
+ filter: { _id: user._id },
+ update: {
+ $pull: {
+ projects: new ObjectId(projectId),
+ managedProjects: projectId,
+ },
+ },
+ },
+ }));
+
+ const result = await db.collection('users').bulkWrite(operations, { ordered: false });
+
+ console.log(`[SUCCESS] Updated ${result.modifiedCount} user(s) to remove project references.`);
+}
+
+/**
+ * Delete events
+ * @param {Db} db - MongoDB database instance
+ * @param {string} projectId - The project ID
+ * @param {boolean} isMock - If true, only print what would be deleted
+ * @returns {Promise}
+ */
+async function deleteEvents(db, projectId, isMock) {
+ const count = await db.collection('events').countDocuments({ project: new ObjectId(projectId) });
+
+ if (count === 0) {
+ console.log('[INFO] No events to delete.');
+ return;
+ }
+
+ if (isMock) {
+ console.log(`[MOCK] Would delete ${count} event(s).`);
+ return;
+ }
+
+ const result = await db.collection('events').deleteMany({ project: new ObjectId(projectId) });
+
+ console.log(`[SUCCESS] Deleted ${result.deletedCount} event(s).`);
+}
+
+/**
+ * Delete recurring events
+ * @param {Db} db - MongoDB database instance
+ * @param {Array} recurringEvents - Array of recurring event documents
+ * @param {boolean} isMock - If true, only print what would be deleted
+ * @returns {Promise}
+ */
+async function deleteRecurringEvents(db, recurringEvents, isMock) {
+ if (recurringEvents.length === 0) {
+ console.log('[INFO] No recurring events to delete.');
+ return;
+ }
+
+ if (isMock) {
+ console.log(`[MOCK] Would delete ${recurringEvents.length} recurring event(s).`);
+ return;
+ }
+
+ const recurringEventIds = recurringEvents.map((re) => re._id);
+ const result = await db
+ .collection('recurringevents')
+ .deleteMany({ _id: { $in: recurringEventIds } });
+
+ console.log(`[SUCCESS] Deleted ${result.deletedCount} recurring event(s).`);
+}
+
+/**
+ * Delete the project
+ * @param {Db} db - MongoDB database instance
+ * @param {string} projectId - The project ID
+ * @param {boolean} isMock - If true, only print what would be deleted
+ * @returns {Promise}
+ */
+async function deleteProject(db, projectId, isMock) {
+ if (isMock) {
+ console.log(`[MOCK] Would delete project: ${projectId}`);
+ return;
+ }
+
+ const result = await db.collection('projects').deleteOne({ _id: new ObjectId(projectId) });
+
+ console.log(`[SUCCESS] Deleted ${result.deletedCount} project.`);
+}
+
+module.exports = {
+ deleteCheckIns,
+ deleteProjectTeamMembers,
+ updateUsersRemoveProject,
+ deleteEvents,
+ deleteRecurringEvents,
+ deleteProject,
+};
diff --git a/backend/scripts/deleteProject/displays.js b/backend/scripts/deleteProject/displays.js
new file mode 100644
index 000000000..ec04dad77
--- /dev/null
+++ b/backend/scripts/deleteProject/displays.js
@@ -0,0 +1,288 @@
+/**
+ * Display functions for showing detailed information about records
+ */
+
+const { ObjectId } = require('mongodb');
+
+/**
+ * Display detailed information about users
+ * @param {Array} users - Array of user documents
+ * @param {string} projectId - The project ID
+ * @returns {void}
+ */
+function displayUserDetails(users, projectId) {
+ if (users.length === 0) {
+ console.log(' No users reference this project.\n');
+ return;
+ }
+
+ console.log(`\n${'─'.repeat(60)}`);
+ console.log('USERS REFERENCING THIS PROJECT');
+ console.log(`${'─'.repeat(60)}\n`);
+
+ users.forEach((user, index) => {
+ const hasInProjects = user.projects?.some((pid) => String(pid) === projectId);
+ const hasInManagedProjects = user.managedProjects?.includes(projectId);
+
+ console.log(`User ${index + 1}/${users.length}:`);
+ console.log(` Name: ${user.name?.firstName || 'N/A'} ${user.name?.lastName || 'N/A'}`);
+ console.log(` Email: ${user.email || 'N/A'}`);
+ console.log(` User ID: ${user._id}`);
+ console.log(` Access Level: ${user.accessLevel || 'N/A'}`);
+ console.log(` Is Active: ${user.isActive !== undefined ? user.isActive : 'N/A'}`);
+
+ if (hasInProjects) {
+ console.log(` ⚠ In 'projects' array (total projects: ${user.projects?.length || 0})`);
+ }
+ if (hasInManagedProjects) {
+ console.log(
+ ` ⚠ In 'managedProjects' array (total managed: ${user.managedProjects?.length || 0})`,
+ );
+ }
+
+ console.log('');
+ });
+}
+
+/**
+ * Display detailed information about events
+ * @param {Array} events - Array of event documents
+ * @returns {void}
+ */
+function displayEventDetails(events) {
+ if (events.length === 0) {
+ console.log(' No events associated with this project.\n');
+ return;
+ }
+
+ console.log(`\n${'─'.repeat(60)}`);
+ console.log('EVENTS ASSOCIATED WITH THIS PROJECT');
+ console.log(`${'─'.repeat(60)}\n`);
+
+ // Group events by type
+ const eventsByType = {};
+ events.forEach((event) => {
+ const type = event.eventType || 'Unknown';
+ if (!eventsByType[type]) {
+ eventsByType[type] = [];
+ }
+ eventsByType[type].push(event);
+ });
+
+ // Display summary by type
+ console.log('Event Summary by Type:');
+ Object.entries(eventsByType).forEach(([type, evts]) => {
+ console.log(` ${type}: ${evts.length} event(s)`);
+ });
+ console.log('');
+
+ // Display first 10 events in detail, then summarize the rest
+ const displayLimit = 10;
+ const eventsToDisplay = events.slice(0, displayLimit);
+
+ console.log(`Showing first ${eventsToDisplay.length} of ${events.length} event(s):\n`);
+
+ eventsToDisplay.forEach((event, index) => {
+ console.log(`Event ${index + 1}/${events.length}:`);
+ console.log(` Event ID: ${event._id}`);
+ console.log(` Name: ${event.name || 'N/A'}`);
+ console.log(` Type: ${event.eventType || 'N/A'}`);
+ console.log(` Location: ${event.hacknight || event.location?.city || 'N/A'}`);
+ console.log(` Date: ${event.date ? new Date(event.date).toLocaleDateString() : 'N/A'}`);
+ console.log(
+ ` Start Time: ${event.startTime ? new Date(event.startTime).toLocaleString() : 'N/A'}`,
+ );
+ console.log(` Check-In Ready: ${event.checkInReady || false}`);
+
+ if (event.recurringEventLink?.recurringEventId) {
+ console.log(` ⚠ Part of recurring event: ${event.recurringEventLink.recurringEventId}`);
+ }
+
+ console.log('');
+ });
+
+ if (events.length > displayLimit) {
+ console.log(`... and ${events.length - displayLimit} more event(s)\n`);
+ }
+}
+
+/**
+ * Display detailed information about check-ins
+ * @param {Array} checkIns - Array of check-in documents
+ * @param {Array} events - Array of event documents for reference
+ * @param {Db} db - MongoDB database instance (optional, for user lookup)
+ * @returns {Promise}
+ */
+async function displayCheckInDetails(checkIns, events, db) {
+ if (checkIns.length === 0) {
+ console.log(' No check-ins associated with project events.\n');
+ return;
+ }
+
+ console.log(`\n${'─'.repeat(60)}`);
+ console.log('CHECK-INS ASSOCIATED WITH PROJECT EVENTS');
+ console.log(`${'─'.repeat(60)}\n`);
+
+ // Group check-ins by event
+ const checkInsByEvent = {};
+ const uniqueUserIds = new Set();
+
+ checkIns.forEach((checkIn) => {
+ const eventId = checkIn.eventId;
+ if (!checkInsByEvent[eventId]) {
+ checkInsByEvent[eventId] = [];
+ }
+ checkInsByEvent[eventId].push(checkIn);
+ if (checkIn.userId) {
+ uniqueUserIds.add(checkIn.userId);
+ }
+ });
+
+ console.log(`Total check-ins: ${checkIns.length}`);
+ console.log(`Spread across ${Object.keys(checkInsByEvent).length} event(s)`);
+ console.log(`Unique users: ${uniqueUserIds.size}\n`);
+
+ // Fetch user information if db is provided
+ let users = [];
+ if (db && uniqueUserIds.size > 0) {
+ const userIdArray = Array.from(uniqueUserIds);
+ users = await db
+ .collection('users')
+ .find({ _id: { $in: userIdArray.map((id) => new ObjectId(id)) } })
+ .toArray();
+ }
+
+ console.log('Check-ins by Event:');
+ Object.entries(checkInsByEvent).forEach(([eventId, eventCheckIns]) => {
+ const event = events.find((e) => String(e._id) === eventId);
+ const eventName = event?.name || 'Unknown Event';
+ const eventDate = event?.date ? new Date(event.date).toLocaleDateString() : 'Unknown Date';
+ console.log(` ${eventName} (${eventDate}): ${eventCheckIns.length} check-in(s)`);
+ });
+ console.log('');
+
+ // Display user information
+ if (users.length > 0 || uniqueUserIds.size > 0) {
+ console.log('Users who checked in:');
+
+ // Count check-ins per user
+ const checkInCountByUser = {};
+ checkIns.forEach((checkIn) => {
+ const userId = checkIn.userId;
+ if (!checkInCountByUser[userId]) {
+ checkInCountByUser[userId] = 0;
+ }
+ checkInCountByUser[userId]++;
+ });
+
+ users.forEach((user) => {
+ const userId = String(user._id);
+ const checkInCount = checkInCountByUser[userId] || 0;
+ const userName = `${user.name?.firstName || 'N/A'} ${user.name?.lastName || 'N/A'}`;
+ const userEmail = user.email || 'N/A';
+ console.log(` ${userName} (${userEmail}): ${checkInCount} check-in(s)`);
+ });
+
+ // Check for orphaned check-ins (userIds without matching users)
+ const foundUserIds = new Set(users.map((u) => String(u._id)));
+ const orphanedUserIds = Array.from(uniqueUserIds).filter((id) => !foundUserIds.has(id));
+
+ if (orphanedUserIds.length > 0) {
+ console.log('\n⚠️ Orphaned check-ins (user not found in database):');
+ orphanedUserIds.forEach((userId) => {
+ const checkInCount = checkInCountByUser[userId] || 0;
+ console.log(` User ID: ${userId}: ${checkInCount} check-in(s)`);
+ console.log(` → This user no longer exists in the database`);
+ });
+ }
+
+ console.log('');
+ }
+}
+
+/**
+ * Display detailed information about project team members
+ * @param {Array} teamMembers - Array of project team member documents
+ * @returns {void}
+ */
+function displayProjectTeamMemberDetails(teamMembers) {
+ if (teamMembers.length === 0) {
+ console.log(' No project team members.\n');
+ return;
+ }
+
+ console.log(`\n${'─'.repeat(60)}`);
+ console.log('PROJECT TEAM MEMBERS');
+ console.log(`${'─'.repeat(60)}\n`);
+
+ // Group by status
+ const activeMembers = teamMembers.filter((m) => m.teamMemberStatus === 'Active');
+ const inactiveMembers = teamMembers.filter((m) => m.teamMemberStatus !== 'Active');
+
+ console.log(`Total team members: ${teamMembers.length}`);
+ console.log(` Active: ${activeMembers.length}`);
+ console.log(` Inactive: ${inactiveMembers.length}\n`);
+
+ teamMembers.forEach((member, index) => {
+ console.log(`Team Member ${index + 1}/${teamMembers.length}:`);
+ console.log(` User ID: ${member.userId}`);
+ console.log(` Status: ${member.teamMemberStatus || 'N/A'}`);
+ console.log(` Role: ${member.roleOnProject || 'N/A'}`);
+ console.log(` VRMS Admin: ${member.vrmsProjectAdmin || false}`);
+ console.log(
+ ` Joined: ${member.joinedDate ? new Date(member.joinedDate).toLocaleDateString() : 'N/A'}`,
+ );
+
+ if (member.leftDate) {
+ console.log(` Left: ${new Date(member.leftDate).toLocaleDateString()}`);
+ console.log(` Left Reason: ${member.leftReason || 'N/A'}`);
+ }
+
+ console.log('');
+ });
+}
+
+/**
+ * Display information about related recurring events
+ * @param {Array} recurringEvents - Array of recurring event documents
+ * @returns {void}
+ */
+function displayRecurringEventDetails(recurringEvents) {
+ if (recurringEvents.length === 0) {
+ console.log(' No recurring events associated with this project.\n');
+ return;
+ }
+
+ console.log(`\n${'─'.repeat(60)}`);
+ console.log('RECURRING EVENTS ASSOCIATED WITH THIS PROJECT');
+ console.log(`${'─'.repeat(60)}\n`);
+
+ console.log(`${recurringEvents.length} recurring event(s) will be deleted.\n`);
+
+ recurringEvents.forEach((recurringEvent, index) => {
+ console.log(`Recurring Event ${index + 1}/${recurringEvents.length}:`);
+ console.log(` Recurring Event ID: ${recurringEvent._id}`);
+ console.log(` Project ID: ${recurringEvent.project || 'N/A'}`);
+
+ // Display recurring event details if available
+ if (recurringEvent.name) {
+ console.log(` Name: ${recurringEvent.name}`);
+ }
+ if (recurringEvent.eventType) {
+ console.log(` Type: ${recurringEvent.eventType}`);
+ }
+ if (recurringEvent.hacknight) {
+ console.log(` Location: ${recurringEvent.hacknight}`);
+ }
+
+ console.log('');
+ });
+}
+
+module.exports = {
+ displayUserDetails,
+ displayEventDetails,
+ displayCheckInDetails,
+ displayProjectTeamMemberDetails,
+ displayRecurringEventDetails,
+};
diff --git a/backend/scripts/deleteProject/finders.js b/backend/scripts/deleteProject/finders.js
new file mode 100644
index 000000000..b8b43e36e
--- /dev/null
+++ b/backend/scripts/deleteProject/finders.js
@@ -0,0 +1,128 @@
+/**
+ * Database query functions for finding related records
+ */
+
+const { ObjectId } = require('mongodb');
+
+/**
+ * Find the project by ID
+ * @param {Db} db - MongoDB database instance
+ * @param {string} projectId - The project ID
+ * @returns {Promise} Project document or null
+ */
+async function findProject(db, projectId) {
+ const project = await db.collection('projects').findOne({ _id: new ObjectId(projectId) });
+
+ if (!project) {
+ console.log(`[ERROR] Project not found: ${projectId}`);
+ return null;
+ }
+
+ console.log(`[INFO] Found project: "${project.name}" (${projectId})`);
+ return project;
+}
+
+/**
+ * Find all events associated with the project
+ * @param {Db} db - MongoDB database instance
+ * @param {string} projectId - The project ID
+ * @returns {Promise} Array of event documents
+ */
+async function findProjectEvents(db, projectId) {
+ const events = await db
+ .collection('events')
+ .find({ project: new ObjectId(projectId) })
+ .toArray();
+
+ console.log(`[INFO] Found ${events.length} event(s) for this project.`);
+ return events;
+}
+
+/**
+ * Find all check-ins associated with the project events
+ * @param {Db} db - MongoDB database instance
+ * @param {Array} events - Array of event documents
+ * @returns {Promise} Array of check-in documents
+ */
+async function findEventCheckIns(db, events) {
+ if (events.length === 0) {
+ console.log('[INFO] No events, so no check-ins to delete.');
+ return [];
+ }
+
+ const eventIds = events.map((event) => String(event._id));
+ const checkIns = await db
+ .collection('checkins')
+ .find({ eventId: { $in: eventIds } })
+ .toArray();
+
+ console.log(`[INFO] Found ${checkIns.length} check-in(s) for project events.`);
+ return checkIns;
+}
+
+/**
+ * Find all project team members for the project
+ * @param {Db} db - MongoDB database instance
+ * @param {string} projectId - The project ID
+ * @returns {Promise} Array of project team member documents
+ */
+async function findProjectTeamMembers(db, projectId) {
+ const teamMembers = await db
+ .collection('projectteammembers')
+ .find({ projectId: projectId })
+ .toArray();
+
+ console.log(`[INFO] Found ${teamMembers.length} project team member(s).`);
+ return teamMembers;
+}
+
+/**
+ * Find all users who reference this project
+ * @param {Db} db - MongoDB database instance
+ * @param {string} projectId - The project ID
+ * @returns {Promise} Array of user documents
+ */
+async function findUsersReferencingProject(db, projectId) {
+ const users = await db
+ .collection('users')
+ .find({
+ $or: [{ projects: new ObjectId(projectId) }, { managedProjects: projectId }],
+ })
+ .toArray();
+
+ console.log(`[INFO] Found ${users.length} user(s) referencing this project.`);
+ return users;
+}
+
+/**
+ * Check for recurring events that may be affected
+ * @param {Db} db - MongoDB database instance
+ * @param {Array} events - Array of event documents
+ * @returns {Promise} Array of recurring event IDs
+ */
+async function findRelatedRecurringEvents(db, events) {
+ const recurringEventIds = events
+ .filter((e) => e.recurringEventLink?.recurringEventId)
+ .map((e) => e.recurringEventLink.recurringEventId)
+ .filter((id, index, self) => self.indexOf(id) === index); // unique
+
+ if (recurringEventIds.length === 0) {
+ return [];
+ }
+
+ const recurringEvents = await db
+ .collection('recurringevents')
+ .find({ _id: { $in: recurringEventIds.map((id) => new ObjectId(id)) } })
+ .toArray();
+
+ return recurringEvents;
+}
+
+module.exports = {
+ findProject,
+ findProjectEvents,
+ findEventCheckIns,
+ findProjectTeamMembers,
+ findUsersReferencingProject,
+ findRelatedRecurringEvents,
+};
diff --git a/backend/scripts/deleteProject/index.js b/backend/scripts/deleteProject/index.js
new file mode 100644
index 000000000..c407b3dd0
--- /dev/null
+++ b/backend/scripts/deleteProject/index.js
@@ -0,0 +1,273 @@
+#!/usr/bin/env node
+
+/**
+ * Script to delete a project and all associated records from the database.
+ *
+ * This script performs a cascading deletion of:
+ * - Check-ins associated with project events
+ * - Project team members
+ * - Project references from users (projects and managedProjects arrays)
+ * - Events associated with the project
+ * - Recurring events associated with the project
+ * - The project itself
+ *
+ * Usage:
+ * node index.js --project-id= [options]
+ *
+ * Options:
+ * --project-id= The MongoDB ObjectId of the project to delete (REQUIRED)
+ * --prod Operate on production database (db)
+ * --live Operate on development/staging database (vrms-test)
+ * --test Operate on test database (vrms-populate-projects-test)
+ * --mock Only print what would be deleted, do not delete (dry run)
+ * --execute Execute the deletion (actually delete from the database)
+ * --help Show this help message and exit
+ * (no flags) Show help (safety mode)
+ *
+ * Requires environment variable:
+ * MIGRATION_DB_URI - MongoDB connection string for migration
+ *
+ * Examples:
+ * node index.js --project-id=644748563212e6001fbca24a --test --mock
+ * node index.js --project-id=644748563212e6001fbca24a --test --execute
+ * node index.js --project-id=644748563212e6001fbca24a --live --execute
+ * node index.js --project-id=644748563212e6001fbca24a --prod --execute
+ *
+ * See documentation: tmp/GUIDE.md
+ */
+
+const { MongoClient } = require('mongodb');
+const path = require('path');
+require('dotenv').config({ path: path.join(__dirname, '../../.env') });
+
+// Import modules
+const { PROD_DB_NAME, DEV_DB_NAME, DEV_TEST_DB_NAME } = require('./config');
+const { checkEnv, getProjectIdFromArgs, printHelp } = require('./utils');
+const {
+ findProject,
+ findProjectEvents,
+ findEventCheckIns,
+ findProjectTeamMembers,
+ findUsersReferencingProject,
+ findRelatedRecurringEvents,
+} = require('./finders');
+const {
+ displayUserDetails,
+ displayEventDetails,
+ displayCheckInDetails,
+ displayProjectTeamMemberDetails,
+ displayRecurringEventDetails,
+} = require('./displays');
+const {
+ deleteCheckIns,
+ deleteProjectTeamMembers,
+ updateUsersRemoveProject,
+ deleteEvents,
+ deleteRecurringEvents,
+ deleteProject,
+} = require('./deleters');
+
+/**
+ * Main coordinator function
+ */
+async function main() {
+ const isProd = process.argv.includes('--prod');
+ const isLive = process.argv.includes('--live');
+ const isTest = process.argv.includes('--test');
+ const isMock = process.argv.includes('--mock');
+ const isExecute = process.argv.includes('--execute');
+ const isHelp = process.argv.includes('--help');
+ const noArgs = process.argv.length <= 2;
+
+ if (isHelp || noArgs) {
+ printHelp();
+ return;
+ }
+
+ // Check environment only after help check
+ checkEnv();
+
+ // Get project ID
+ let projectId;
+ try {
+ projectId = getProjectIdFromArgs();
+ if (!projectId) {
+ console.error('[ERROR] --project-id is required.');
+ printHelp();
+ process.exitCode = 1;
+ return;
+ }
+ } catch (err) {
+ console.error(`[ERROR] ${err.message}`);
+ printHelp();
+ process.exitCode = 1;
+ return;
+ }
+
+ // Check that only one database flag is specified
+ const dbFlags = [isProd, isLive, isTest].filter(Boolean);
+ if (dbFlags.length > 1) {
+ console.error(
+ '[ERROR] Cannot specify multiple database flags. Choose one: --prod, --live, or --test.',
+ );
+ process.exitCode = 1;
+ return;
+ }
+
+ if (dbFlags.length === 0) {
+ console.error('[ERROR] Must specify a database: --prod, --live, or --test.');
+ printHelp();
+ process.exitCode = 1;
+ return;
+ }
+
+ if (isMock && isExecute) {
+ console.error('[ERROR] Cannot specify both --mock and --execute. Choose one.');
+ process.exitCode = 1;
+ return;
+ }
+
+ if (!isMock && !isExecute) {
+ console.error('[ERROR] Must specify either --mock (dry run) or --execute (actual deletion).');
+ printHelp();
+ process.exitCode = 1;
+ return;
+ }
+
+ // Determine database name and mode label
+ let dbName, modeLabel;
+ if (isProd) {
+ dbName = PROD_DB_NAME;
+ modeLabel = 'PRODUCTION';
+ } else if (isLive) {
+ dbName = DEV_DB_NAME;
+ modeLabel = 'DEVELOPMENT/STAGING';
+ } else {
+ dbName = DEV_TEST_DB_NAME;
+ modeLabel = 'TEST';
+ }
+
+ console.log(`\n${'='.repeat(60)}`);
+ console.log(`DELETE PROJECT AND ASSOCIATED RECORDS`);
+ console.log(
+ `Mode: ${modeLabel} ${
+ isMock ? '(DRY RUN - No changes will be made)' : '(EXECUTE - Will DELETE from database)'
+ }`,
+ );
+ console.log(`Database: ${dbName}`);
+ console.log(`Project ID: ${projectId}`);
+ console.log(`${'='.repeat(60)}\n`);
+
+ if ((isProd || isLive) && isExecute) {
+ console.log(
+ `[WARNING] Operating on ${modeLabel} database with EXECUTE mode. This will DELETE data!`,
+ );
+ console.log(`[WARNING] Press Ctrl+C within 5 seconds to cancel...`);
+ await new Promise((resolve) => setTimeout(resolve, 5000)); // 5 seconds for prod/live
+ }
+
+ let client;
+ try {
+ // Connect to MongoDB
+ client = new MongoClient(process.env.MIGRATION_DB_URI);
+ await client.connect();
+ console.log('[INFO] Connected to MongoDB.\n');
+
+ const db = client.db(dbName);
+
+ // Step 0: Verify project exists
+ const project = await findProject(db, projectId);
+ if (!project) {
+ console.log('[ERROR] Cannot proceed without a valid project.');
+ process.exitCode = 1;
+ return;
+ }
+
+ console.log('\n--- GATHERING RECORDS TO DELETE ---\n');
+
+ // Step 1: Find all events for this project
+ const events = await findProjectEvents(db, projectId);
+
+ // Step 2: Find all check-ins for those events
+ const checkIns = await findEventCheckIns(db, events);
+
+ // Step 3: Find all project team members
+ const teamMembers = await findProjectTeamMembers(db, projectId);
+
+ // Step 4: Find all users referencing this project
+ const users = await findUsersReferencingProject(db, projectId);
+
+ // Step 5: Check for recurring events
+ const recurringEvents = await findRelatedRecurringEvents(db, events);
+ if (recurringEvents.length > 0) {
+ console.log(
+ `[INFO] Found ${recurringEvents.length} recurring event(s) associated with this project.`,
+ );
+ }
+
+ console.log('\n--- DELETION PLAN SUMMARY ---\n');
+ console.log(` Check-ins to delete: ${checkIns.length}`);
+ console.log(` Project team members to delete: ${teamMembers.length}`);
+ console.log(` Users to update: ${users.length}`);
+ console.log(` Events to delete: ${events.length}`);
+ console.log(` Recurring events to delete: ${recurringEvents.length}`);
+ console.log(` Projects to delete: 1`);
+ console.log('');
+
+ // Display detailed information about all records
+ displayUserDetails(users, projectId);
+ displayProjectTeamMemberDetails(teamMembers);
+ displayEventDetails(events);
+ await displayCheckInDetails(checkIns, events, db);
+ displayRecurringEventDetails(recurringEvents);
+
+ if (isMock) {
+ console.log('[MOCK] This is a dry run. No changes will be made.\n');
+ } else {
+ console.log('[EXECUTE] Beginning deletion process...\n');
+ }
+
+ console.log('--- EXECUTING DELETIONS (in order) ---\n');
+
+ // Step 6: Delete check-ins
+ await deleteCheckIns(db, checkIns, isMock);
+
+ // Step 7: Delete project team members
+ await deleteProjectTeamMembers(db, projectId, isMock);
+
+ // Step 8: Update users to remove project references
+ await updateUsersRemoveProject(db, projectId, users, isMock);
+
+ // Step 9: Delete events
+ await deleteEvents(db, projectId, isMock);
+
+ // Step 10: Delete recurring events
+ await deleteRecurringEvents(db, recurringEvents, isMock);
+
+ // Step 11: Delete the project
+ await deleteProject(db, projectId, isMock);
+
+ console.log(`\n${'='.repeat(60)}`);
+ if (isMock) {
+ console.log('[MOCK] Dry run completed. No changes were made.');
+ } else {
+ console.log('[SUCCESS] Project and all associated records deleted successfully.');
+ }
+ console.log(`${'='.repeat(60)}\n`);
+ } catch (err) {
+ console.error('[ERROR]', err.message);
+ console.error(err.stack);
+ process.exitCode = 1;
+ } finally {
+ if (client) {
+ await client.close();
+ console.log('[INFO] Database connection closed.');
+ }
+ }
+}
+
+if (require.main === module) {
+ main();
+}
+
+module.exports = { main };
diff --git a/backend/scripts/deleteProject/utils.js b/backend/scripts/deleteProject/utils.js
new file mode 100644
index 000000000..481a52f05
--- /dev/null
+++ b/backend/scripts/deleteProject/utils.js
@@ -0,0 +1,56 @@
+/**
+ * Utility functions for project deletion script
+ */
+
+const { ObjectId } = require('mongodb');
+const { PROD_DB_NAME, DEV_DB_NAME, DEV_TEST_DB_NAME } = require('./config');
+
+/**
+ * Validate required environment variables
+ */
+function checkEnv() {
+ if (!process.env.MIGRATION_DB_URI) {
+ throw new Error('MIGRATION_DB_URI environment variable must be set.');
+ }
+}
+
+/**
+ * Validate if a string is a valid MongoDB ObjectId
+ * @param {string} id - The ID to validate
+ * @returns {boolean}
+ */
+function isValidObjectId(id) {
+ return ObjectId.isValid(id) && String(new ObjectId(id)) === id;
+}
+
+/**
+ * Extract project ID from command line arguments
+ * @returns {string|null} Project ID or null if not found
+ */
+function getProjectIdFromArgs() {
+ const projectIdArg = process.argv.find((arg) => arg.startsWith('--project-id='));
+ if (!projectIdArg) return null;
+
+ const projectId = projectIdArg.split('=')[1];
+ if (!projectId || !isValidObjectId(projectId)) {
+ throw new Error(`Invalid project ID: ${projectId}`);
+ }
+
+ return projectId;
+}
+
+/**
+ * Print help message for CLI usage
+ */
+function printHelp() {
+ console.log(
+ `\nUsage: node deleteProjectAndAssociatedRecords.js --project-id= [options]\n\nOptions:\n --project-id= The MongoDB ObjectId of the project to delete (REQUIRED)\n --prod Operate on PRODUCTION database (${PROD_DB_NAME})\n --live Operate on development/staging database (${DEV_DB_NAME})\n --test Operate on test database (${DEV_TEST_DB_NAME})\n --mock Only print what would be deleted, do not delete (dry run)\n --execute Execute the deletion (actually delete from the database)\n --help Show this help message and exit\n\nExamples:\n node deleteProjectAndAssociatedRecords.js --project-id=644748563212e6001fbca24a --test --mock\n node deleteProjectAndAssociatedRecords.js --project-id=644748563212e6001fbca24a --test --execute\n node deleteProjectAndAssociatedRecords.js --project-id=644748563212e6001fbca24a --live --execute\n node deleteProjectAndAssociatedRecords.js --project-id=644748563212e6001fbca24a --prod --execute\n\nDocumentation:\n See tmp/GUIDE.md for details\n`,
+ );
+}
+
+module.exports = {
+ checkEnv,
+ isValidObjectId,
+ getProjectIdFromArgs,
+ printHelp,
+};
diff --git a/backend/scripts/deleteProjectAndAssociatedRecords.js b/backend/scripts/deleteProjectAndAssociatedRecords.js
new file mode 100644
index 000000000..0ce81d0a3
--- /dev/null
+++ b/backend/scripts/deleteProjectAndAssociatedRecords.js
@@ -0,0 +1,16 @@
+#!/usr/bin/env node
+
+/**
+ * Wrapper script for backwards compatibility.
+ * The actual implementation has been moved to ./deleteProject/index.js
+ *
+ * This script simply delegates to the modular implementation.
+ */
+
+const { main } = require('./deleteProject/index');
+
+if (require.main === module) {
+ main();
+}
+
+module.exports = require('./deleteProject/index');
diff --git a/backend/scripts/python/env/Duplicate Removal.ipynb b/backend/scripts/python/env/Duplicate Removal.ipynb
new file mode 100644
index 000000000..5e59938a9
--- /dev/null
+++ b/backend/scripts/python/env/Duplicate Removal.ipynb
@@ -0,0 +1,405 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "377dcf28-dc41-416c-85bd-03c723ac73c5",
+ "metadata": {},
+ "source": [
+ "# Setup\n",
+ "\n",
+ "For dev, you must have the backend api running on your computer. For prod, please change USER_API_URL to reflect the production url.\n",
+ "\n",
+ "Please also configure the `x-custom-required-header` within your `.env` file to have the correct value."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "bf6a5708-01b8-4439-b085-996a0b9309df",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import requests\n",
+ "import json\n",
+ "from dotenv import load_dotenv\n",
+ "import os\n",
+ "import re\n",
+ "from datetime import datetime\n",
+ "from functools import reduce\n",
+ "\n",
+ "load_dotenv()\n",
+ "custom_request_header = os.getenv(\"CUSTOM_REQUEST_HEADER\")\n",
+ "DATABASE_URL = os.getenv(\"DATABASE_URL\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "id": "3387bab6-f11c-47c2-9c7a-def83999f50e",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "USER_API_URL = 'http://localhost:3000/api/users'\n",
+ "HEADERS = { \"x-customrequired-header\": custom_request_header }"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "0c952dc5-c39e-4337-9043-16c1dbce38b3",
+ "metadata": {},
+ "source": [
+ "## Retrieve Users\n",
+ "\n",
+ "Retrieve a list of all users and the format it into a dictionary where users are hashed to their _id."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 42,
+ "id": "2345b8e1-5601-4852-89c0-7e01f1b15e04",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Get a List of all users\n",
+ "r = requests.get(USER_API_URL, headers=HEADERS)\n",
+ "users = json.loads(r.content)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 43,
+ "id": "0fb71fe2-9976-4d24-b308-d9f511d01192",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "user_dict = {}\n",
+ "for user in users:\n",
+ " user_dict[user['_id']] = user"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "e8658c5b-dc98-4954-befe-ddfc54668a25",
+ "metadata": {},
+ "source": [
+ "## Identify Capitalized Emails\n",
+ "\n",
+ "Create a function that identifies which users have capital characters in their email addresses. This function will return a dictionary of user ids hashed to the email that they all share called `duped_emails` and a set of tuples for capitalized emails addresses that don't have multiple user ids called `non_duped_capital_emails_with_ids`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "8c5646cc-e708-4212-b280-d56711d71f64",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def identify_problem_users(users):\n",
+ " users_with_capital_emails = [user for user in users if re.compile('[A-Z]').search(user['email'])]\n",
+ "\n",
+ " potential_duplicate_emails = set([user['email'].lower() for user in users_with_capital_emails])\n",
+ "\n",
+ " problem_users = {}\n",
+ " for user in users:\n",
+ " current_email = user['email'].lower()\n",
+ " if current_email in potential_duplicate_emails:\n",
+ " if problem_users.get(current_email, None) is not None:\n",
+ " problem_users[current_email].append(user['_id'])\n",
+ " else:\n",
+ " problem_users[current_email] = [user['_id']]\n",
+ "\n",
+ " non_duped_capital_emails_with_ids = set([(email, problem_users[email][0]) for email in problem_users.keys() if len(problem_users[email]) == 1])\n",
+ "\n",
+ " for email, user_id in non_duped_capital_emails_with_ids:\n",
+ " problem_users.pop(email)\n",
+ "\n",
+ " return problem_users, non_duped_capital_emails_with_ids"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "id": "feae3b2e-8bb8-43d2-9056-5db90b44708f",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "duped_emails, non_duped_capital_emails_with_ids = identify_problem_users(users)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "f8f0c346-9ac5-4e05-b3d7-25e68d6cbcf7",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "duped_emails"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "7b5d62f5-3625-4693-9471-8d49e7f4b6fe",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "non_duped_capital_emails_with_ids"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c762e074-04f9-4590-bef5-f4eaac0d3ac6",
+ "metadata": {},
+ "source": [
+ "## Fixing non-duped emails\n",
+ "\n",
+ "These functions will use the API to update user documents in the database that have an email with a capitalized character. To fix all such emails, run `fix_non_duped_capital_emails(non_duped_capital_emails_with_ids)`"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "id": "dba2cc1d-2df2-4322-a9c8-dc7351e9a1ac",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def update_user(user_id, user_data):\n",
+ " r = requests.patch(USER_API_URL + '/' + user_id, json=user_data, headers=HEADERS)\n",
+ " print(r.content)\n",
+ " \n",
+ "def fix_non_duped_capital_emails(emails_with_ids):\n",
+ " for email, user_id in emails_with_ids:\n",
+ " print(email, user_id)\n",
+ " update_user(user_id, {'email': email})"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "c8c5313c-05fd-4121-87ce-b4e175a112d6",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "fix_non_duped_capital_emails(non_duped_capital_emails_with_ids)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "75a6a051-8622-49d4-9508-0e6ac0526d6f",
+ "metadata": {},
+ "source": [
+ "## Removing duplicate users\n",
+ "\n",
+ "These following cells will order the userIds in the duped_emails dict from oldest to newest, merge the information from new user documents into the oldest one, update the original, and then delete the duplicates. It will also keep track of the duplicate user documents' userIds so that we can change any checkins later to have the userId of the original user document."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 47,
+ "id": "3f9e99a4-c67e-4109-913d-ee8cced7bb57",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Sort ids for each duped email by oldest to newest\n",
+ "\n",
+ "for lowercase_email in duped_emails.keys():\n",
+ " duped_emails[lowercase_email].sort(key=(lambda _id: user_dict[_id]['createdDate']))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "id": "6270f2b8-aa8d-446f-8ee8-7dcbac3a4cc7",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# This will be used later for updating checkins\n",
+ "ids_to_replace = {}"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "b2e9128e-b338-4456-ae0d-8772621536dd",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def merge_users(older_id, newer_id):\n",
+ " canonical_user = user_dict[older_id]\n",
+ " duplicate_user = user_dict[newer_id]\n",
+ " ids_to_replace[newer_id] = older_id\n",
+ "\n",
+ " # For any list fields, combine them\n",
+ " list_fields = ['skillsToMatch', 'projects', 'managedProjects']\n",
+ " for field in list_fields:\n",
+ " all_array_values = set(canonical_user.get(field, []) + duplicate_user.get(field, []))\n",
+ " canonical_user[field] = list(all_array_values)\n",
+ "\n",
+ " # For boolean fields, set to true if either is true\n",
+ " bool_fields = ['textingOk', 'isActive', 'newMember']\n",
+ " for field in bool_fields:\n",
+ " canonical_user[field] = canonical_user[field] or duplicate_user[field]\n",
+ "\n",
+ " # For fields about roles, take the most recent information\n",
+ " take_the_newer_fields = ['currentRole', 'desiredRole']\n",
+ " for field in take_the_newer_fields:\n",
+ " if len(duplicate_user.get(field, '')) > 0:\n",
+ " canonical_user[field] = duplicate_user[field]\n",
+ "\n",
+ " # Take the highest access level\n",
+ " access_level = ['user', 'admin', 'superadmin']\n",
+ " highest_access_level = max(access_level.index(canonical_user['accessLevel']), access_level.index(duplicate_user['accessLevel']))\n",
+ " canonical_user['accessLevel'] = access_level[highest_access_level]\n",
+ "\n",
+ " # Make user that email is all lower case\n",
+ " canonical_user['email'] = canonical_user['email'].lower()\n",
+ " \n",
+ " \n",
+ " return older_id"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 57,
+ "id": "78ccbbd6-7fea-4657-bf9a-850bd9e68505",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def delete_user(user_id):\n",
+ " r = requests.delete(USER_API_URL + '/' + user_id, headers=HEADERS)\n",
+ " print(r.content)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 79,
+ "id": "98f86221-5f32-4a1f-8537-bb40e967b17a",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "for lowercase_email in duped_emails.keys():\n",
+ " reduce(merge_users, duped_emails[lowercase_email])\n",
+ " correct_user_id = duped_emails[lower_case_email][0]\n",
+ " dupes = duped_emails[lower_case_email][1:]\n",
+ " update_user(correct_user_id, user_dict[correct_user_id])\n",
+ " for dupe in dupes:\n",
+ " delete_user(dupe)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "d57d8cac-0873-42ee-86ca-aa61e8be119f",
+ "metadata": {},
+ "source": [
+ "## Correcting Checkins\n",
+ "\n",
+ "With the following cells, we use pymongo becuase our API does not expose any endpoints for editing checkins. For each duplicate_id, we will find all checkins with that userId and replace it with the id of the original user document."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "id": "2d6d014b-71c4-44a0-8247-8d10f87a2f1a",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "{}"
+ ]
+ },
+ "execution_count": 9,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "ids_to_replace"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "id": "5f4ec365-ab7c-45fe-bfbe-b35cdbb0ec94",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from pymongo import MongoClient\n",
+ "client = MongoClient(DATABASE_URL)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "id": "8556cf83-a412-4ee0-bfde-0447b63d6ac4",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "db = client['vrms-test']"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "id": "87d77a37-c404-4aa9-9b0a-7db74039db17",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "col = db['checkins']"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 89,
+ "id": "ecd00e41-1f4c-4aec-97a9-bf64a92974a4",
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "UpdateResult({'n': 4, 'electionId': ObjectId('7fffffff0000000000000222'), 'opTime': {'ts': Timestamp(1754280535, 23), 't': 546}, 'nModified': 4, 'ok': 1.0, '$clusterTime': {'clusterTime': Timestamp(1754280535, 23), 'signature': {'hash': b'\\x00n\\x88\\xed\\xcc\\xb1\\x7f\\x99\\xf6@l\\xd4\\xa2N\\xb3\\xfa\\x9b\\xcd\\xec\\x7f', 'keyId': 7488330297243598876}}, 'operationTime': Timestamp(1754280535, 23), 'updatedExisting': True}, acknowledged=True)"
+ ]
+ },
+ "execution_count": 89,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "col.update_many({'userId': '633b9a74d98663001f8b5c46'}, {'$set': {'userId': '5e965e554e2fc70017aa3970'}})"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "2db985ba-fab9-4820-9d67-ed42f8f1ba03",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "for duplicate_user_id in ids_to_replace.keys():\n",
+ " col.update_many({'userId': duplicate_user_id}, {'$set': {'userId': ids_to_replace[duplicate_user_id]}})"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.8.10"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/backend/scripts/python/env/Populate Projects.ipynb b/backend/scripts/python/env/Populate Projects.ipynb
new file mode 100644
index 000000000..6978986f3
--- /dev/null
+++ b/backend/scripts/python/env/Populate Projects.ipynb
@@ -0,0 +1,554 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "b9b6c5e5-4b20-4407-9542-3bea81ab742e",
+ "metadata": {},
+ "source": [
+ "# Setup\n",
+ "For dev, you must have the backend api running on your computer. For prod, please change USER_API_URL to reflect the production url."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 21,
+ "id": "d04b046c-ad92-4f9b-a7d1-c900c1ff4581",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import requests\n",
+ "import json\n",
+ "import os\n",
+ "import re\n",
+ "import pprint as pp\n",
+ "from dotenv import load_dotenv\n",
+ "from bson.objectid import ObjectId\n",
+ "from datetime import datetime\n",
+ "from functools import reduce\n",
+ "from pymongo import MongoClient, ReturnDocument, UpdateOne\n",
+ "from pymongo.errors import BulkWriteError\n",
+ "\n",
+ "load_dotenv()\n",
+ "custom_request_header = os.getenv(\"CUSTOM_REQUEST_HEADER\")\n",
+ "DATABASE_URL = os.getenv(\"DATABASE_URL\")\n",
+ "\n",
+ "OPERATE_ON_LIVE_DATA = True"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "852bea67-8354-49df-b6fb-c766f305ee8a",
+ "metadata": {},
+ "source": [
+ "# Connect to database and check current list of DBs"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "id": "33d48fca-a40d-4619-b97b-46b598258967",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "['backup_db', 'testdb', 'vrms-populate-projects-test', 'vrms-slack-dev', 'vrms-slack-main', 'vrms-slack-staging', 'vrms-test', 'vrms-test-2', 'vrms-test-3', 'vrms-test-4', 'vrms-test-5', 'vrms-test-6', 'vrms-test-clone-project-sync', 'vrms-test-copy', 'vrms-test-sync', 'vrms-user-migration-test', 'admin', 'local']\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Connect to MongoDB\n",
+ "client = MongoClient(DATABASE_URL)\n",
+ "print(client.list_database_names())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "0e4d3414-f130-4e76-9506-efd468d401df",
+ "metadata": {},
+ "source": [
+ "# Create a new test database\n",
+ "\n",
+ "Define a source and copy for databases\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "id": "27434cbf-0ecd-477d-889b-9ecd3a53e6aa",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Operate on live data is: True\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(f\"Operate on live data is: {OPERATE_ON_LIVE_DATA}\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 23,
+ "id": "68a7e8a9-e3f3-4231-8424-8b8dd44f522f",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Database(MongoClient(host=['cluster0-shard-00-01.5buwz.mongodb.net:27017', 'cluster0-shard-00-02.5buwz.mongodb.net:27017', 'cluster0-shard-00-00.5buwz.mongodb.net:27017'], document_class=dict, tz_aware=False, connect=True, retrywrites=True, w='majority', authsource='admin', replicaset='atlas-rl9oiw-shard-0', tls=True), 'vrms-test')\n"
+ ]
+ }
+ ],
+ "source": [
+ "db_source = client['vrms-test']\n",
+ "db_copy = None\n",
+ "\n",
+ "if OPERATE_ON_LIVE_DATA:\n",
+ " db_copy = client['vrms-test']\n",
+ "else:\n",
+ " db_copy = client['vrms-populate-projects-test']\n",
+ "print(db_copy)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "6565ea84-e799-40d0-a56b-7859620db461",
+ "metadata": {},
+ "source": [
+ "# Drop all collections in test database (ONLY IF NECESSARY!)\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "id": "a4cb07f2-3e55-4a2e-8358-96bf67ebf354",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Dropped collection: users\n",
+ "Dropped collection: projects\n",
+ "[]\n"
+ ]
+ }
+ ],
+ "source": [
+ "if OPERATE_ON_LIVE_DATA == False:\n",
+ " for collection_name in db_copy.list_collection_names():\n",
+ " db_copy.drop_collection(collection_name)\n",
+ " print(f\"Dropped collection: {collection_name}\")\n",
+ " print(db_copy.list_collection_names())\n",
+ "else:\n",
+ " print('Skipping this step')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "141b69ae-a407-4c41-a551-33f547244eb0",
+ "metadata": {},
+ "source": [
+ "# Copy Users and Projects collections from source -> test databases\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 25,
+ "id": "fd46eb06-d246-455e-8f48-a4e5df0efc9a",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Skipping this step\n"
+ ]
+ }
+ ],
+ "source": [
+ "users_collection = db_source['users']\n",
+ "users = list(users_collection.find())\n",
+ "projects_collection = db_source['projects']\n",
+ "projects = list(projects_collection.find())\n",
+ "\n",
+ "users_copy = db_copy['users']\n",
+ "projects_copy = db_copy['projects']\n",
+ "\n",
+ "try:\n",
+ " if OPERATE_ON_LIVE_DATA == False:\n",
+ " users_copy.insert_many(users, ordered=False) # Copy source db users to test db users\n",
+ " projects_copy.insert_many(projects, ordered=False) # Copy source db projects to test db projects\n",
+ " print(db_copy.list_collection_names())\n",
+ " else:\n",
+ " print('Skipping database insertions')\n",
+ "except BulkWriteError as bwe:\n",
+ " print(\"BulkWriteError details:\")\n",
+ " print(bwe.details) # This contains info on which documents failed and why\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "0c8b8712-7654-4f42-96c2-3809d33d214a",
+ "metadata": {},
+ "source": [
+ "# Get Users with at least one managedProjects\n",
+ "\n",
+ "Retrieve a list of all users with at least one managedProject.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 26,
+ "id": "d4f52891-72c0-440c-8ef1-0f2102cebdb1",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "[{'__v': 0,\n",
+ " '_id': ObjectId('6481155fab091f001e30925b'),\n",
+ " 'accessLevel': 'admin',\n",
+ " 'createdDate': datetime.datetime(2023, 6, 7, 23, 40, 15, 196000),\n",
+ " 'currentRole': 'Product Manager',\n",
+ " 'desiredRole': 'Product Manager',\n",
+ " 'email': 'jhaeger30@gmail.com',\n",
+ " 'firstAttended': 'JUN 2023',\n",
+ " 'managedProjects': ['68a3e64ee2653c001fe3ff3b'],\n",
+ " 'name': {'firstName': 'Jack', 'lastName': 'Haeger'},\n",
+ " 'newMember': False,\n",
+ " 'projects': [],\n",
+ " 'skillsToMatch': [],\n",
+ " 'textingOk': False},\n",
+ " {'__v': 0,\n",
+ " '_id': ObjectId('66024c13e6a0050028e07948'),\n",
+ " 'accessLevel': 'user',\n",
+ " 'createdDate': datetime.datetime(2024, 3, 26, 4, 16, 19, 45000),\n",
+ " 'currentRole': 'PM',\n",
+ " 'desiredRole': 'PM',\n",
+ " 'email': 'jack.haeger@gmail.com',\n",
+ " 'firstAttended': 'MAR 2024',\n",
+ " 'isActive': True,\n",
+ " 'managedProjects': ['68a3e64ee2653c001fe3ff3b'],\n",
+ " 'name': {'firstName': 'Jack', 'lastName': 'Haeger-PM'},\n",
+ " 'newMember': True,\n",
+ " 'projects': [],\n",
+ " 'skillsToMatch': [],\n",
+ " 'textingOk': False},\n",
+ " {'__v': 3,\n",
+ " '_id': ObjectId('670dd397cace6a002abb20ce'),\n",
+ " 'accessLevel': 'admin',\n",
+ " 'createdDate': datetime.datetime(2024, 10, 15, 2, 29, 43, 441000),\n",
+ " 'currentRole': 'Full Stack Developer',\n",
+ " 'desiredRole': 'Full Stack Developer',\n",
+ " 'email': 'njames15@gmail.com',\n",
+ " 'firstAttended': 'OCT 2024',\n",
+ " 'isActive': True,\n",
+ " 'managedProjects': ['68a3e64ee2653c001fe3ff3b', '68a3e75ea19d60385b3938f8'],\n",
+ " 'name': {'firstName': 'James', 'lastName': 'Ng'},\n",
+ " 'newMember': False,\n",
+ " 'projects': [],\n",
+ " 'skillsToMatch': [],\n",
+ " 'textingOk': False},\n",
+ " {'__v': 0,\n",
+ " '_id': ObjectId('68d1ffc4e2653c001fe400c9'),\n",
+ " 'accessLevel': 'admin',\n",
+ " 'createdDate': datetime.datetime(2025, 9, 23, 2, 2, 44, 786000),\n",
+ " 'currentRole': 'Software Engineer',\n",
+ " 'desiredRole': 'Software Engineer',\n",
+ " 'email': 'simmigon.flagg@gmail.com',\n",
+ " 'firstAttended': 'SEP 2025',\n",
+ " 'isActive': True,\n",
+ " 'managedProjects': ['5edeac78ce228b001778facd'],\n",
+ " 'name': {'firstName': 'Simmigon', 'lastName': 'Flagg'},\n",
+ " 'newMember': True,\n",
+ " 'projects': [],\n",
+ " 'skillsToMatch': [],\n",
+ " 'textingOk': False}]\n"
+ ]
+ }
+ ],
+ "source": [
+ "query = {\n",
+ " \"managedProjects\": { \n",
+ " \"$exists\": True, \n",
+ " \"$not\": { \"$size\": 0 } \n",
+ " }\n",
+ "}\n",
+ "\n",
+ "target_users = list(users_copy.find(query))\n",
+ "pp.pprint(target_users)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "de61c365-ec09-4acf-b863-221067f988db",
+ "metadata": {},
+ "source": [
+ "# Create an dictionary called `projects_users`\n",
+ "\n",
+ "The dict has project IDs as keys and arrays of user IDs as values\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 27,
+ "id": "dd384405-c9bc-4b00-bb9b-8dcd4be0e9ba",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "{'5edeac78ce228b001778facd': [ObjectId('68d1ffc4e2653c001fe400c9')],\n",
+ " '68a3e64ee2653c001fe3ff3b': [ObjectId('6481155fab091f001e30925b'),\n",
+ " ObjectId('66024c13e6a0050028e07948'),\n",
+ " ObjectId('670dd397cace6a002abb20ce')],\n",
+ " '68a3e75ea19d60385b3938f8': [ObjectId('670dd397cace6a002abb20ce')]}\n"
+ ]
+ }
+ ],
+ "source": [
+ "projects_users = {}\n",
+ "\n",
+ "# Function to filter only projects with valid mongoose IDs\n",
+ "def filter_valid_mongoose_ids(id_list):\n",
+ " return [x for x in id_list if ObjectId.is_valid(x)]\n",
+ "\n",
+ "for user in target_users:\n",
+ " # Destructure id and managed projects from user\n",
+ " _id, managed_projects = user['_id'], user['managedProjects']\n",
+ "\n",
+ " # Filter projects\n",
+ " filtered_projects = filter_valid_mongoose_ids(managed_projects)\n",
+ "\n",
+ " for proj_id in filtered_projects:\n",
+ " if proj_id in projects_users:\n",
+ " projects_users[f\"{proj_id}\"].append(_id)\n",
+ " else:\n",
+ " projects_users[f\"{proj_id}\"] = [_id]\n",
+ "\n",
+ "pp.pprint(projects_users)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "a34d198a-ce32-41af-b4e2-2be590a6f5a6",
+ "metadata": {},
+ "source": [
+ "# Update `managedByUsers` field in Projects \n",
+ "\n",
+ "Update all project's `managedByUsers` array using bulk write"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 28,
+ "id": "f280d029-47ed-46ef-a8d1-731071600a49",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Project before update:\n",
+ "{'__v': 0,\n",
+ " '_id': ObjectId('68a3e64ee2653c001fe3ff3b'),\n",
+ " 'createdDate': datetime.datetime(2025, 8, 19, 2, 49, 50, 843000),\n",
+ " 'description': 'Testing...',\n",
+ " 'githubIdentifier': 'lkjlkj',\n",
+ " 'githubUrl': 'lkjlk',\n",
+ " 'googleDriveUrl': 'https://drive.google.com/drive/folders/1hAq0wyZKOaZLujqOYiaFv5PYgooISger?usp=drive_link',\n",
+ " 'hflaWebsiteUrl': 'lkjlkj',\n",
+ " 'managedByUsers': [],\n",
+ " 'name': 'Jacks Test Project',\n",
+ " 'partners': [],\n",
+ " 'projectStatus': 'Active',\n",
+ " 'recruitingCategories': [],\n",
+ " 'slackUrl': 'lkjlkj'}\n",
+ "Project before update:\n",
+ "{'__v': 0,\n",
+ " '_id': ObjectId('68a3e75ea19d60385b3938f8'),\n",
+ " 'createdDate': datetime.datetime(2025, 8, 19, 2, 54, 22, 871000),\n",
+ " 'description': 'afk',\n",
+ " 'githubIdentifier': 'afk',\n",
+ " 'githubUrl': 'afk',\n",
+ " 'googleDriveUrl': 'https://drive.google.com/test',\n",
+ " 'hflaWebsiteUrl': 'afk',\n",
+ " 'managedByUsers': [],\n",
+ " 'name': 'VRMS Test Project',\n",
+ " 'partners': [],\n",
+ " 'projectStatus': 'Active',\n",
+ " 'recruitingCategories': [],\n",
+ " 'slackUrl': 'afk'}\n",
+ "Project before update:\n",
+ "{'__v': 0,\n",
+ " '_id': ObjectId('5edeac78ce228b001778facd'),\n",
+ " 'createdDate': datetime.datetime(2020, 6, 8, 21, 24, 8, 313000),\n",
+ " 'description': 'VRMS is a tool used for the engagement, support, and '\n",
+ " 'retention of a network of volunteers.',\n",
+ " 'githubIdentifier': '226157870',\n",
+ " 'githubUrl': 'https://github.com/hackforla/VRMS',\n",
+ " 'googleDriveId': '1uxCkpcPtDjrhftBO-axU2g8hjXsxiLiS',\n",
+ " 'googleDriveUrl': 'https://drive.google.com/drive/folders/1uxCkpcPtDjrhftBO-axU2g8hjXsxiLiS',\n",
+ " 'hflaWebsiteUrl': 'https://www.hackforla.org/projects/vrms',\n",
+ " 'location': 'DTLA',\n",
+ " 'lookingDescription': '- Front end devs (any level) \\n'\n",
+ " ' - Back end devs (any level)',\n",
+ " 'managedByUsers': [],\n",
+ " 'name': 'VRMS',\n",
+ " 'partners': ['TBD'],\n",
+ " 'projectStatus': 'Active',\n",
+ " 'recruitingCategories': [],\n",
+ " 'slackUrl': 'https://hackforla.slack.com/archives/CRGH5HM0Q',\n",
+ " 'videoConferenceLink': 'https://us02web.zoom.us/j/4451450308?pwd=U0JobzZqN0xGSXBUUkRsNlB5YzJiQT09'}\n",
+ "Result: BulkWriteResult({'writeErrors': [], 'writeConcernErrors': [], 'nInserted': 0, 'nUpserted': 0, 'nMatched': 3, 'nModified': 3, 'nRemoved': 0, 'upserted': []}, acknowledged=True)\n"
+ ]
+ }
+ ],
+ "source": [
+ "operations = []\n",
+ "\n",
+ "for proj_id, user_ids in projects_users.items():\n",
+ " valid_user_ids = [uid for uid in user_ids if ObjectId.is_valid(uid)] \n",
+ "\n",
+ " proj = projects_copy.find_one({\"_id\": ObjectId(proj_id)})\n",
+ "\n",
+ " if proj:\n",
+ " print('Project before update:')\n",
+ " pp.pprint(proj)\n",
+ " \n",
+ " # Compile individual updates in operations \n",
+ " operations.append(UpdateOne(\n",
+ " {\"_id\": ObjectId(proj_id)}, # Filter\n",
+ " {\"$set\": {\"managedByUsers\": valid_user_ids}}, # Update\n",
+ " ))\n",
+ " else:\n",
+ " print(f\"No project with {proj_id} found\")\n",
+ "\n",
+ "# Execute the bulk write to update operations\n",
+ "result = projects_copy.bulk_write(operations)\n",
+ "\n",
+ "print(f\"Result: \", result)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "e50a4c4b-fe7a-45fa-bb4c-aab60ef2ffa5",
+ "metadata": {},
+ "source": [
+ "## Confirm Projects have been updated"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 29,
+ "id": "6fa0e77b-96bc-4117-83b8-59431f68ff5c",
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Project after update:\n",
+ "{'__v': 0,\n",
+ " '_id': ObjectId('68a3e64ee2653c001fe3ff3b'),\n",
+ " 'createdDate': datetime.datetime(2025, 8, 19, 2, 49, 50, 843000),\n",
+ " 'description': 'Testing...',\n",
+ " 'githubIdentifier': 'lkjlkj',\n",
+ " 'githubUrl': 'lkjlk',\n",
+ " 'googleDriveUrl': 'https://drive.google.com/drive/folders/1hAq0wyZKOaZLujqOYiaFv5PYgooISger?usp=drive_link',\n",
+ " 'hflaWebsiteUrl': 'lkjlkj',\n",
+ " 'managedByUsers': [ObjectId('6481155fab091f001e30925b'),\n",
+ " ObjectId('66024c13e6a0050028e07948'),\n",
+ " ObjectId('670dd397cace6a002abb20ce')],\n",
+ " 'name': 'Jacks Test Project',\n",
+ " 'partners': [],\n",
+ " 'projectStatus': 'Active',\n",
+ " 'recruitingCategories': [],\n",
+ " 'slackUrl': 'lkjlkj'}\n",
+ "Project after update:\n",
+ "{'__v': 0,\n",
+ " '_id': ObjectId('68a3e75ea19d60385b3938f8'),\n",
+ " 'createdDate': datetime.datetime(2025, 8, 19, 2, 54, 22, 871000),\n",
+ " 'description': 'afk',\n",
+ " 'githubIdentifier': 'afk',\n",
+ " 'githubUrl': 'afk',\n",
+ " 'googleDriveUrl': 'https://drive.google.com/test',\n",
+ " 'hflaWebsiteUrl': 'afk',\n",
+ " 'managedByUsers': [ObjectId('670dd397cace6a002abb20ce')],\n",
+ " 'name': 'VRMS Test Project',\n",
+ " 'partners': [],\n",
+ " 'projectStatus': 'Active',\n",
+ " 'recruitingCategories': [],\n",
+ " 'slackUrl': 'afk'}\n",
+ "Project after update:\n",
+ "{'__v': 0,\n",
+ " '_id': ObjectId('5edeac78ce228b001778facd'),\n",
+ " 'createdDate': datetime.datetime(2020, 6, 8, 21, 24, 8, 313000),\n",
+ " 'description': 'VRMS is a tool used for the engagement, support, and '\n",
+ " 'retention of a network of volunteers.',\n",
+ " 'githubIdentifier': '226157870',\n",
+ " 'githubUrl': 'https://github.com/hackforla/VRMS',\n",
+ " 'googleDriveId': '1uxCkpcPtDjrhftBO-axU2g8hjXsxiLiS',\n",
+ " 'googleDriveUrl': 'https://drive.google.com/drive/folders/1uxCkpcPtDjrhftBO-axU2g8hjXsxiLiS',\n",
+ " 'hflaWebsiteUrl': 'https://www.hackforla.org/projects/vrms',\n",
+ " 'location': 'DTLA',\n",
+ " 'lookingDescription': '- Front end devs (any level) \\n'\n",
+ " ' - Back end devs (any level)',\n",
+ " 'managedByUsers': [ObjectId('68d1ffc4e2653c001fe400c9')],\n",
+ " 'name': 'VRMS',\n",
+ " 'partners': ['TBD'],\n",
+ " 'projectStatus': 'Active',\n",
+ " 'recruitingCategories': [],\n",
+ " 'slackUrl': 'https://hackforla.slack.com/archives/CRGH5HM0Q',\n",
+ " 'videoConferenceLink': 'https://us02web.zoom.us/j/4451450308?pwd=U0JobzZqN0xGSXBUUkRsNlB5YzJiQT09'}\n"
+ ]
+ }
+ ],
+ "source": [
+ "for proj_id, user_ids in projects_users.items():\n",
+ " proj = projects_copy.find_one({\"_id\": ObjectId(proj_id)})\n",
+ " if proj:\n",
+ " print('Project after update:')\n",
+ " pp.pprint(proj)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "d7c1423a-b538-4d06-8268-93547755c199",
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3 (ipykernel)",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.8.10"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/backend/scripts/python/env/README.md b/backend/scripts/python/env/README.md
new file mode 100644
index 000000000..76ad4feda
--- /dev/null
+++ b/backend/scripts/python/env/README.md
@@ -0,0 +1,81 @@
+# Python Virtual Environment
+
+Welcome to the `scripts\python\env` folder of the VRMS backend. This folder contains a Jupyter notebook, dependencies for setting up the environment, and a `.gitignore` file for managing which files should be ignored by version control.
+
+## Prerequisites
+
+Before you begin, make sure you have Python installed on your machine. If you don't have Python installed yet, you can download and install it from the official website:
+
+[Download Python](https://www.python.org/downloads/)
+
+Once you have Python installed, you're ready to set up the virtual environment and install the necessary dependencies as described below.
+
+## Requirements
+
+Before you can run the Jupyter notebook, you will need to set up a Python virtual environment and install the required dependencies. Here's how you can do that:
+
+### 1. Set Up a Python Virtual Environment
+
+From within the `scripts` directory, run the following command to create a virtual environment:
+
+```
+python -m venv .
+```
+
+or
+
+```
+python3 -m venv venv
+```
+
+This will create a virtual environment within the current directory.
+
+### 2. Activate the Virtual Environment
+
+Once the virtual environment is created, you'll need to activate it.
+
+- On **Windows**, run:
+
+ ```
+ .\Scripts\activate
+ ```
+
+ If using **Ubuntu**, run:
+
+ ```
+ source venv/bin/activate
+ ```
+
+- On **MacOS/Linux**, run:
+
+ ```
+ source bin/activate
+ ```
+
+### 3. Install Dependencies
+
+With the virtual environment activated, you can now install the dependencies listed in `requirements.txt`:
+
+```
+pip install -r requirements.txt
+```
+
+### 4. Launch Jupyter Notebook
+
+After installing the required dependencies, you can start the Jupyter notebook by running the following command:
+
+```
+jupyter notebook
+```
+
+This will open the Jupyter notebook interface in your web browser, where you can navigate to and run the script.
+
+## .gitignore
+
+The `.gitignore` file in this directory is set to ignore all files, including the virtual environment, so that unnecessary files don't get committed to version control. If you wish to track changes to new files added to this directory, you will need to use a command like:
+
+```
+git add -f .\backend\scripts\python\env\your-file.file
+```
+
+where -f forces git to add and begin tracking that file.
diff --git a/backend/scripts/python/env/requirements.txt b/backend/scripts/python/env/requirements.txt
new file mode 100644
index 000000000..8a0827574
--- /dev/null
+++ b/backend/scripts/python/env/requirements.txt
@@ -0,0 +1,103 @@
+anyio<=4
+# argon2-cffi<=25.1.0
+argon2-cffi-bindings<=25.1.0
+arrow<=1.3.0
+asttokens<=3.0.0
+async-lru<=2.0.5
+# attrs<=25.3.0
+babel<=2.17.0
+# beautifulsoup4<=4.13.4
+bleach<=6.2.0
+# certifi<=2025.7.14
+cffi<=1.17.1
+charset-normalizer<=3.4.2
+# colorama<=0.4.6
+comm<=0.2.3
+debugpy<=1.8.15
+decorator<=5.2.1
+defusedxml<=0.7.1
+dnspython<=2.7.0
+dotenv<=0.9.9
+executing<=2.2.0
+fastjsonschema<=2.21.1
+fqdn<=1.5.1
+h11<=0.16.0
+httpcore<=1.0.9
+httpx<=0.28.1
+idna<=3.10
+ipykernel<=6.30.0
+ipython<=9.4.0
+ipython_pygments_lexers<=1.1.1
+ipywidgets<=8.1.7
+isoduration<=20.11.0
+jedi<=0.19.2
+Jinja2<=3.1.6
+json5<=0.12.0
+jsonpointer<=3.0.0
+jsonschema<=4.25.0
+jsonschema-specifications<=2025.4.1
+jupyter<=1.1.1
+jupyter-console<=6.6.3
+jupyter-events<=0.12.0
+jupyter-lsp<=2.2.6
+jupyter_client<=8.6.3
+jupyter_core<=5.8.1
+jupyter_server<=2.16.0
+jupyter_server_terminals<=0.5.3
+jupyterlab<=4.4.5
+jupyterlab_pygments<=0.3.0
+jupyterlab_server<=2.27.3
+jupyterlab_widgets<=3.0.15
+lark<=1.2.2
+MarkupSafe<=3.0.2
+matplotlib-inline<=0.1.7
+mistune<=3.1.3
+nbclient<=0.10.2
+nbconvert<=7.16.6
+nbformat<=5.10.4
+nest-asyncio<=1.6.0
+notebook<=7.4.4
+notebook_shim<=0.2.4
+overrides<=7.7.0
+packaging<=25.0
+pandocfilters<=1.5.1
+parso<=0.8.4
+platformdirs<=4.3.8
+prometheus_client<=0.22.1
+prompt_toolkit<=3.0.51
+psutil<=7.0.0
+pure_eval<=0.2.3
+pycparser<=2.22
+Pygments<=2.19.2
+pymongo<=4.13.2
+python-dateutil<=2.9.0.post0
+python-dotenv<=1.1.1
+python-json-logger<=3.3.0
+# pywinpty<=2.0.15
+PyYAML<=6.0.2
+pyzmq<=27.0.0
+referencing<=0.36.2
+requests<=2.32.4
+rfc3339-validator<=0.1.4
+# rfc3986-validator<=0.1.1
+# rfc3987-syntax<=1.1.0
+rpds-py<=0.26.0
+Send2Trash<=1.8.3
+setuptools<=80.9.0
+six<=1.17.0
+sniffio<=1.3.1
+soupsieve<=2.7
+stack-data<=0.6.3
+terminado<=0.18.1
+tinycss2<=1.4.0
+tornado<=6.5.1
+traitlets<=5.14.3
+types-python-dateutil<=2.9.0.20250708
+typing_extensions<=4.14.1
+uri-template<=1.3.0
+urllib3<=2.5.0
+wcwidth<=0.2.13
+webcolors<=24.11.1
+webencodings<=0.5.1
+websocket-client<=1.8.0
+widgetsnbextension<=4.0.14
diff --git a/backend/server.js b/backend/server.js
index 2d4bb9604..175730fc8 100644
--- a/backend/server.js
+++ b/backend/server.js
@@ -1,7 +1,7 @@
-const app = require("./app");
-const mongoose = require("mongoose");
+const app = require('./app');
+const mongoose = require('mongoose');
-const { Role } = require("./models");
+const { Role } = require('./models');
// Load config variables
const { CONFIG_DB } = require('./config/');
@@ -12,21 +12,14 @@ mongoose.Promise = global.Promise;
let server;
async function runServer(databaseUrl = CONFIG_DB.DATABASE_URL, port = CONFIG_DB.PORT) {
await mongoose
- .connect(databaseUrl, {
- useNewUrlParser: true,
- useCreateIndex: true,
- useUnifiedTopology: true,
- useFindAndModify: false,
- })
+ .connect(databaseUrl)
.catch((err) => err);
server = app
.listen(port, () => {
- console.log(
- `Mongoose connected from runServer() and is listening on ${port}`
- );
+ console.log(`Mongoose connected from runServer() and is listening on ${port}`);
})
- .on("error", (err) => {
+ .on('error', (err) => {
mongoose.disconnect();
return err;
});
@@ -35,7 +28,7 @@ async function runServer(databaseUrl = CONFIG_DB.DATABASE_URL, port = CONFIG_DB.
async function closeServer() {
await mongoose.disconnect().then(() => {
return new Promise((resolve, reject) => {
- console.log("Closing Mongoose connection. Bye");
+ console.log('Closing Mongoose connection. Bye');
server.close((err) => {
if (err) {
@@ -48,35 +41,30 @@ async function closeServer() {
});
}
-function initial() {
- Role.collection.estimatedDocumentCount((err, count) => {
- if (!err && count === 0) {
- new Role({
- name: "APP_USER",
- }).save((err) => {
- if (err) {
- console.log("error", err);
- }
+async function initial() {
+ try {
+ const count = await Role.collection.estimatedDocumentCount();
- console.log("added 'user' to roles collection");
- });
+ if (count === 0) {
+ await new Role({
+ name: "APP_USER",
+ }).save();
+ console.log("added 'user' to roles collection");
- new Role({
+ await new Role({
name: "APP_ADMIN",
- }).save((err) => {
- if (err) {
- console.log("error", err);
- }
-
- console.log("added 'moderator' to roles collection");
- });
+ }).save();
+ console.log("added 'moderator' to roles collection");
}
- });
+ } catch (err) {
+ console.log("error", err);
+ }
}
if (require.main === module) {
- runServer().catch((err) => console.error(err));
- initial();
+ runServer()
+ .then(() => initial())
+ .catch((err) => console.error(err));
}
module.exports = { app, runServer, closeServer };
diff --git a/backend/setup-test.js b/backend/setup-test.js
index 60e168cd4..847757a5e 100644
--- a/backend/setup-test.js
+++ b/backend/setup-test.js
@@ -1,6 +1,5 @@
// test-setup.js
const mongoose = require("mongoose");
-mongoose.set("useCreateIndex", true);
mongoose.promise = global.Promise;
const { MongoMemoryServer } = require("mongodb-memory-server");
@@ -32,28 +31,18 @@ async function dropAllCollections() {
}
}
let mongoServer;
-module.exports = {
- setupDB(databaseName) {
+const setupIntegrationDB = (databaseName) => {
// Connect to Mongoose
beforeAll(async () => {
- mongoServer = new MongoMemoryServer({
+ mongoServer = await MongoMemoryServer.create({
instance: { dbName: databaseName },
});
- const mongoUri = await mongoServer.getUri();
- const opts = {
- useNewUrlParser: true,
- useFindAndModify: false,
- useCreateIndex: true,
- useUnifiedTopology: true,
- };
- await mongoose.connect(mongoUri, opts, (err) => {
- if (err) console.error(err);
- });
- });
-
- // Cleans up database between each test
- afterEach(async () => {
- await removeAllCollections();
+ const mongoUri = mongoServer.getUri();
+ try {
+ await mongoose.connect(mongoUri);
+ } catch (err) {
+ console.error(err);
+ }
});
// Disconnect Mongoose
@@ -62,5 +51,9 @@ module.exports = {
await mongoose.connection.close();
await mongoServer.stop();
});
- },
+};
+
+module.exports = {
+ setupIntegrationDB,
+ setupDB: setupIntegrationDB,
};
diff --git a/backend/test/old-tests/auth.router.test.js b/backend/test/old-tests/auth.router.test.js
new file mode 100644
index 000000000..f7942272d
--- /dev/null
+++ b/backend/test/old-tests/auth.router.test.js
@@ -0,0 +1,205 @@
+const supertest = require('supertest');
+const app = require('../app');
+const request = supertest(app);
+
+const { setupDB } = require('../setup-test');
+setupDB('api-auth');
+
+const { CONFIG_AUTH } = require('../config/');
+const { User } = require('../models');
+
+
+// Create mock for EmailController
+const sendMailMock = jest.fn()
+jest.mock('../controllers/email.controller');
+const mockEmailController = require('../controllers/email.controller');
+mockEmailController.sendLoginLink.mockReturnValue({ sendMail: sendMailMock });
+
+beforeEach(() => {
+ sendMailMock.mockClear();
+ mockEmailController.sendLoginLink.mockClear();
+});
+
+const headers = {};
+headers['x-customrequired-header'] = CONFIG_AUTH.CUSTOM_REQUEST_HEADER;
+headers.Accept = 'application/json';
+
+// API Tests
+describe('CREATE User', () => {
+ test('Create user with POST to /users', async () => {
+ // Test Data
+ const submittedData = {
+ name: { firstName: 'test_first', lastName: 'test_last' },
+ email: 'test@test.com',
+ };
+
+ // Add a user using the API.
+ const res = await request.post('/api/users').send(submittedData).set(headers);
+
+ expect(res.status).toBe(201);
+
+ // Retrieve and compare the the User values using the DB.
+ const databaseUserQuery = await User.find();
+
+ const databaseUser = databaseUserQuery[0];
+
+ expect(databaseUserQuery.length).toBeGreaterThanOrEqual(1);
+ expect(databaseUser.name.firstName).toBe(submittedData.name.firstName);
+ expect(databaseUser.name.lastName).toBe(submittedData.name.lastName);
+
+ // Retrieve and compare the User values using the API.
+ const response = await request.get('/api/users').set(headers);
+ expect(response.statusCode).toBe(200);
+ const APIData = response.body[0];
+ expect(APIData.name.firstName).toBe(submittedData.name.firstName);
+ expect(APIData.name.lastName).toBe(submittedData.name.lastName);
+
+ });
+
+ test('Create user with POST to /auth/signup', async () => {
+ // setupDBRoles();
+ // Test Data
+ const goodUserData = {
+ name: { firstName: 'testname', lastName: 'testlast' },
+ email: 'test@test.com',
+ };
+
+ const res = await request
+ .post('/api/auth/signup')
+ .send(goodUserData)
+ .set(headers);
+
+ expect(res.status).toBe(201);
+ });
+});
+
+describe('SIGNUP Validation', () => {
+ test('Invalid data to /api/auth/signup returns 403', async () => {
+ // Test Data
+ const badUserData = {
+ firstName: 'test_first',
+ lastName: 'test_last',
+ email: 'test@test.com',
+ };
+
+ const res = await request
+ .post('/api/auth/signup')
+ .send(badUserData)
+ .set(headers);
+
+ expect(res.status).toBe(403);
+ const errorMessage = JSON.parse(res.text);
+
+ expect(errorMessage.errors).toEqual([
+ { msg: 'Invalid value', param: 'name.firstName', location: 'body' },
+ { msg: 'Invalid value', param: 'name.lastName', location: 'body' },
+ ]);
+ });
+
+ test('Existing user returns 400', async () => {
+ // Test Data
+ const userOneWithSameEmail = {
+ name: { firstName: 'one', lastName: 'two' },
+ email: 'test@test.com',
+ };
+
+ const userTwoWithSameEmail = {
+ name: { firstName: 'three', lastName: 'four' },
+ email: 'test@test.com',
+ };
+
+ await request
+ .post('/api/auth/signup')
+ .send(userOneWithSameEmail)
+ .set(headers);
+
+ const res2 = await request
+ .post('/api/auth/signup')
+ .send(userTwoWithSameEmail)
+ .set(headers);
+
+ expect(res2.status).toBe(400);
+ });
+
+});
+
+describe('SIGNIN User', () => {
+ test('User can signin and returns 200', async () => {
+ // Create user in DB
+ const goodUserData = {
+ name: {
+ firstName: 'Free',
+ lastName: 'Mason',
+ },
+ email: 'test@test.com',
+ accessLevel: 'admin',
+ };
+ await User.create(goodUserData);
+
+ // POST to the DB with that same data.
+ const res = await request
+ .post('/api/auth/signin')
+ .send(goodUserData)
+ .set(headers)
+ .set('Origin', 'localhost');
+
+ expect(res.status).toBe(200);
+ });
+});
+
+describe('SIGNIN Validation', () => {
+ test('Non admin user returns 401', async () => {
+ // Test Data
+
+ // Create user in DB
+ const notValidPermission = {
+ name: {
+ firstName: 'Free',
+ lastName: 'Mason',
+ },
+ email: 'test@test.com',
+ accessLevel: 'user',
+ };
+ await User.create(notValidPermission);
+
+ // POST to the DB with that same data.
+ const res = await request
+ .post('/api/auth/signin')
+ .send(notValidPermission)
+ .set(headers)
+ .set('Origin', 'localhost');
+
+ expect(res.status).toBe(401);
+ });
+
+ test('A non-valid email return 403', async () => {
+ // Create user in DB
+ const notValidEmailPayload = {
+ name: {
+ firstName: 'Free',
+ lastName: 'Mason',
+ },
+ email: 'test',
+ accessLevel: 'admin',
+ };
+ await User.create(notValidEmailPayload);
+
+ // POST to the DB with that same data.
+ const res = await request
+ .post('/api/auth/signin')
+ .send(notValidEmailPayload)
+ .set(headers);
+
+ expect(res.status).toBe(403);
+ const errorMessage = JSON.parse(res.text);
+
+ expect(errorMessage.errors).toEqual([
+ {
+ value: 'test',
+ msg: 'Invalid email',
+ param: 'email',
+ location: 'body',
+ },
+ ]);
+ });
+})
diff --git a/backend/test/old-tests/events.router.test.js b/backend/test/old-tests/events.router.test.js
new file mode 100644
index 000000000..5c55a32a0
--- /dev/null
+++ b/backend/test/old-tests/events.router.test.js
@@ -0,0 +1,153 @@
+const supertest = require("supertest");
+const app = require("../app");
+const CONFIG = require('../config/auth.config');
+
+const request = supertest(app);
+
+const { setupDB } = require("../setup-test");
+setupDB("api-events");
+
+const { Event } = require('../models');
+
+const headers = {};
+headers['x-customrequired-header'] = CONFIG.CUSTOM_REQUEST_HEADER;
+headers.Accept = 'application/json';
+
+// API Tests
+describe('CREATE', () => {
+ test('Create Event', async (done) => {
+ // Test Data
+ const submittedData = {
+ name: 'eventName',
+ };
+
+ // Submit an event
+ const res = await request
+ .post('/api/events/')
+ .set(headers)
+ .send(submittedData);
+ expect(res.status).toBe(201);
+
+ // Retrieve that event
+ const databaseEventQuery = await Event.find();
+ const databaseEvent = databaseEventQuery[0];
+ expect(databaseEventQuery.length).toBeGreaterThanOrEqual(1);
+ expect(databaseEvent.name).toBe(submittedData.name);
+ done();
+ });
+});
+
+describe('READ', () => {
+ test('GET Events list', async (done) => {
+ // Test Data
+ const submittedData = {
+ createdDate: '2020-05-20T21:16:44.498Z',
+ checkinReady: true,
+ };
+
+ // Add an event with a project using the API.
+ const res = await request.post("/api/events").send(submittedData).set(headers);
+
+ // Retrieve and compare the the Event values using the DB.
+ const databaseEventQuery = await Event.find();
+ const databaseEvent = databaseEventQuery[0];
+ expect(databaseEventQuery.length).toBeGreaterThanOrEqual(1);
+ expect(databaseEvent.createdDate).toStrictEqual(new Date(submittedData.createdDate));
+
+ // Retrieve and compare the the values using the API.
+ const response = await request.get('/api/events/').set(headers);
+ expect(response.statusCode).toBe(200);
+ const APIData = response.body[0];
+ expect(APIData.createdDate).toBe(submittedData.createdDate);
+ done();
+ });
+ test('GET Event by ID', async (done) => {
+ // Test Data
+ const submittedData = {
+ name: 'eventName',
+ location: {
+ // should we include address here?
+ city: 'Los Angeles',
+ state: 'California',
+ country: 'USA',
+ },
+ hacknight: 'Online', // DTLA, Westside, South LA, Online
+ eventType: 'Workshop', // Project Meeting, Orientation, Workshop
+ description: 'A workshop to do stuff',
+ date: 1594023390039,
+ startTime: 1594023390039, // start date and time of the event
+ endTime: 1594023390039, // end date and time of the event
+ hours: 2, // length of the event in hours
+ createdDate: 1594023390039, // date/time event was created
+ updatedDate: 1594023390039, // date/time event was last updated
+ checkInReady: false, // is the event open for check-ins?
+ owner: {
+ ownerId: 33, // id of user who created event
+ },
+ };
+
+ // Create Event by DB
+ const dbCreatedevent = await Event.create(submittedData);
+ const dbCreatedeventId = dbCreatedevent.id;
+ const dbCreatedEventIdURL = `/api/events/${dbCreatedeventId}`;
+
+ // Retrieve and compare the the values using the API.
+ const response = await request.get(dbCreatedEventIdURL).set(headers);
+ expect(response.statusCode).toBe(200);
+ const apiRetrievedEvent = await response.body;
+ expect(apiRetrievedEvent._id).toBe(dbCreatedeventId);
+
+ done();
+ });
+});
+
+describe('UPDATE', () => {
+ test('Update Event by ID with PATCH', async (done) => {
+ // Test Data
+ const submittedData = {
+ name: 'originalEventName',
+ };
+
+ // Submit an event
+ const res = await request
+ .post('/api/events/')
+ .set(headers)
+ .send(submittedData);
+ expect(res.status).toBe(201);
+
+ const updatedDataPayload = {
+ name: 'updateEventName',
+ };
+
+ // Update the event
+ const res2 = await request
+ .patch(`/api/events/${res.body._id}`)
+ .set(headers)
+ .send(updatedDataPayload);
+ expect(res2.status).toBe(200);
+
+ done();
+ });
+});
+
+describe('DELETE', () => {
+ test('Delete Event by ID with DELETE', async (done) => {
+ // Test Data
+ const submittedData = {
+ name: 'eventName',
+ };
+
+ // Submit an event
+ const res = await request
+ .post('/api/events/')
+ .set(headers)
+ .send(submittedData);
+ expect(res.status).toBe(201);
+
+ // Delete the event
+ const res2 = await request.delete(`/api/events/${res.body._id}/`).set(headers);
+ expect(res2.status).toBe(200);
+
+ done();
+ });
+});
diff --git a/backend/test/old-tests/projects.router.test.js b/backend/test/old-tests/projects.router.test.js
new file mode 100644
index 000000000..41028b68f
--- /dev/null
+++ b/backend/test/old-tests/projects.router.test.js
@@ -0,0 +1,229 @@
+const supertest = require('supertest');
+const app = require('../app');
+const request = supertest(app);
+const jwt = require('jsonwebtoken');
+const { CONFIG_AUTH } = require('../config');
+
+const { setupDB } = require('../setup-test');
+setupDB('api-projects');
+
+const { User } = require('../models');
+const CONFIG = require('../config/auth.config');
+
+const headers = {};
+headers['x-customrequired-header'] = CONFIG.CUSTOM_REQUEST_HEADER;
+headers.Accept = 'application/json';
+headers.authorization = 'Bearer sometoken';
+
+let token;
+
+describe('CREATE', () => {
+ beforeAll(async () => {
+ const submittedData = {
+ name: {
+ firstName: 'test',
+ lastName: 'user',
+ },
+ email: 'newtest@test.com',
+ };
+ const user = await User.create(submittedData);
+ const auth_origin = 'TEST';
+ token = jwt.sign({ id: user.id, role: user.accessLevel, auth_origin }, CONFIG_AUTH.JWT_SECRET, {
+ expiresIn: `${CONFIG_AUTH.ACCESS_TOKEN_EXPIRATION_SEC}s`,
+ });
+ });
+ test('Create a Project with POST to /api/projects/ without token', async (done) => {
+ // Test Data
+ const submittedData = {
+ name: 'projectName',
+ };
+
+ // Submit a project
+ const res = await request.post('/api/projects/').set(headers).send(submittedData);
+ expect(res.status).toBe(401);
+ done();
+ });
+
+ test('Create a Project with POST to /api/projects/', async (done) => {
+ // Test Data
+ const submittedData = {
+ name: 'projectName',
+ };
+
+ // Submit a project
+ const res = await request
+ .post('/api/projects/')
+ .set(headers)
+ .set('Cookie', [`token=${token}`])
+ .send(submittedData);
+ expect(res.status).toBe(201);
+ done();
+ });
+});
+
+describe('READ', () => {
+ test('Get all projects with GET to /api/projects/', async (done) => {
+ // Test Data
+ const submittedData = {
+ name: 'projectName',
+ };
+
+ // Submit a project
+ const res = await request
+ .post('/api/projects/')
+ .set(headers)
+ .set('Cookie', [`token=${token}`])
+ .send(submittedData);
+ expect(res.status).toBe(201);
+
+ // Get all projects
+ const res2 = await request.get('/api/projects/').set(headers);
+ expect(res2.status).toBe(200);
+
+ const APIData = res2.body[0];
+ expect(APIData.name).toBe(submittedData.name);
+ done();
+ });
+});
+
+describe('UPDATE', () => {
+ beforeAll(async () => {
+ const submittedData = {
+ name: {
+ firstName: 'test',
+ lastName: 'user',
+ },
+ email: 'newtest@test.com',
+ };
+ const user = await User.create(submittedData);
+ const auth_origin = 'TEST';
+ token = jwt.sign({ id: user.id, role: user.accessLevel, auth_origin }, CONFIG_AUTH.JWT_SECRET, {
+ expiresIn: `${CONFIG_AUTH.ACCESS_TOKEN_EXPIRATION_SEC}s`,
+ });
+ });
+ test('Update a project with PATCH to /api/projects/:id without a token', async (done) => {
+ // Test Data
+ const submittedData = {
+ name: 'projectName',
+ };
+
+ // Submit a project
+ const res = await request
+ .post('/api/projects/')
+ .set(headers)
+ .set('Cookie', [`token=${token}`])
+ .send(submittedData);
+ expect(res.status).toBe(201);
+
+ const updatedDataPayload = {
+ name: 'updatedProjectName',
+ };
+
+ // Update project
+ const res2 = await request
+ .put(`/api/projects/${res.body._id}`)
+ .set(headers)
+ .send(updatedDataPayload);
+ expect(res2.status).toBe(401);
+
+ // Get project
+ const res3 = await request.get(`/api/projects/${res.body._id}`).set(headers);
+ expect(res3.status).toBe(200);
+ done();
+ });
+ test('Update a project with PATCH to /api/projects/:id with a token', async (done) => {
+ // Test Data
+ const submittedData = {
+ name: 'projectName',
+ };
+
+ // Submit a project
+ const res = await request
+ .post('/api/projects/')
+ .set(headers)
+ .set('Cookie', [`token=${token}`])
+ .send(submittedData);
+ expect(res.status).toBe(201);
+
+ const updatedDataPayload = {
+ name: 'updatedProjectName',
+ };
+
+ // Update project
+ const res2 = await request
+ .put(`/api/projects/${res.body._id}`)
+ .set(headers)
+ .set('Cookie', [`token=${token}`])
+ .send(updatedDataPayload);
+ expect(res2.status).toBe(200);
+
+ // Get project
+ const res3 = await request
+ .get(`/api/projects/${res.body._id}`)
+ .set(headers)
+ .set('Cookie', [`token=${token}`]);
+ expect(res3.status).toBe(200);
+
+ const APIData = res3.body;
+ expect(APIData.name).toBe(updatedDataPayload.name);
+ done();
+ });
+});
+
+describe('DELETE', () => {
+ beforeAll(async () => {
+ const submittedData = {
+ name: {
+ firstName: 'test',
+ lastName: 'user',
+ },
+ email: 'newtest@test.com',
+ };
+ const user = await User.create(submittedData);
+ const auth_origin = 'TEST';
+ token = jwt.sign({ id: user.id, role: user.accessLevel, auth_origin }, CONFIG_AUTH.JWT_SECRET, {
+ expiresIn: `${CONFIG_AUTH.ACCESS_TOKEN_EXPIRATION_SEC}s`,
+ });
+ });
+ test('Delete a project with POST to /api/projects/:id without a token', async (done) => {
+ // Test Data
+ const submittedData = {
+ name: 'projectName',
+ };
+
+ // Submit a project
+ const res = await request
+ .post('/api/projects/')
+ .set(headers)
+ .set('Cookie', [`token=${token}`])
+ .send(submittedData);
+ expect(res.status).toBe(201);
+
+ // Delete project
+ const res2 = await request.patch(`/api/projects/${res.body._id}`).set(headers);
+ expect(res2.status).toBe(401);
+ done();
+ });
+ test('Delete a project with POST to /api/projects/:id with a token', async (done) => {
+ // Test Data
+ const submittedData = {
+ name: 'projectName',
+ };
+
+ // Submit a project
+ const res = await request
+ .post('/api/projects/')
+ .set(headers)
+ .set('Cookie', [`token=${token}`])
+ .send(submittedData);
+ expect(res.status).toBe(201);
+
+ // Delete project
+ const res2 = await request
+ .patch(`/api/projects/${res.body._id}`)
+ .set(headers)
+ .set('Cookie', [`token=${token}`]);
+ expect(res2.status).toBe(200);
+ done();
+ });
+});
diff --git a/backend/test/old-tests/users.router.test.js b/backend/test/old-tests/users.router.test.js
new file mode 100644
index 000000000..61d91791d
--- /dev/null
+++ b/backend/test/old-tests/users.router.test.js
@@ -0,0 +1,199 @@
+const supertest = require('supertest');
+const app = require('../app');
+const request = supertest(app);
+
+const { setupDB } = require('../setup-test');
+setupDB('api-users');
+
+const backendHeaders = process.env.CUSTOM_REQUEST_HEADER;
+describe('CREATE', () => {
+ test('Create a User with POST to /api/users/', async (done) => {
+ // Test Data
+ const submittedData = {
+ name: {
+ firstName: 'test',
+ lastName: 'user',
+ },
+ email: 'newtest@test.com',
+ };
+
+ // Submit a User
+ const res = await request
+ .post('/api/users/')
+ .set('Accept', 'application/json')
+ .set('x-customrequired-header', backendHeaders)
+ .send(submittedData);
+ expect(res.status).toBe(201);
+
+ done();
+ });
+});
+
+describe('READ', () => {
+ test('Get a list of Users with with GET to /api/users/', async (done) => {
+ // Test Data
+ const submittedData = {
+ name: {
+ firstName: 'test',
+ lastName: 'user',
+ },
+ email: 'newtest@test.com',
+ };
+
+ // Submit a User
+ const res = await request
+ .post('/api/users/')
+ .set('Accept', 'application/json')
+ .set('x-customrequired-header', backendHeaders)
+ .send(submittedData);
+ expect(res.status).toBe(201);
+
+ // Get all Users
+ const res2 = await request.get('/api/users/').set('x-customrequired-header', backendHeaders);
+ expect(res2.status).toBe(200);
+
+ const APIData = res2.body[0];
+ expect(APIData.name).toMatchObject(submittedData.name);
+
+ done();
+ });
+ test('Get a specific User by param with GET to /api/users?email=', async (done) => {
+ // Test Data
+ const submittedData = {
+ name: {
+ firstName: 'test',
+ lastName: 'user',
+ },
+ email: 'newtest@test.com',
+ };
+
+ // Submit a User
+ const res = await request
+ .post('/api/users/')
+ .set('Accept', 'application/json')
+ .set('x-customrequired-header', backendHeaders)
+ .send(submittedData);
+ expect(res.status).toBe(201);
+
+ // Get all Users
+ const res2 = await request
+ .get('/api/users?email=newtest@test.com')
+ .set('x-customrequired-header', backendHeaders);
+ expect(res2.status).toBe(200);
+
+ const APIData = res2.body[0];
+ expect(APIData.name).toMatchObject(submittedData.name);
+
+ done();
+ });
+
+ test('Get a specific User by UserId with GET to /api/users/:UserId', async (done) => {
+ // Test Data
+ const submittedData = {
+ name: {
+ firstName: 'test',
+ lastName: 'user',
+ },
+ email: 'newtest@test.com',
+ };
+
+ // Submit a User
+ const res = await request
+ .post('/api/users/')
+ .set('Accept', 'application/json')
+ .set('x-customrequired-header', backendHeaders)
+ .send(submittedData);
+ expect(res.status).toBe(201);
+
+ // Get User by UserId
+ const res2 = await request
+ .get(`/api/users/${res.body._id}`)
+ .set('x-customrequired-header', backendHeaders);
+ expect(res2.status).toBe(200);
+
+ const APIData = res2.body;
+ expect(APIData.email).toBe(submittedData.email);
+ expect(APIData.name).toMatchObject(submittedData.name);
+
+ done();
+ });
+});
+
+describe('UPDATE', () => {
+ test('Update a User with PATCH to /api/users/:UserId', async (done) => {
+ // Test Data
+ const submittedData = {
+ name: {
+ firstName: 'test',
+ lastName: 'user',
+ },
+ email: 'newtest@test.com',
+ };
+
+ // Submit a User
+ const res = await request
+ .post('/api/users/')
+ .set('Accept', 'application/json')
+ .set('x-customrequired-header', backendHeaders)
+ .send(submittedData);
+ expect(res.status).toBe(201);
+
+ const updatedEmail = {
+ email: 'newtest@test.com',
+ };
+
+ // Update User
+ const resUpdate = await request
+ .patch(`/api/users/${res.body._id}`)
+ .set('Accept', 'application/json')
+ .set('x-customrequired-header', backendHeaders)
+ .send(updatedEmail);
+ expect(resUpdate.status).toBe(200);
+ // TODO: The updated User call is not returning a repsonse. Uncomment below line and
+ // run to see.
+ // expect(resUpdate.name).toMatchObject(submittedData.name);
+
+ const res2 = await request
+ .get(`/api/users/${res.body._id}`)
+ .set('x-customrequired-header', backendHeaders);
+ expect(res2.status).toBe(200);
+
+ const APIData = res2.body;
+ expect(APIData.email).toBe(updatedEmail.email);
+ expect(APIData.name).toMatchObject(submittedData.name);
+
+ done();
+ });
+});
+
+describe('DELETE', () => {
+ test('Delete a specific user by Id with DELETE /api/users/:UserId', async (done) => {
+ // Test Data
+ const submittedData = {
+ name: {
+ firstName: 'test',
+ lastName: 'user',
+ },
+ email: 'newtest@test.com',
+ };
+
+ // Submit a User
+ const res = await request
+ .post('/api/users/')
+ .set('Accept', 'application/json')
+ .set('x-customrequired-header', backendHeaders)
+ .send(submittedData);
+ expect(res.status).toBe(201);
+
+ // Delete User
+ const res2 = await request
+ .delete(`/api/users/${res.body._id}`)
+ .set('x-customrequired-header', backendHeaders);
+ expect(res2.status).toBe(200);
+
+ const APIData = res2.body;
+ expect(APIData.name).toMatchObject(submittedData.name);
+
+ done();
+ });
+});
diff --git a/backend/test/user.integration.test.js b/backend/test/user.integration.test.js
new file mode 100644
index 000000000..1d3149b0c
--- /dev/null
+++ b/backend/test/user.integration.test.js
@@ -0,0 +1,135 @@
+const supertest = require('supertest');
+const app = require('../app');
+const request = supertest(app);
+
+const { setupIntegrationDB } = require('../setup-test');
+setupIntegrationDB('api-users');
+
+const backendHeaders = process.env.CUSTOM_REQUEST_HEADER;
+
+const submittedData = {
+ name: {
+ firstName: 'test',
+ lastName: 'user',
+ },
+ email: 'newtest@test.com',
+};
+var createdUserId = '';
+
+// @TODO: Fix failing test, require investigation. Please referece issue 2036
+describe.skip('CREATE', () => {
+ test('Create a User with POST to /api/users/', async () => {
+ // Submit a User
+ const res = await request
+ .post('/api/users/')
+ .set('Accept', 'application/json')
+ .set('x-customrequired-header', backendHeaders)
+ .send(submittedData);
+ expect(res.status).toBe(201);
+ expect(res.body.name).toMatchObject(submittedData.name);
+
+ createdUserId = res.body._id;
+
+ });
+
+ test('Fail when creating a User with duplicate email', async () => {
+ // Submit a User
+ const res = await request
+ .post('/api/users/')
+ .set('Accept', 'application/json')
+ .set('x-customrequired-header', backendHeaders)
+ .send(submittedData);
+ expect(res.status).toBe(409);
+ expect(res.body).toMatchObject({
+ error: { code: 11000, driver: true, name: 'MongoError', index: 0 },
+ message: 'User already exists',
+ });
+
+ });
+});
+
+// @TODO: Fix failing test, require investigation. Please referece issue 2036
+describe.skip('READ', () => {
+ test('Get a list of Users with with GET to /api/users/', async () => {
+ // Get all Users
+ const res = await request.get('/api/users/').set('x-customrequired-header', backendHeaders);
+ expect(res.status).toBe(200);
+
+ const APIData = res.body[0];
+ expect(APIData.name).toMatchObject(submittedData.name);
+
+ });
+ test('Get a specific User by param with GET to /api/users?email=', async () => {
+ // Get User by query of email
+ const res = await request
+ .get('/api/users?email=newtest@test.com')
+ .set('x-customrequired-header', backendHeaders);
+ expect(res.status).toBe(200);
+
+ const APIData = res.body[0];
+ expect(APIData.name).toMatchObject(submittedData.name);
+
+ });
+
+ test('Get a specific User by UserId with GET to /api/users/:UserId', async () => {
+ // Get User by UserId
+ const res = await request
+ .get(`/api/users/${createdUserId}`)
+ .set('x-customrequired-header', backendHeaders);
+ expect(res.status).toBe(200);
+
+ const APIData = res.body;
+ expect(APIData.email).toBe(submittedData.email);
+ expect(APIData.name).toMatchObject(submittedData.name);
+
+ });
+});
+
+// @TODO: Fix failing test, require investigation. Please referece issue 2036
+describe.skip('UPDATE', () => {
+ test('Update a User with PATCH to /api/users/:UserId', async () => {
+ const updatedEmail = {
+ email: 'newtest2@test.com',
+ };
+
+ // Update User
+ const resUpdate = await request
+ .patch(`/api/users/${createdUserId}`)
+ .set('Accept', 'application/json')
+ .set('x-customrequired-header', backendHeaders)
+ .send(updatedEmail);
+ expect(resUpdate.status).toBe(200);
+ expect(resUpdate.body.name).toMatchObject(submittedData.name);
+
+ const res2 = await request
+ .get(`/api/users/${createdUserId}`)
+ .set('x-customrequired-header', backendHeaders);
+ expect(res2.status).toBe(200);
+
+ const APIData = res2.body;
+ expect(APIData.email).toBe(updatedEmail.email);
+ expect(APIData.name).toMatchObject(submittedData.name);
+
+ });
+});
+
+// @TODO: Fix failing test, require investigation. Please referece issue 2036
+describe.skip('DELETE', () => {
+ test('Delete a specific user by Id with DELETE /api/users/:UserId', async () => {
+ // Delete User
+ const res = await request
+ .delete(`/api/users/${createdUserId}`)
+ .set('x-customrequired-header', backendHeaders);
+ expect(res.status).toBe(200);
+
+ const APIData = res.body;
+ expect(APIData.name).toMatchObject(submittedData.name);
+
+ // Check to see that deleted User is gone
+ const res2 = await request
+ .get(`/api/users/${createdUserId}`)
+ .set('x-customrequired-header', backendHeaders);
+ expect(res2.body).toEqual({});
+
+ });
+});
diff --git a/backend/validators/user.api.validator.js b/backend/validators/user.api.validator.js
index 88278bd73..6909f9221 100644
--- a/backend/validators/user.api.validator.js
+++ b/backend/validators/user.api.validator.js
@@ -6,7 +6,7 @@ async function validateCreateUserAPICall(req, res, next) {
await body('email', 'Invalid email')
.exists()
.isEmail()
- .normalizeEmail({ gmail_remove_dots: false })
+ .normalizeEmail({ gmail_remove_dots: false, gmail_remove_subaddress: false })
.run(req);
// Finds the validation errors in this request and wraps them in an object with handy functions
@@ -22,7 +22,7 @@ async function validateSigninUserAPICall(req, res, next) {
await body('email', 'Invalid email')
.exists()
.isEmail()
- .normalizeEmail({ gmail_remove_dots: false })
+ .normalizeEmail({ gmail_remove_dots: false, gmail_remove_subaddress: false })
.run(req);
// Finds the validation errors in this request and wraps them in an object with handy functions
diff --git a/backend/workers/closeCheckins.js b/backend/workers/closeCheckins.js
index a9dc0a0e8..4a5141521 100644
--- a/backend/workers/closeCheckins.js
+++ b/backend/workers/closeCheckins.js
@@ -1,80 +1,99 @@
module.exports = (cron, fetch) => {
+ // Check to see if any events are about to start,
+ // and if so, open their respective check-ins
+
+ const url =
+ process.env.NODE_ENV === 'prod'
+ ? 'https://www.vrms.io'
+ : `http://localhost:${process.env.BACKEND_PORT}`;
+ const headerToSend = process.env.CUSTOM_REQUEST_HEADER;
+
+ async function fetchEvents() {
+ try {
+ const res = await fetch(`${url}/api/events`, {
+ headers: {
+ 'x-customrequired-header': headerToSend,
+ },
+ });
+ const resJson = await res.json();
+
+ return resJson;
+ } catch (error) {
+ console.log(error);
+ }
+ }
+
+ async function updateEvents(eventsToUpdate) {
+ try {
+ const res = await fetch(`${url}/api/events/batchUpdate`, {
+ method: 'PATCH',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'x-customrequired-header': headerToSend,
+ },
+ body: JSON.stringify(eventsToUpdate),
+ });
+ if (!res.ok) throw new Error('Failed to update event');
+ return await res.json();
+ } catch (error) {
+ console.error('Error updating event:', error);
+ return null;
+ }
+ }
+ async function sortAndFilterEvents() {
+ const events = await fetchEvents();
+
+ // Get current time and set to date variable
+ const now = Date.now();
+
+ // Filter events if event date is after now but before thirty minutes from now
+ if (events && events.length > 0) {
+ const sortedEvents = events.filter((event) => {
+ if (!event.date) {
+ // handle if event date is null/undefined
+ // false meaning don't include in sortedEvents
+ return false;
+ }
+ // Calculate three hours from now
+ const threeHoursFromStartTime = new Date(event.date).getTime() + 10800000;
+ if (Number.isNaN(threeHoursFromStartTime)) return false;
+ return now >= threeHoursFromStartTime && event.checkInReady === true;
+ });
+
+ // console.log('Sorted events: ', sortedEvents);
+ return sortedEvents;
+ }
+ }
+
+ async function closeCheckins(events) {
+ if (events && events.length > 0) {
+ console.log('Closing check-ins');
+ // console.log('Closing event: ', event);
+ const batchEventsToUpdate = events.map((e) => ({
+ _id: e._id,
+ checkInReady: false,
+ }));
+ const updatedEvents = await updateEvents(batchEventsToUpdate);
+ if (updatedEvents) console.log('Updated events:', updatedEvents);
+ console.log('Check-ins closed');
+ } else {
+ console.log('No open events to close');
+ }
+ }
+
+ async function runTask() {
+ const eventsToClose = await sortAndFilterEvents().catch((err) => {
+ console.log(err);
+ });
- // Check to see if any events are about to start,
- // and if so, open their respective check-ins
-
- const url = process.env.NODE_ENV === 'prod' ? 'https://www.vrms.io' : 'http://localhost:4000';
- const headerToSend = process.env.CUSTOM_REQUEST_HEADER;
-
- async function fetchEvents() {
- try {
- const res = await fetch(`${url}/api/events`, {
- headers: {
- "x-customrequired-header": headerToSend
- }
- });
- const resJson = await res.json();
-
- return resJson;
- } catch(error) {
- console.log(error);
- };
- };
-
- async function sortAndFilterEvents() {
- const events = await fetchEvents();
-
- // Filter events if event date is after now but before thirty minutes from now
- if (events && events.length > 0) {
-
- const sortedEvents = events.filter(event => {
- const currentTimeISO = new Date().toISOString();
- const threeHoursFromStartTime = new Date(event.date).getTime() + 10800000;
- const threeHoursISO = new Date(threeHoursFromStartTime).toISOString();
-
- return (currentTimeISO > threeHoursISO) && (event.checkInReady === true);
- });
-
- // console.log('Sorted events: ', sortedEvents);
- return sortedEvents;
- };
- };
-
- async function closeCheckins(events) {
- if(events && events.length > 0) {
- events.forEach(async event => {
- // console.log('Closing event: ', event);
-
- await fetch(`${url}/api/events/${event._id}`, {
- method: "PATCH",
- headers: {
- "Content-Type": "application/json",
- "x-customrequired-header": headerToSend
- },
- body: JSON.stringify({ checkInReady: false })
- })
- .catch(err => {
- console.log(err);
- });
- });
- };
- };
-
- async function runTask() {
- console.log("Closing check-ins");
-
- const eventsToClose = await sortAndFilterEvents()
- .catch(err => {console.log(err)});
-
- await closeCheckins(eventsToClose)
- .catch(err => {console.log(err)});
-
- console.log("Check-ins closed");
- };
-
- const scheduledTask = cron.schedule('*/30 * * * *', () => {
- runTask();
+ await closeCheckins(eventsToClose).catch((err) => {
+ console.log(err);
});
+ }
+
+ const scheduledTask = cron.schedule('*/30 * * * *', () => {
+ runTask();
+ });
- return scheduledTask;
-};
\ No newline at end of file
+ return scheduledTask;
+};
diff --git a/backend/workers/createRecurringEvents.js b/backend/workers/createRecurringEvents.js
index 40bcafc0e..d5fce7dff 100644
--- a/backend/workers/createRecurringEvents.js
+++ b/backend/workers/createRecurringEvents.js
@@ -1,170 +1,227 @@
-module.exports = (cron, fetch) => {
-
- // Check to see if any recurring events are happening today,
- // and if so, check to see if an event has already been created
- // for it. If not, create one.
-
- let EVENTS;
- let RECURRING_EVENTS;
- let TODAY_DATE;
- let TODAY;
- const URL = process.env.NODE_ENV === 'prod' ? 'https://www.vrms.io' : 'http://localhost:4000';
-
- const headerToSend = process.env.CUSTOM_REQUEST_HEADER;
- const fetchEvents = async () => {
- try {
- const res = await fetch(`${URL}/api/events/`, {
- headers: {
- "x-customrequired-header": headerToSend
- }
- });
-
- EVENTS = await res.json();
-
- // return EVENTS;
- } catch(error) {
- console.log(error);
- };
- };
-
- const fetchRecurringEvents = async () => {
- try {
- const res = await fetch(`${URL}/api/recurringevents/`, {
- headers: {
- "x-customrequired-header": headerToSend
- }
- });
- RECURRING_EVENTS = await res.json();
-
- // return resJson;
- } catch(error) {
- console.log(error);
- };
- };
-
- async function filterAndCreateEvents() {
- TODAY_DATE = new Date();
- TODAY = TODAY_DATE.getDay();
- console.log("Date: ", TODAY_DATE, "Day: ", TODAY);
- const recurringEvents = RECURRING_EVENTS;
- // console.log("Today Day: ", TODAY);
- // Filter recurring events where the event date is today
- if (recurringEvents && recurringEvents.length > 0) {
- const filteredEvents = recurringEvents.filter(event => {
- const eventDay = new Date(event.date).getDay();
- // console.log("Event Day: ", eventDay);
- return (eventDay === TODAY);
- });
- // For each recurring event, check to see if an event already
- // exists for it and do something if true/false. Can't use
- // forEach function with async/await.
- for (filteredEvent of filteredEvents) {
- const eventExists = await checkIfEventExists(filteredEvent.name);
- // console.log('Event exists? ', eventExists);
- const eventDate = new Date(filteredEvent.date);
-
- if (eventExists) {
- console.log("Not going to run ceateEvent");
- } else {
- // Create new event
- const hours = eventDate.getHours();
- const minutes = eventDate.getMinutes();
- const seconds = eventDate.getSeconds();
- const milliseconds = eventDate.getMilliseconds();
-
- const yearToday = TODAY_DATE.getFullYear();
- const monthToday = TODAY_DATE.getMonth();
- const dateToday = TODAY_DATE.getDate();
-
- const newEventDate = new Date(yearToday, monthToday, dateToday, hours, minutes, seconds, milliseconds);
-
- const newEndTime = new Date(yearToday, monthToday, dateToday, hours + filteredEvent.hours, minutes, seconds, milliseconds)
-
- const eventToCreate = {
- name: filteredEvent.name && filteredEvent.name,
- hacknight: filteredEvent.hacknight && filteredEvent.hacknight,
- eventType: filteredEvent.eventType && filteredEvent.eventType,
- description: filteredEvent.eventDescription && filteredEvent.eventDescription,
- project: filteredEvent.project && filteredEvent.project,
- date: filteredEvent.date && newEventDate,
- startTime: filteredEvent.startTime && newEventDate,
- endTime: filteredEvent.endTime && newEndTime,
- hours: filteredEvent.hours && filteredEvent.hours
- }
- if (filteredEvent.hasOwnProperty("location")) {
- eventToCreate.location = {
- city: filteredEvent.location.city ? filteredEvent.location.city : 'REMOTE',
- state: filteredEvent.location.state ? filteredEvent.location.state : 'REMOTE',
- country: filteredEvent.location.country ? filteredEvent.location.country : 'REMOTE'
- };
- }
-
- const created = await createEvent(eventToCreate);
- console.log(created);
- };
- };
- };
- };
-
- async function checkIfEventExists(eventName) {
- const events = EVENTS;
- // const today = new Date();
-
- if (events && events.length > 0) {
- const filteredEvents = events.filter(event => {
- const eventDate = new Date(event.date);
- const year = eventDate.getFullYear();
- const month = eventDate.getMonth();
- const date = eventDate.getDate();
-
- const yearToday = TODAY_DATE.getFullYear();
- const monthToday = TODAY_DATE.getMonth();
- const dateToday = TODAY_DATE.getDate();
-
- return (year === yearToday && month === monthToday && date === dateToday && eventName === event.name);
- });
- console.log("Events already created: ", filteredEvents);
- return filteredEvents.length > 0 ? true : false;
- };
- };
-
- const createEvent = async (event) => {
- if(event) {
- const jsonEvent = JSON.stringify(event);
- const options = {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- "x-customrequired-header": headerToSend
- },
- body: jsonEvent
- }
-
- console.log('Running createEvent: ', jsonEvent);
-
- try {
- const response = await fetch(`${URL}/api/events/`, options);
- const resJson = await response.json();
- return resJson;
- } catch (error) {
- console.log(error);
- };
- };
- };
-
- async function runTask() {
- console.log("Creating today's events");
-
- await fetchEvents();
- await fetchRecurringEvents();
- await filterAndCreateEvents();
-
- console.log("Today's events are created");
-
- };
-
- const scheduledTask = cron.schedule('*/30 * * * *', () => {
- runTask();
+const { generateEventData } = require('./lib/generateEventData');
+
+//API CALLS to GET and POST
+/** GET
+ * Utility to fetch data from an API endpoint.
+ * @param {string} endpoint - The API endpoint to fetch data from.
+ * @param {string} URL - The base URL for API requests.
+ * @param {string} headerToSend - Custom request header.
+ * @returns {Promise} - Resolves to the fetched data or an empty array on failure.
+ */
+const fetchData = async (endpoint, URL, headerToSend, fetch) => {
+ try {
+ const res = await fetch(`${URL}${endpoint}`, {
+ headers: { 'x-customrequired-header': headerToSend },
+ });
+ if (!res?.ok) throw new Error(`Failed to fetch: ${endpoint}`);
+ return await res.json();
+ } catch (error) {
+ console.error(`Error fetching ${endpoint}:`, error);
+ return [];
+ }
+};
+
+/** POST
+ * Creates a new event by making a POST request to the events API.
+ * @param {Object} eventArray - The events array data to create.
+ * @returns {Promise} - The created event data or null on failure.
+ */
+const createEvents = async (eventArray, URL, headerToSend, fetch) => {
+ if (!eventArray) return null;
+
+ try {
+ const res = await fetch(`${URL}/api/events/`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'x-customrequired-header': headerToSend,
+ },
+ body: JSON.stringify(eventArray),
});
- return scheduledTask;
-};
\ No newline at end of file
+ if (!res.ok) throw new Error('Failed to create event');
+ return await res.json();
+ } catch (error) {
+ console.error('Error creating event:', error);
+ return null;
+ }
+};
+
+/**
+ * Checks if two dates are on the same day in UTC.
+ * @param {Date} eventDate - Event date.
+ * @param {Date} todayDate - Today's data.
+ * @returns {boolean} - True if both dates are on the same UTC day.
+ */
+const isSameUTCDate = (eventDate, todayDate) => {
+ return (
+ eventDate.getUTCFullYear() === todayDate.getUTCFullYear() &&
+ eventDate.getUTCMonth() === todayDate.getUTCMonth() &&
+ eventDate.getUTCDate() === todayDate.getUTCDate()
+ );
+};
+
+/**
+ * Checks if an event with the given name already exists for today's date.
+ * @param {string} recurringEventName - The name of the recurring event to check.
+ * @param {Date} today - Today's date in UTC.
+ * @returns {boolean} - True if the event exists, false otherwise.
+ */
+const doesEventExist = (recurringEventName, today, events) =>
+ events.some((event) => {
+ const eventDate = new Date(event.date);
+ return isSameUTCDate(eventDate, today) && event.name === recurringEventName;
+ });
+
+/**
+ * Adjusts an event date to Los_Angeles time, accounting for DST offsets.
+ * @param {Date} eventDate - The event date to adjust.
+ * @returns {Date} - The adjusted event date.
+ */
+const adjustToLosAngelesTime = (eventDate) => {
+ const tempDate = new Date(eventDate);
+ const losAngelesOffsetHours = new Intl.DateTimeFormat('en-US', {
+ timeZone: 'America/Los_Angeles',
+ timeZoneName: 'shortOffset',
+ })
+ .formatToParts(tempDate)
+ .find((part) => part.type === 'timeZoneName')
+ .value.slice(3);
+ const offsetMinutes = parseInt(losAngelesOffsetHours, 10) * 60;
+ return new Date(tempDate.getTime() + offsetMinutes * 60000);
+};
+
+/**
+ * Filters recurring events happening today and creates new events if they do not already exist.
+ * Adjusts for Daylight Saving Time (DST) by converting stored UTC dates to Los Angeles time.
+ * @param {Array} events - The list of existing events.
+ * @param {Array} recurringEvents - The list of recurring events to check.
+ * @param {string} URL - The base URL for API requests.
+ * @param {string} headerToSend - Custom header for authentication or request tracking.
+ * @param {Function} fetch - Fetch function for making API calls.
+ * @returns {Promise} - A promise that resolves when all events are processed.
+ */
+const filterAndCreateEvents = async (events, recurringEvents, URL, headerToSend, fetch) => {
+ const today = new Date();
+ const todayUTCDay = today.getUTCDay();
+ //2025-11-25
+ // const allLocalDays = [];
+ // const dateCheck = [];
+ // const eventNameExist = [];
+ // // filter recurring events for today and not already existing
+ const eventsToCreate = recurringEvents?.filter((recurringEvent) => {
+ // we're converting the stored UTC event date to local time to compare the system DOW with the event DOW
+ const localEventDate = adjustToLosAngelesTime(recurringEvent.date);
+ //Logs for checking
+ // allLocalDays.push(localEventDate.getUTCDay());
+ // dateCheck.push(localEventDate.getUTCDay() === todayUTCDay);
+ // eventNameExist.push(!doesEventExist(recurringEvent.name, today, events));
+ return (
+ localEventDate.getUTCDay() === todayUTCDay &&
+ !doesEventExist(recurringEvent.name, today, events)
+ );
+ });
+ // console.log(
+ // 'Event date\n',
+ // today,
+ // '\nToday\n',
+ // todayUTCDay,
+ // '\nAll days\n',
+ // allLocalDays,
+ // '\nDay vs All Days comparison (Bool)\n',
+ // dateCheck,
+ // '\nEvent exist or not (Bool)\n',
+ // eventNameExist,
+ // '\nList of events to create\n',
+ // eventsToCreate,
+ // );
+
+ //Check if event exists
+ if (!eventsToCreate || eventsToCreate?.length === 0) {
+ return 'No events for today.';
+ } else {
+ const batchEvents = [];
+ for (const event of eventsToCreate) {
+ // convert to local time for DST correction...
+ const correctedStartTime = adjustToLosAngelesTime(event.startTime);
+ const timeCorrectedEvent = {
+ ...event,
+ // ... then back to UTC for DB
+ date: correctedStartTime.toISOString(),
+ startTime: correctedStartTime.toISOString(),
+ };
+ // map/generate all event data with adjusted date, startTime
+ const eventToCreate = generateEventData(timeCorrectedEvent);
+ batchEvents.push(eventToCreate);
+ }
+ const createdEvents = await createEvents(batchEvents, URL, headerToSend, fetch);
+ if (createdEvents) console.log('Created events:', createdEvents);
+ return "Today's events have been created.";
+ }
+};
+
+/**
+ * Executes the task of fetching existing events and recurring events,
+ * filtering those that should occur today, and creating them if needed.
+ * @param {Function} fetch - Fetch function for making API requests.
+ * @param {string} URL - The base URL for API requests.
+ * @param {string} headerToSend - Custom header for authentication or request tracking.
+ * @returns {Promise} - A promise that resolves when all tasks are completed.
+ */
+const runTask = async (fetch, URL, headerToSend) => {
+ console.log("Creating today's events...");
+ const [events, recurringEvents] = await Promise.all([
+ fetchData('/api/events/', URL, headerToSend, fetch),
+ fetchData('/api/recurringevents/', URL, headerToSend, fetch),
+ ]);
+
+ const checkAndCreateEvents = await filterAndCreateEvents(
+ events,
+ recurringEvents,
+ URL,
+ headerToSend,
+ fetch,
+ );
+ console.log(checkAndCreateEvents);
+};
+
+/**
+ * Schedules the runTask function to execute periodically using a cron job.
+ * @param {Object} cron - The cron scheduling library.
+ * @param {Function} fetch - Fetch function for making API requests.
+ * @param {string} URL - The base URL for API requests.
+ * @param {string} headerToSend - Custom header for authentication or request tracking.
+ * @returns {Object} - The scheduled cron job instance.
+ */
+const scheduleTask = (cron, fetch, URL, headerToSend) => {
+ return cron.schedule('*/30 * * * *', () => {
+ runTask(fetch, URL, headerToSend).catch((error) => console.error('Error running task:', error));
+ });
+};
+
+/**
+ * Wrapper function to initialize the worker with dependencies in app.js
+ * @param {Object} cron - The cron scheduling library.
+ * @param {Function} fetch - Fetch function for making API requests.
+ * @returns {Object} - The scheduled cron job instance.
+ */
+const createRecurringEvents = (cron, fetch) => {
+ const URL =
+ process.env.NODE_ENV === 'prod'
+ ? 'https://www.vrms.io'
+ : `http://localhost:${process.env.BACKEND_PORT}`;
+ const headerToSend = process.env.CUSTOM_REQUEST_HEADER;
+
+ return scheduleTask(cron, fetch, URL, headerToSend);
+};
+
+module.exports = {
+ createRecurringEvents,
+ fetchData,
+ adjustToLosAngelesTime,
+ isSameUTCDate,
+ doesEventExist,
+ createEvents,
+ filterAndCreateEvents,
+ runTask,
+ scheduleTask,
+};
diff --git a/backend/workers/createRecurringEvents.test.js b/backend/workers/createRecurringEvents.test.js
new file mode 100644
index 000000000..2f905ef48
--- /dev/null
+++ b/backend/workers/createRecurringEvents.test.js
@@ -0,0 +1,377 @@
+const {
+ fetchData,
+ adjustToLosAngelesTime,
+ isSameUTCDate,
+ doesEventExist,
+ createEvents,
+ filterAndCreateEvents,
+ runTask,
+ scheduleTask,
+} = jest.requireActual('./createRecurringEvents');
+const { generateEventData } = require('./lib/generateEventData');
+
+const MockDate = require('mockdate');
+const cron = require('node-cron');
+
+jest.mock('./lib/generateEventData', () => ({
+ generateEventData: jest.fn((event) => ({
+ ...event,
+ generated: true,
+ })),
+}));
+
+jest.mock('node-fetch', () => jest.fn());
+const fetch = require('node-fetch');
+
+describe('createRecurringEvents Module Tests', () => {
+ const mockURL = 'http://localhost:3000';
+ const mockHeader = 'mock-header';
+ let mockEvents;
+ let mockRecurringEvents;
+
+ fetch.mockResolvedValue({
+ ok: true,
+ json: jest.fn().mockResolvedValue(mockEvents),
+ });
+
+ beforeEach(() => {
+ MockDate.set('2023-11-02T00:00:00Z');
+
+ mockEvents = [
+ { name: 'Event 1', date: '2023-11-02T19:00:00Z' },
+ { name: 'Event 2', date: '2023-11-02T07:00:00Z' },
+ ];
+ mockRecurringEvents = [
+ { name: 'Event 1', date: '2023-11-02T19:00:00Z' },
+ { name: 'Event 2', date: '2023-11-02T07:00:00Z' },
+ { name: 'Event 3', date: '2023-11-03T07:00:00Z' }, // Does not match today
+ ];
+
+ jest.clearAllMocks();
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ MockDate.reset();
+ });
+
+ describe('fetchData', () => {
+ it('should fetch data from the API endpoint', async () => {
+ fetch.mockResolvedValueOnce({
+ ok: true,
+ json: jest.fn().mockResolvedValue(mockEvents),
+ });
+
+ const result = await fetchData('/api/events/', mockURL, mockHeader, fetch);
+
+ expect(fetch).toHaveBeenCalledWith(`${mockURL}/api/events/`, {
+ headers: { 'x-customrequired-header': mockHeader },
+ });
+ expect(result).toEqual(mockEvents);
+ });
+
+ it('should handle API fetch failures', async () => {
+ fetch.mockRejectedValueOnce(new Error('Network error'));
+
+ const result = await fetchData('/api/events/', mockURL, mockHeader, fetch);
+
+ expect(fetch).toHaveBeenCalledTimes(1);
+ expect(result).toEqual([]);
+ });
+ });
+
+ describe('adjustToLosAngelesTime', () => {
+ it('should correctly adjust timestamps before DST starts (PST -8)', () => {
+ const utcTimestamp = new Date('2024-03-10T07:00:00Z'); // 7 AM UTC
+ const expectedLocal = new Date('2024-03-09T23:00:00Z'); // 11 PM PST (-8)
+
+ const result = adjustToLosAngelesTime(utcTimestamp);
+
+ expect(result.toISOString()).toBe(expectedLocal.toISOString());
+ });
+
+ it('should correctly adjust timestamps after DST starts (PDT -7)', () => {
+ const utcTimestamp = new Date('2024-03-11T07:00:00Z'); // 7 AM UTC (after DST)
+ const expectedLocal = new Date('2024-03-11T00:00:00Z'); // 12 AM PDT (-7)
+
+ const result = adjustToLosAngelesTime(utcTimestamp);
+
+ expect(result.toISOString()).toBe(expectedLocal.toISOString());
+ });
+
+ it('should correctly adjust timestamps after DST ends (PST -8)', () => {
+ const utcTimestamp = new Date('2024-11-10T08:00:00Z'); // 8 AM UTC
+ const expectedLocal = new Date('2024-11-10T00:00:00Z'); // 12 AM PST (-8)
+
+ const result = adjustToLosAngelesTime(utcTimestamp);
+
+ expect(result.toISOString()).toBe(expectedLocal.toISOString());
+ });
+
+ it('should correctly adjust timestamps when DST ends (PST -8)', () => {
+ const utcTimestamp = new Date('2024-11-03T09:00:00Z'); // 9 AM UTC
+ const expectedLocal = new Date('2024-11-03T01:00:00Z'); // 1 AM PST (UTC-8)
+
+ const result = adjustToLosAngelesTime(utcTimestamp);
+
+ expect(result.toISOString()).toBe(expectedLocal.toISOString());
+ });
+
+ it('should correctly handle the repeated hour when DST ends (PST -8)', () => {
+ const utcTimestamp = new Date('2024-11-03T08:30:00Z'); // 8:30 AM UTC
+ const expectedLocal = new Date('2024-11-03T01:30:00Z'); // 1:30 AM PST (during repeat hour)
+
+ const result = adjustToLosAngelesTime(utcTimestamp);
+
+ expect(result.toISOString()).toBe(expectedLocal.toISOString());
+ });
+ });
+
+ describe('isSameUTCDate', () => {
+ it('should return true for the same UTC day', () => {
+ const date1 = new Date('2023-11-02T19:00:00Z');
+ const date2 = new Date('2023-11-02T10:00:00Z');
+ expect(isSameUTCDate(date1, date2)).toBe(true);
+ });
+
+ it('should return false for different UTC days', () => {
+ const date1 = new Date('2023-11-02T19:00:00Z');
+ const date2 = new Date('2023-11-03T10:00:00Z');
+ expect(isSameUTCDate(date1, date2)).toBe(false);
+ });
+ });
+
+ describe('doesEventExist', () => {
+ it('should return true if an event exists on the same UTC day', () => {
+ const today = new Date('2023-11-02T00:00:00Z');
+ expect(doesEventExist('Event 1', today, mockEvents)).toBe(true);
+ });
+
+ it('should return false if no event exists on the same UTC day', () => {
+ const today = new Date('2023-11-03T00:00:00Z');
+ expect(doesEventExist('Event 1', today, mockEvents)).toBe(false);
+ });
+ });
+
+ describe('filterAndCreateEvents', () => {
+ it('should not create events already present for today', async () => {
+ await filterAndCreateEvents(mockEvents, mockRecurringEvents, mockURL, mockHeader, fetch);
+
+ expect(generateEventData).not.toHaveBeenCalledWith(mockRecurringEvents[0]); // Recurring Event 1
+ expect(generateEventData).not.toHaveBeenCalledWith(mockRecurringEvents[1]); // Recurring Event 2
+ expect(fetch).not.toHaveBeenCalled();
+ });
+
+ it('should correctly adjust an event before DST ends (UTC-7 -> UTC-8)', async () => {
+ MockDate.set('2023-11-04T23:00:00Z'); // Before DST ends
+
+ const preDstEvent = [
+ {
+ name: 'Pre-DST Event',
+ date: '2023-11-04T08:00:00Z', // 8 AM UTC (1 AM PDT)
+ startTime: '2023-11-04T08:00:00Z',
+ // hours: 1,
+ },
+ ];
+ await filterAndCreateEvents([], preDstEvent, mockURL, mockHeader, fetch);
+
+ expect(generateEventData).toHaveBeenCalledWith(
+ expect.objectContaining({ name: 'Pre-DST Event' }),
+ );
+
+ const expectedEvent = {
+ name: 'Pre-DST Event',
+ date: new Date('2023-11-04T01:00:00Z').toISOString(), // Should match 1 AM PDT
+ startTime: new Date('2023-11-04T01:00:00Z').toISOString(),
+ generated: true,
+ };
+
+ expect(fetch).toHaveBeenCalledWith(
+ `${mockURL}/api/events/`,
+ expect.objectContaining({
+ body: JSON.stringify([expectedEvent]),
+ }),
+ );
+
+ MockDate.reset();
+ });
+
+ it('should correctly adjust an event during DST ending (PDT -> PST shift)', async () => {
+ MockDate.set('2023-11-05T02:00:00Z'); // The moment of DST shift
+
+ const dstTransitionEvent = [
+ {
+ name: 'DST Shift Event',
+ date: '2023-11-05T09:00:00Z',
+ startTime: '2023-11-05T09:00:00Z',
+ },
+ ];
+
+ await filterAndCreateEvents([], dstTransitionEvent, mockURL, mockHeader, fetch);
+
+ expect(generateEventData).toHaveBeenCalledWith(
+ expect.objectContaining({ name: 'DST Shift Event' }),
+ );
+ const expectedEvent = {
+ name: 'DST Shift Event',
+ date: new Date('2023-11-05T01:00:00Z').toISOString(),
+ startTime: new Date('2023-11-05T01:00:00Z').toISOString(),
+ generated: true,
+ };
+
+ expect(fetch).toHaveBeenCalledWith(
+ `${mockURL}/api/events/`,
+ expect.objectContaining({
+ body: JSON.stringify([expectedEvent]),
+ }),
+ );
+
+ MockDate.reset();
+ });
+
+ it('should correctly adjust an event before DST starts (UTC-8 -> UTC-7)', async () => {
+ MockDate.set('2024-03-10T09:00:00Z'); // 1 AM PST before the shift
+
+ const preDstStartEvent = [
+ {
+ name: 'Pre-DST Start Event',
+ date: '2024-03-10T09:00:00Z', // 1 AM PST in UTC-8
+ startTime: '2024-03-10T09:00:00Z',
+ },
+ ];
+
+ await filterAndCreateEvents([], preDstStartEvent, mockURL, mockHeader, fetch);
+
+ expect(generateEventData).toHaveBeenCalledWith(
+ expect.objectContaining({ name: 'Pre-DST Start Event' }),
+ );
+
+ const expectedEvent = {
+ name: 'Pre-DST Start Event',
+ date: new Date('2024-03-10T01:00:00Z').toISOString(), // Should match 1 AM PST
+ startTime: new Date('2024-03-10T01:00:00Z').toISOString(),
+ generated: true,
+ };
+
+ expect(fetch).toHaveBeenCalledWith(
+ `${mockURL}/api/events/`,
+ expect.objectContaining({
+ body: JSON.stringify([expectedEvent]),
+ }),
+ );
+
+ MockDate.reset();
+ });
+
+ it('should correctly adjust an event during DST start (PST -> PDT shift)', async () => {
+ MockDate.set('2024-03-10T10:00:00Z');
+
+ const dstStartTransitionEvent = [
+ {
+ name: 'DST Start Event',
+ date: '2024-03-10T10:00:00Z', // 2 AM PST in UTC-8
+ startTime: '2024-03-10T10:00:00Z',
+ },
+ ];
+ await filterAndCreateEvents([], dstStartTransitionEvent, mockURL, mockHeader, fetch);
+
+ expect(generateEventData).toHaveBeenCalledWith(
+ expect.objectContaining({ name: 'DST Start Event' }),
+ );
+
+ const expectedEvent = {
+ name: 'DST Start Event',
+ date: new Date('2024-03-10T03:00:00Z').toISOString(), // Should match 3 AM PDT
+ startTime: new Date('2024-03-10T03:00:00Z').toISOString(),
+ generated: true,
+ };
+
+ expect(fetch).toHaveBeenCalledWith(
+ `${mockURL}/api/events/`,
+ expect.objectContaining({
+ body: JSON.stringify([expectedEvent]),
+ }),
+ );
+
+ MockDate.reset();
+ });
+ });
+
+ describe('runTask', () => {
+ it('should fetch data but not create events if all exist', async () => {
+ // First API call response (events)
+ fetch.mockResolvedValueOnce({
+ ok: true,
+ json: jest.fn().mockResolvedValue(mockEvents),
+ });
+
+ // Second API call response (recurring events)
+ fetch.mockResolvedValueOnce({
+ ok: true,
+ json: jest.fn().mockResolvedValue(mockRecurringEvents),
+ });
+
+ await runTask(fetch, mockURL, mockHeader);
+
+ console.log('Actual fetch calls:', fetch.mock.calls);
+ // Expect only 2 fetch calls (no event creation needed)
+ expect(fetch).toHaveBeenCalledTimes(2);
+
+ expect(fetch).toHaveBeenCalledWith(
+ `${mockURL}/api/recurringevents/`,
+ expect.objectContaining({ headers: { 'x-customrequired-header': mockHeader } }),
+ );
+
+ // Ensure no call to createEvent
+ expect(fetch).not.toHaveBeenCalledWith(
+ `${mockURL}/api/events/`,
+ expect.objectContaining({ method: 'POST' }),
+ );
+ });
+ });
+
+ describe('createEvents', () => {
+ it('should create a new event via POST request', async () => {
+ const mockEvent = { name: 'Event 1', date: '2023-11-02T19:00:00Z' };
+ const mockEventArray = [mockEvent];
+ fetch.mockResolvedValueOnce({
+ ok: true,
+ json: jest.fn().mockResolvedValue({ id: 1, ...mockEvent }),
+ });
+
+ const result = await createEvents(mockEventArray, mockURL, mockHeader, fetch);
+
+ expect(fetch).toHaveBeenCalledWith(`${mockURL}/api/events/`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'x-customrequired-header': mockHeader,
+ },
+ body: JSON.stringify(mockEventArray),
+ });
+ expect(result).toEqual({ id: 1, ...mockEvent });
+ });
+
+ it('should return null if event creation fails', async () => {
+ fetch.mockRejectedValueOnce(new Error('Network error'));
+
+ const result = await createEvents(null, mockURL, mockHeader, fetch);
+
+ expect(result).toBeNull();
+ });
+ });
+
+ describe('scheduleTask', () => {
+ it('should schedule the runTask function', () => {
+ const scheduleSpy = jest.spyOn(cron, 'schedule').mockImplementation((_, callback) => {
+ callback();
+ });
+
+ scheduleTask(cron, fetch, mockURL, mockHeader);
+
+ expect(scheduleSpy).toHaveBeenCalledWith('*/30 * * * *', expect.any(Function));
+
+ scheduleSpy.mockRestore();
+ });
+ });
+});
diff --git a/backend/workers/lib/generateEventData.js b/backend/workers/lib/generateEventData.js
new file mode 100644
index 000000000..aac3bc966
--- /dev/null
+++ b/backend/workers/lib/generateEventData.js
@@ -0,0 +1,44 @@
+function generateEventData(eventObj, TODAY_DATE = new Date()) {
+ /**
+ * Generates event data based on the provided event object and date.
+ * In the cron job this function normally runs in, it is expected that eventObj.date is the same as TODAY_DATE.
+ */
+ const eventDate = new Date(eventObj.startTime);
+ // Create new event
+ const hours = eventDate.getHours();
+ const minutes = eventDate.getMinutes();
+ const seconds = eventDate.getSeconds();
+ const milliseconds = eventDate.getMilliseconds();
+
+ const yearToday = TODAY_DATE.getFullYear();
+ const monthToday = TODAY_DATE.getMonth();
+ const dateToday = TODAY_DATE.getDate();
+
+ const newEventDate = new Date(yearToday, monthToday, dateToday, hours, minutes, seconds, milliseconds);
+
+ const newEndTime = new Date(yearToday, monthToday, dateToday, hours + eventObj.hours, minutes, seconds, milliseconds)
+
+ const eventToCreate = {
+ name: eventObj.name && eventObj.name,
+ hacknight: eventObj.hacknight && eventObj.hacknight,
+ eventType: eventObj.eventType && eventObj.eventType,
+ description: eventObj.eventDescription && eventObj.eventDescription,
+ project: eventObj.project && eventObj.project,
+ date: eventObj.date && newEventDate,
+ startTime: eventObj.startTime && newEventDate,
+ endTime: eventObj.endTime && newEndTime,
+ hours: eventObj.hours && eventObj.hours
+ }
+
+ if (eventObj.hasOwnProperty("location")) {
+ eventToCreate.location = {
+ city: eventObj.location.city ? eventObj.location.city : 'REMOTE',
+ state: eventObj.location.state ? eventObj.location.state : 'REMOTE',
+ country: eventObj.location.country ? eventObj.location.country : 'REMOTE'
+ };
+ }
+
+ return eventToCreate
+};
+
+module.exports = { generateEventData };
\ No newline at end of file
diff --git a/backend/workers/openCheckins.js b/backend/workers/openCheckins.js
index bd1191533..6bbb74111 100644
--- a/backend/workers/openCheckins.js
+++ b/backend/workers/openCheckins.js
@@ -1,81 +1,101 @@
module.exports = (cron, fetch) => {
+ // Check to see if any events are about to start,
+ // and if so, open their respective check-ins
- // Check to see if any events are about to start,
- // and if so, open their respective check-ins
+ const url =
+ process.env.NODE_ENV === 'prod'
+ ? 'https://www.vrms.io'
+ : `http://localhost:${process.env.BACKEND_PORT}`;
+ const headerToSend = process.env.CUSTOM_REQUEST_HEADER;
- const url = process.env.NODE_ENV === 'prod' ? 'https://www.vrms.io' : 'http://localhost:4000';
- const headerToSend = process.env.CUSTOM_REQUEST_HEADER;
+ async function fetchEvents() {
+ try {
+ const res = await fetch(`${url}/api/events`, {
+ headers: {
+ 'x-customrequired-header': headerToSend,
+ },
+ });
+ const resJson = await res.json();
- async function fetchEvents() {
- try {
- const res = await fetch(`${url}/api/events`, {
- headers: {
- "x-customrequired-header": headerToSend
- }
- });
- const resJson = await res.json();
+ return resJson;
+ } catch (error) {
+ console.log(error);
+ }
+ }
- return resJson;
- } catch(error) {
- console.log(error);
- };
- };
+ async function updateEvents(eventsToUpdate) {
+ try {
+ const res = await fetch(`${url}/api/events/batchUpdate`, {
+ method: 'PATCH',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'x-customrequired-header': headerToSend,
+ },
+ body: JSON.stringify(eventsToUpdate),
+ });
+ if (!res.ok) throw new Error('Failed to update event');
+ return await res.json();
+ } catch (error) {
+ console.error('Error updating event:', error);
+ return null;
+ }
+ }
- async function sortAndFilterEvents(currentTime, thirtyMinutes) {
- const events = await fetchEvents();
+ async function sortAndFilterEvents() {
+ const events = await fetchEvents();
- // Filter events if event date is after now but before thirty minutes from now
- if (events && events.length > 0) {
- const sortedEvents = events.filter(event => {
- return (event.date >= currentTime) && (event.date <= thirtyMinutes) && (event.checkInReady === false);
- })
- // console.log('Sorted events: ', sortedEvents);
- return sortedEvents;
- };
- };
+ // Get current time and set to date variable
+ const now = Date.now();
- async function openCheckins(events) {
- if(events && events.length > 0) {
- events.forEach(event => {
- // console.log('Opening event: ', event);
+ // Calculate thirty minutes from now
+ const thirtyMinutesFromNow = now + 1800000;
- fetch(`${url}/api/events/${event._id}`, {
- method: "PATCH",
- headers: {
- "Content-Type": "application/json",
- "x-customrequired-header": headerToSend
- },
- body: JSON.stringify({ checkInReady: true })
- })
- .then(res => {
- const response = res;
- })
- .catch(err => {
- console.log(err);
- });
- });
- };
- };
+ // Filter events if event date is after now but before thirty minutes from now
+ if (events && events.length > 0) {
+ const sortedEvents = events.filter((event) => {
+ if (!event.date) {
+ // handle if event date is null/undefined
+ // false meaning don't include in sortedEvents
+ return false;
+ }
+ const startMs = new Date(event.date).getTime();
+ if (Number.isNaN(startMs)) return false;
+ return startMs >= now && startMs <= thirtyMinutesFromNow && event.checkInReady === false;
+ });
+ // console.log('Sorted events: ', sortedEvents);
+ return sortedEvents;
+ }
+ }
- async function runTask() {
- console.log("Opening check-ins");
+ async function openCheckins(events) {
+ if (events && events.length > 0) {
+ console.log('Opening check-ins');
+ // console.log('Opening event: ', event);
+ const batchEventsToUpdate = events.map((e) => ({
+ _id: e._id,
+ checkInReady: true,
+ }));
+ const updatedEvents = await updateEvents(batchEventsToUpdate);
+ if (updatedEvents) console.log('Updated events:', updatedEvents);
+ console.log('Check-ins opened');
+ } else {
+ console.log('No scheduled events to open');
+ }
+ }
- // Get current time and set to date variable
- const currentTimeISO = new Date().toISOString();
-
- // Calculate thirty minutes from now
- const thirtyMinutesFromNow = new Date().getTime() + 1800000;
- const thirtyMinutesISO = new Date(thirtyMinutesFromNow).toISOString();
-
- const eventsToOpen = await sortAndFilterEvents(currentTimeISO, thirtyMinutesISO);
- await openCheckins(eventsToOpen);
-
- console.log("Check-ins opened");
- };
+ async function runTask() {
+ const eventsToOpen = await sortAndFilterEvents().catch((err) => {
+ console.log(err);
+ });
- const scheduledTask = cron.schedule('*/30 * * * *', () => {
- runTask();
+ await openCheckins(eventsToOpen).catch((err) => {
+ console.log(err);
});
+ }
+
+ const scheduledTask = cron.schedule('*/30 * * * *', () => {
+ runTask();
+ });
- return scheduledTask;
-};
\ No newline at end of file
+ return scheduledTask;
+};
diff --git a/backend/workers/slackbot.js b/backend/workers/slackbot.js
index 27748a78d..d50e56f9b 100644
--- a/backend/workers/slackbot.js
+++ b/backend/workers/slackbot.js
@@ -9,7 +9,7 @@ module.exports = (fetch) => {
const fetchEvents = async () => {
try {
- const res = await fetch('http://localhost:4000/api/events', {
+ const res = await fetch(`http://localhost:${process.env.BACKEND_PORT}/api/events`, {
headers: {
'x-customrequired-header': headerToSend,
},
diff --git a/backend/workers/tokenCleanup.js b/backend/workers/tokenCleanup.js
new file mode 100644
index 000000000..e4c98917d
--- /dev/null
+++ b/backend/workers/tokenCleanup.js
@@ -0,0 +1,17 @@
+const { RefreshToken } = require('../models');
+
+async function cleanupExpiredTokens() {
+ try {
+ const result = await RefreshToken.deleteMany({
+ expiresAt: { $lt: new Date() },
+ });
+ console.log(`Cleaned up ${result.deletedCount} expired refresh tokens`);
+ } catch (err) {
+ console.error('Token cleanup error:', err);
+ }
+}
+
+// Run daily
+setInterval(cleanupExpiredTokens, 24 * 60 * 60 * 1000);
+
+module.exports = { cleanupExpiredTokens };
diff --git a/backend/yarn.lock b/backend/yarn.lock
index 30806eb13..15d9c3a36 100644
--- a/backend/yarn.lock
+++ b/backend/yarn.lock
@@ -2,6 +2,579 @@
# yarn lockfile v1
+"@aws-crypto/sha256-browser@5.2.0":
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz#153895ef1dba6f9fce38af550e0ef58988eb649e"
+ integrity sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==
+ dependencies:
+ "@aws-crypto/sha256-js" "^5.2.0"
+ "@aws-crypto/supports-web-crypto" "^5.2.0"
+ "@aws-crypto/util" "^5.2.0"
+ "@aws-sdk/types" "^3.222.0"
+ "@aws-sdk/util-locate-window" "^3.0.0"
+ "@smithy/util-utf8" "^2.0.0"
+ tslib "^2.6.2"
+
+"@aws-crypto/sha256-js@5.2.0", "@aws-crypto/sha256-js@^5.2.0":
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz#c4fdb773fdbed9a664fc1a95724e206cf3860042"
+ integrity sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==
+ dependencies:
+ "@aws-crypto/util" "^5.2.0"
+ "@aws-sdk/types" "^3.222.0"
+ tslib "^2.6.2"
+
+"@aws-crypto/supports-web-crypto@^5.2.0":
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz#a1e399af29269be08e695109aa15da0a07b5b5fb"
+ integrity sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==
+ dependencies:
+ tslib "^2.6.2"
+
+"@aws-crypto/util@^5.2.0":
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/@aws-crypto/util/-/util-5.2.0.tgz#71284c9cffe7927ddadac793c14f14886d3876da"
+ integrity sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==
+ dependencies:
+ "@aws-sdk/types" "^3.222.0"
+ "@smithy/util-utf8" "^2.0.0"
+ tslib "^2.6.2"
+
+"@aws-sdk/client-cognito-identity@3.980.0":
+ version "3.980.0"
+ resolved "https://registry.yarnpkg.com/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.980.0.tgz#e74f1a87864b729405e5504badcf0a52b90bc10b"
+ integrity sha512-nLgMW2drTzv+dTo3ORCcotQPcrUaTQ+xoaDTdSaUXdZO7zbbVyk7ysE5GDTnJdZWcUjHOSB8xfNQhOTTNVPhFw==
+ dependencies:
+ "@aws-crypto/sha256-browser" "5.2.0"
+ "@aws-crypto/sha256-js" "5.2.0"
+ "@aws-sdk/core" "^3.973.5"
+ "@aws-sdk/credential-provider-node" "^3.972.4"
+ "@aws-sdk/middleware-host-header" "^3.972.3"
+ "@aws-sdk/middleware-logger" "^3.972.3"
+ "@aws-sdk/middleware-recursion-detection" "^3.972.3"
+ "@aws-sdk/middleware-user-agent" "^3.972.5"
+ "@aws-sdk/region-config-resolver" "^3.972.3"
+ "@aws-sdk/types" "^3.973.1"
+ "@aws-sdk/util-endpoints" "3.980.0"
+ "@aws-sdk/util-user-agent-browser" "^3.972.3"
+ "@aws-sdk/util-user-agent-node" "^3.972.3"
+ "@smithy/config-resolver" "^4.4.6"
+ "@smithy/core" "^3.22.0"
+ "@smithy/fetch-http-handler" "^5.3.9"
+ "@smithy/hash-node" "^4.2.8"
+ "@smithy/invalid-dependency" "^4.2.8"
+ "@smithy/middleware-content-length" "^4.2.8"
+ "@smithy/middleware-endpoint" "^4.4.12"
+ "@smithy/middleware-retry" "^4.4.29"
+ "@smithy/middleware-serde" "^4.2.9"
+ "@smithy/middleware-stack" "^4.2.8"
+ "@smithy/node-config-provider" "^4.3.8"
+ "@smithy/node-http-handler" "^4.4.8"
+ "@smithy/protocol-http" "^5.3.8"
+ "@smithy/smithy-client" "^4.11.1"
+ "@smithy/types" "^4.12.0"
+ "@smithy/url-parser" "^4.2.8"
+ "@smithy/util-base64" "^4.3.0"
+ "@smithy/util-body-length-browser" "^4.2.0"
+ "@smithy/util-body-length-node" "^4.2.1"
+ "@smithy/util-defaults-mode-browser" "^4.3.28"
+ "@smithy/util-defaults-mode-node" "^4.2.31"
+ "@smithy/util-endpoints" "^3.2.8"
+ "@smithy/util-middleware" "^4.2.8"
+ "@smithy/util-retry" "^4.2.8"
+ "@smithy/util-utf8" "^4.2.0"
+ tslib "^2.6.2"
+
+"@aws-sdk/client-cognito-identity@3.981.0":
+ version "3.981.0"
+ resolved "https://registry.yarnpkg.com/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.981.0.tgz#4f183f161e1d06373a50009453ac9a4e7e51b5af"
+ integrity sha512-Epc/dSH5VlAHBYxLGNZm+ZZNF2vHoNJdrVa1NJfYylaLVGgQpscoT8QN7ijqQUl7b888JAAGY5tAFSlvPqeoLA==
+ dependencies:
+ "@aws-crypto/sha256-browser" "5.2.0"
+ "@aws-crypto/sha256-js" "5.2.0"
+ "@aws-sdk/core" "^3.973.5"
+ "@aws-sdk/credential-provider-node" "^3.972.4"
+ "@aws-sdk/middleware-host-header" "^3.972.3"
+ "@aws-sdk/middleware-logger" "^3.972.3"
+ "@aws-sdk/middleware-recursion-detection" "^3.972.3"
+ "@aws-sdk/middleware-user-agent" "^3.972.5"
+ "@aws-sdk/region-config-resolver" "^3.972.3"
+ "@aws-sdk/types" "^3.973.1"
+ "@aws-sdk/util-endpoints" "3.981.0"
+ "@aws-sdk/util-user-agent-browser" "^3.972.3"
+ "@aws-sdk/util-user-agent-node" "^3.972.3"
+ "@smithy/config-resolver" "^4.4.6"
+ "@smithy/core" "^3.22.0"
+ "@smithy/fetch-http-handler" "^5.3.9"
+ "@smithy/hash-node" "^4.2.8"
+ "@smithy/invalid-dependency" "^4.2.8"
+ "@smithy/middleware-content-length" "^4.2.8"
+ "@smithy/middleware-endpoint" "^4.4.12"
+ "@smithy/middleware-retry" "^4.4.29"
+ "@smithy/middleware-serde" "^4.2.9"
+ "@smithy/middleware-stack" "^4.2.8"
+ "@smithy/node-config-provider" "^4.3.8"
+ "@smithy/node-http-handler" "^4.4.8"
+ "@smithy/protocol-http" "^5.3.8"
+ "@smithy/smithy-client" "^4.11.1"
+ "@smithy/types" "^4.12.0"
+ "@smithy/url-parser" "^4.2.8"
+ "@smithy/util-base64" "^4.3.0"
+ "@smithy/util-body-length-browser" "^4.2.0"
+ "@smithy/util-body-length-node" "^4.2.1"
+ "@smithy/util-defaults-mode-browser" "^4.3.28"
+ "@smithy/util-defaults-mode-node" "^4.2.31"
+ "@smithy/util-endpoints" "^3.2.8"
+ "@smithy/util-middleware" "^4.2.8"
+ "@smithy/util-retry" "^4.2.8"
+ "@smithy/util-utf8" "^4.2.0"
+ tslib "^2.6.2"
+
+"@aws-sdk/client-sso@3.980.0":
+ version "3.980.0"
+ resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.980.0.tgz#2ec6a01335c8344fa8f4c2a7c8d2f98428b5c7a0"
+ integrity sha512-AhNXQaJ46C1I+lQ+6Kj+L24il5K9lqqIanJd8lMszPmP7bLnmX0wTKK0dxywcvrLdij3zhWttjAKEBNgLtS8/A==
+ dependencies:
+ "@aws-crypto/sha256-browser" "5.2.0"
+ "@aws-crypto/sha256-js" "5.2.0"
+ "@aws-sdk/core" "^3.973.5"
+ "@aws-sdk/middleware-host-header" "^3.972.3"
+ "@aws-sdk/middleware-logger" "^3.972.3"
+ "@aws-sdk/middleware-recursion-detection" "^3.972.3"
+ "@aws-sdk/middleware-user-agent" "^3.972.5"
+ "@aws-sdk/region-config-resolver" "^3.972.3"
+ "@aws-sdk/types" "^3.973.1"
+ "@aws-sdk/util-endpoints" "3.980.0"
+ "@aws-sdk/util-user-agent-browser" "^3.972.3"
+ "@aws-sdk/util-user-agent-node" "^3.972.3"
+ "@smithy/config-resolver" "^4.4.6"
+ "@smithy/core" "^3.22.0"
+ "@smithy/fetch-http-handler" "^5.3.9"
+ "@smithy/hash-node" "^4.2.8"
+ "@smithy/invalid-dependency" "^4.2.8"
+ "@smithy/middleware-content-length" "^4.2.8"
+ "@smithy/middleware-endpoint" "^4.4.12"
+ "@smithy/middleware-retry" "^4.4.29"
+ "@smithy/middleware-serde" "^4.2.9"
+ "@smithy/middleware-stack" "^4.2.8"
+ "@smithy/node-config-provider" "^4.3.8"
+ "@smithy/node-http-handler" "^4.4.8"
+ "@smithy/protocol-http" "^5.3.8"
+ "@smithy/smithy-client" "^4.11.1"
+ "@smithy/types" "^4.12.0"
+ "@smithy/url-parser" "^4.2.8"
+ "@smithy/util-base64" "^4.3.0"
+ "@smithy/util-body-length-browser" "^4.2.0"
+ "@smithy/util-body-length-node" "^4.2.1"
+ "@smithy/util-defaults-mode-browser" "^4.3.28"
+ "@smithy/util-defaults-mode-node" "^4.2.31"
+ "@smithy/util-endpoints" "^3.2.8"
+ "@smithy/util-middleware" "^4.2.8"
+ "@smithy/util-retry" "^4.2.8"
+ "@smithy/util-utf8" "^4.2.0"
+ tslib "^2.6.2"
+
+"@aws-sdk/core@^3.973.5":
+ version "3.973.5"
+ resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.973.5.tgz#21b0c0f7f8cc2624f5402c2694e44738365bbabe"
+ integrity sha512-IMM7xGfLGW6lMvubsA4j6BHU5FPgGAxoQ/NA63KqNLMwTS+PeMBcx8DPHL12Vg6yqOZnqok9Mu4H2BdQyq7gSA==
+ dependencies:
+ "@aws-sdk/types" "^3.973.1"
+ "@aws-sdk/xml-builder" "^3.972.2"
+ "@smithy/core" "^3.22.0"
+ "@smithy/node-config-provider" "^4.3.8"
+ "@smithy/property-provider" "^4.2.8"
+ "@smithy/protocol-http" "^5.3.8"
+ "@smithy/signature-v4" "^5.3.8"
+ "@smithy/smithy-client" "^4.11.1"
+ "@smithy/types" "^4.12.0"
+ "@smithy/util-base64" "^4.3.0"
+ "@smithy/util-middleware" "^4.2.8"
+ "@smithy/util-utf8" "^4.2.0"
+ tslib "^2.6.2"
+
+"@aws-sdk/credential-provider-cognito-identity@^3.972.3":
+ version "3.972.3"
+ resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.972.3.tgz#699ccf8450be66848e972568e59e37b95bd62c2b"
+ integrity sha512-dW/DqTk90XW7hIngqntAVtJJyrkS51wcLhGz39lOMe0TlSmZl+5R/UGnAZqNbXmWuJHLzxe+MLgagxH41aTsAQ==
+ dependencies:
+ "@aws-sdk/client-cognito-identity" "3.980.0"
+ "@aws-sdk/types" "^3.973.1"
+ "@smithy/property-provider" "^4.2.8"
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@aws-sdk/credential-provider-env@^3.972.3":
+ version "3.972.3"
+ resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.3.tgz#3fba92900120a58e8b0adecacdcf30fea0bca208"
+ integrity sha512-OBYNY4xQPq7Rx+oOhtyuyO0AQvdJSpXRg7JuPNBJH4a1XXIzJQl4UHQTPKZKwfJXmYLpv4+OkcFen4LYmDPd3g==
+ dependencies:
+ "@aws-sdk/core" "^3.973.5"
+ "@aws-sdk/types" "^3.973.1"
+ "@smithy/property-provider" "^4.2.8"
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@aws-sdk/credential-provider-http@^3.972.5":
+ version "3.972.5"
+ resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.5.tgz#8ce948d8798f04446d34704c14efb5e78eee908a"
+ integrity sha512-GpvBgEmSZPvlDekd26Zi+XsI27Qz7y0utUx0g2fSTSiDzhnd1FSa1owuodxR0BcUKNL7U2cOVhhDxgZ4iSoPVg==
+ dependencies:
+ "@aws-sdk/core" "^3.973.5"
+ "@aws-sdk/types" "^3.973.1"
+ "@smithy/fetch-http-handler" "^5.3.9"
+ "@smithy/node-http-handler" "^4.4.8"
+ "@smithy/property-provider" "^4.2.8"
+ "@smithy/protocol-http" "^5.3.8"
+ "@smithy/smithy-client" "^4.11.1"
+ "@smithy/types" "^4.12.0"
+ "@smithy/util-stream" "^4.5.10"
+ tslib "^2.6.2"
+
+"@aws-sdk/credential-provider-ini@^3.972.3":
+ version "3.972.3"
+ resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.3.tgz#0f060fee33bda6c120cf0b531f9ce6c69b0a181d"
+ integrity sha512-rMQAIxstP7cLgYfsRGrGOlpyMl0l8JL2mcke3dsIPLWke05zKOFyR7yoJzWCsI/QiIxjRbxpvPiAeKEA6CoYkg==
+ dependencies:
+ "@aws-sdk/core" "^3.973.5"
+ "@aws-sdk/credential-provider-env" "^3.972.3"
+ "@aws-sdk/credential-provider-http" "^3.972.5"
+ "@aws-sdk/credential-provider-login" "^3.972.3"
+ "@aws-sdk/credential-provider-process" "^3.972.3"
+ "@aws-sdk/credential-provider-sso" "^3.972.3"
+ "@aws-sdk/credential-provider-web-identity" "^3.972.3"
+ "@aws-sdk/nested-clients" "3.980.0"
+ "@aws-sdk/types" "^3.973.1"
+ "@smithy/credential-provider-imds" "^4.2.8"
+ "@smithy/property-provider" "^4.2.8"
+ "@smithy/shared-ini-file-loader" "^4.4.3"
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@aws-sdk/credential-provider-login@^3.972.3":
+ version "3.972.3"
+ resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.3.tgz#ed6542a91bd026d2c7f54c8963b362302109367b"
+ integrity sha512-Gc3O91iVvA47kp2CLIXOwuo5ffo1cIpmmyIewcYjAcvurdFHQ8YdcBe1KHidnbbBO4/ZtywGBACsAX5vr3UdoA==
+ dependencies:
+ "@aws-sdk/core" "^3.973.5"
+ "@aws-sdk/nested-clients" "3.980.0"
+ "@aws-sdk/types" "^3.973.1"
+ "@smithy/property-provider" "^4.2.8"
+ "@smithy/protocol-http" "^5.3.8"
+ "@smithy/shared-ini-file-loader" "^4.4.3"
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@aws-sdk/credential-provider-node@^3.972.4":
+ version "3.972.4"
+ resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.4.tgz#24f711c5c24950b616fe7d790586219f5d4082b1"
+ integrity sha512-UwerdzosMSY7V5oIZm3NsMDZPv2aSVzSkZxYxIOWHBeKTZlUqW7XpHtJMZ4PZpJ+HMRhgP+MDGQx4THndgqJfQ==
+ dependencies:
+ "@aws-sdk/credential-provider-env" "^3.972.3"
+ "@aws-sdk/credential-provider-http" "^3.972.5"
+ "@aws-sdk/credential-provider-ini" "^3.972.3"
+ "@aws-sdk/credential-provider-process" "^3.972.3"
+ "@aws-sdk/credential-provider-sso" "^3.972.3"
+ "@aws-sdk/credential-provider-web-identity" "^3.972.3"
+ "@aws-sdk/types" "^3.973.1"
+ "@smithy/credential-provider-imds" "^4.2.8"
+ "@smithy/property-provider" "^4.2.8"
+ "@smithy/shared-ini-file-loader" "^4.4.3"
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@aws-sdk/credential-provider-process@^3.972.3":
+ version "3.972.3"
+ resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.3.tgz#bcc57cf0fccd293dfbe328045186dcfcc41306ab"
+ integrity sha512-xkSY7zjRqeVc6TXK2xr3z1bTLm0wD8cj3lAkproRGaO4Ku7dPlKy843YKnHrUOUzOnMezdZ4xtmFc0eKIDTo2w==
+ dependencies:
+ "@aws-sdk/core" "^3.973.5"
+ "@aws-sdk/types" "^3.973.1"
+ "@smithy/property-provider" "^4.2.8"
+ "@smithy/shared-ini-file-loader" "^4.4.3"
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@aws-sdk/credential-provider-sso@^3.972.3":
+ version "3.972.3"
+ resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.3.tgz#2069923eea35c74aa7c40cf351b059802b5de2df"
+ integrity sha512-8Ww3F5Ngk8dZ6JPL/V5LhCU1BwMfQd3tLdoEuzaewX8FdnT633tPr+KTHySz9FK7fFPcz5qG3R5edVEhWQD4AA==
+ dependencies:
+ "@aws-sdk/client-sso" "3.980.0"
+ "@aws-sdk/core" "^3.973.5"
+ "@aws-sdk/token-providers" "3.980.0"
+ "@aws-sdk/types" "^3.973.1"
+ "@smithy/property-provider" "^4.2.8"
+ "@smithy/shared-ini-file-loader" "^4.4.3"
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@aws-sdk/credential-provider-web-identity@^3.972.3":
+ version "3.972.3"
+ resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.3.tgz#ed9224ab139628fff5cab0d48b251948eaed422e"
+ integrity sha512-62VufdcH5rRfiRKZRcf1wVbbt/1jAntMj1+J0qAd+r5pQRg2t0/P9/Rz16B1o5/0Se9lVL506LRjrhIJAhYBfA==
+ dependencies:
+ "@aws-sdk/core" "^3.973.5"
+ "@aws-sdk/nested-clients" "3.980.0"
+ "@aws-sdk/types" "^3.973.1"
+ "@smithy/property-provider" "^4.2.8"
+ "@smithy/shared-ini-file-loader" "^4.4.3"
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@aws-sdk/credential-providers@^3.186.0":
+ version "3.981.0"
+ resolved "https://registry.yarnpkg.com/@aws-sdk/credential-providers/-/credential-providers-3.981.0.tgz#8708f31f115a43fadc5e28d4883258fe09304538"
+ integrity sha512-ULmXLUvZqQDqH4SgTcFXHHUf9RSa/4H+BC3/UDpiq2t2515MUPqSw6cgEpCax/6v0zY5CVWe8GBGj/Rx/saGPA==
+ dependencies:
+ "@aws-sdk/client-cognito-identity" "3.981.0"
+ "@aws-sdk/core" "^3.973.5"
+ "@aws-sdk/credential-provider-cognito-identity" "^3.972.3"
+ "@aws-sdk/credential-provider-env" "^3.972.3"
+ "@aws-sdk/credential-provider-http" "^3.972.5"
+ "@aws-sdk/credential-provider-ini" "^3.972.3"
+ "@aws-sdk/credential-provider-login" "^3.972.3"
+ "@aws-sdk/credential-provider-node" "^3.972.4"
+ "@aws-sdk/credential-provider-process" "^3.972.3"
+ "@aws-sdk/credential-provider-sso" "^3.972.3"
+ "@aws-sdk/credential-provider-web-identity" "^3.972.3"
+ "@aws-sdk/nested-clients" "3.981.0"
+ "@aws-sdk/types" "^3.973.1"
+ "@smithy/config-resolver" "^4.4.6"
+ "@smithy/core" "^3.22.0"
+ "@smithy/credential-provider-imds" "^4.2.8"
+ "@smithy/node-config-provider" "^4.3.8"
+ "@smithy/property-provider" "^4.2.8"
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@aws-sdk/middleware-host-header@^3.972.3":
+ version "3.972.3"
+ resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.3.tgz#47c161dec62d89c66c89f4d17ff4434021e04af5"
+ integrity sha512-aknPTb2M+G3s+0qLCx4Li/qGZH8IIYjugHMv15JTYMe6mgZO8VBpYgeGYsNMGCqCZOcWzuf900jFBG5bopfzmA==
+ dependencies:
+ "@aws-sdk/types" "^3.973.1"
+ "@smithy/protocol-http" "^5.3.8"
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@aws-sdk/middleware-logger@^3.972.3":
+ version "3.972.3"
+ resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.972.3.tgz#ef1afd4a0b70fe72cf5f7c817f82da9f35c7e836"
+ integrity sha512-Ftg09xNNRqaz9QNzlfdQWfpqMCJbsQdnZVJP55jfhbKi1+FTWxGuvfPoBhDHIovqWKjqbuiew3HuhxbJ0+OjgA==
+ dependencies:
+ "@aws-sdk/types" "^3.973.1"
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@aws-sdk/middleware-recursion-detection@^3.972.3":
+ version "3.972.3"
+ resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.3.tgz#5b95dcecff76a0d2963bd954bdef87700d1b1c8c"
+ integrity sha512-PY57QhzNuXHnwbJgbWYTrqIDHYSeOlhfYERTAuc16LKZpTZRJUjzBFokp9hF7u1fuGeE3D70ERXzdbMBOqQz7Q==
+ dependencies:
+ "@aws-sdk/types" "^3.973.1"
+ "@aws/lambda-invoke-store" "^0.2.2"
+ "@smithy/protocol-http" "^5.3.8"
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@aws-sdk/middleware-user-agent@^3.972.5":
+ version "3.972.5"
+ resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.5.tgz#bf7e29b94618c0f89049357fe16d006011ad4824"
+ integrity sha512-TVZQ6PWPwQbahUI8V+Er+gS41ctIawcI/uMNmQtQ7RMcg3JYn6gyKAFKUb3HFYx2OjYlx1u11sETSwwEUxVHTg==
+ dependencies:
+ "@aws-sdk/core" "^3.973.5"
+ "@aws-sdk/types" "^3.973.1"
+ "@aws-sdk/util-endpoints" "3.980.0"
+ "@smithy/core" "^3.22.0"
+ "@smithy/protocol-http" "^5.3.8"
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@aws-sdk/nested-clients@3.980.0":
+ version "3.980.0"
+ resolved "https://registry.yarnpkg.com/@aws-sdk/nested-clients/-/nested-clients-3.980.0.tgz#42972c534f4a94408d98605b1d2a4b0651244e24"
+ integrity sha512-/dONY5xc5/CCKzOqHZCTidtAR4lJXWkGefXvTRKdSKMGaYbbKsxDckisd6GfnvPSLxWtvQzwgRGRutMRoYUApQ==
+ dependencies:
+ "@aws-crypto/sha256-browser" "5.2.0"
+ "@aws-crypto/sha256-js" "5.2.0"
+ "@aws-sdk/core" "^3.973.5"
+ "@aws-sdk/middleware-host-header" "^3.972.3"
+ "@aws-sdk/middleware-logger" "^3.972.3"
+ "@aws-sdk/middleware-recursion-detection" "^3.972.3"
+ "@aws-sdk/middleware-user-agent" "^3.972.5"
+ "@aws-sdk/region-config-resolver" "^3.972.3"
+ "@aws-sdk/types" "^3.973.1"
+ "@aws-sdk/util-endpoints" "3.980.0"
+ "@aws-sdk/util-user-agent-browser" "^3.972.3"
+ "@aws-sdk/util-user-agent-node" "^3.972.3"
+ "@smithy/config-resolver" "^4.4.6"
+ "@smithy/core" "^3.22.0"
+ "@smithy/fetch-http-handler" "^5.3.9"
+ "@smithy/hash-node" "^4.2.8"
+ "@smithy/invalid-dependency" "^4.2.8"
+ "@smithy/middleware-content-length" "^4.2.8"
+ "@smithy/middleware-endpoint" "^4.4.12"
+ "@smithy/middleware-retry" "^4.4.29"
+ "@smithy/middleware-serde" "^4.2.9"
+ "@smithy/middleware-stack" "^4.2.8"
+ "@smithy/node-config-provider" "^4.3.8"
+ "@smithy/node-http-handler" "^4.4.8"
+ "@smithy/protocol-http" "^5.3.8"
+ "@smithy/smithy-client" "^4.11.1"
+ "@smithy/types" "^4.12.0"
+ "@smithy/url-parser" "^4.2.8"
+ "@smithy/util-base64" "^4.3.0"
+ "@smithy/util-body-length-browser" "^4.2.0"
+ "@smithy/util-body-length-node" "^4.2.1"
+ "@smithy/util-defaults-mode-browser" "^4.3.28"
+ "@smithy/util-defaults-mode-node" "^4.2.31"
+ "@smithy/util-endpoints" "^3.2.8"
+ "@smithy/util-middleware" "^4.2.8"
+ "@smithy/util-retry" "^4.2.8"
+ "@smithy/util-utf8" "^4.2.0"
+ tslib "^2.6.2"
+
+"@aws-sdk/nested-clients@3.981.0":
+ version "3.981.0"
+ resolved "https://registry.yarnpkg.com/@aws-sdk/nested-clients/-/nested-clients-3.981.0.tgz#e601ac39e996ea115fd3bb3121dec67211e19c7a"
+ integrity sha512-U8Nv/x0+9YleQ0yXHy0bVxjROSXXLzFzInRs/Q/Un+7FShHnS72clIuDZphK0afesszyDFS7YW4QFnm1sFIrCg==
+ dependencies:
+ "@aws-crypto/sha256-browser" "5.2.0"
+ "@aws-crypto/sha256-js" "5.2.0"
+ "@aws-sdk/core" "^3.973.5"
+ "@aws-sdk/middleware-host-header" "^3.972.3"
+ "@aws-sdk/middleware-logger" "^3.972.3"
+ "@aws-sdk/middleware-recursion-detection" "^3.972.3"
+ "@aws-sdk/middleware-user-agent" "^3.972.5"
+ "@aws-sdk/region-config-resolver" "^3.972.3"
+ "@aws-sdk/types" "^3.973.1"
+ "@aws-sdk/util-endpoints" "3.981.0"
+ "@aws-sdk/util-user-agent-browser" "^3.972.3"
+ "@aws-sdk/util-user-agent-node" "^3.972.3"
+ "@smithy/config-resolver" "^4.4.6"
+ "@smithy/core" "^3.22.0"
+ "@smithy/fetch-http-handler" "^5.3.9"
+ "@smithy/hash-node" "^4.2.8"
+ "@smithy/invalid-dependency" "^4.2.8"
+ "@smithy/middleware-content-length" "^4.2.8"
+ "@smithy/middleware-endpoint" "^4.4.12"
+ "@smithy/middleware-retry" "^4.4.29"
+ "@smithy/middleware-serde" "^4.2.9"
+ "@smithy/middleware-stack" "^4.2.8"
+ "@smithy/node-config-provider" "^4.3.8"
+ "@smithy/node-http-handler" "^4.4.8"
+ "@smithy/protocol-http" "^5.3.8"
+ "@smithy/smithy-client" "^4.11.1"
+ "@smithy/types" "^4.12.0"
+ "@smithy/url-parser" "^4.2.8"
+ "@smithy/util-base64" "^4.3.0"
+ "@smithy/util-body-length-browser" "^4.2.0"
+ "@smithy/util-body-length-node" "^4.2.1"
+ "@smithy/util-defaults-mode-browser" "^4.3.28"
+ "@smithy/util-defaults-mode-node" "^4.2.31"
+ "@smithy/util-endpoints" "^3.2.8"
+ "@smithy/util-middleware" "^4.2.8"
+ "@smithy/util-retry" "^4.2.8"
+ "@smithy/util-utf8" "^4.2.0"
+ tslib "^2.6.2"
+
+"@aws-sdk/region-config-resolver@^3.972.3":
+ version "3.972.3"
+ resolved "https://registry.yarnpkg.com/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.3.tgz#25af64235ca6f4b6b21f85d4b3c0b432efc4ae04"
+ integrity sha512-v4J8qYAWfOMcZ4MJUyatntOicTzEMaU7j3OpkRCGGFSL2NgXQ5VbxauIyORA+pxdKZ0qQG2tCQjQjZDlXEC3Ow==
+ dependencies:
+ "@aws-sdk/types" "^3.973.1"
+ "@smithy/config-resolver" "^4.4.6"
+ "@smithy/node-config-provider" "^4.3.8"
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@aws-sdk/token-providers@3.980.0":
+ version "3.980.0"
+ resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.980.0.tgz#7e5f48dc5757ac8b0dbee3cb25834b017a5eba74"
+ integrity sha512-1nFileg1wAgDmieRoj9dOawgr2hhlh7xdvcH57b1NnqfPaVlcqVJyPc6k3TLDUFPY69eEwNxdGue/0wIz58vjA==
+ dependencies:
+ "@aws-sdk/core" "^3.973.5"
+ "@aws-sdk/nested-clients" "3.980.0"
+ "@aws-sdk/types" "^3.973.1"
+ "@smithy/property-provider" "^4.2.8"
+ "@smithy/shared-ini-file-loader" "^4.4.3"
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@aws-sdk/types@^3.222.0", "@aws-sdk/types@^3.973.1":
+ version "3.973.1"
+ resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.973.1.tgz#1b2992ec6c8380c3e74c9bd2c74703e9a807d6e0"
+ integrity sha512-DwHBiMNOB468JiX6+i34c+THsKHErYUdNQ3HexeXZvVn4zouLjgaS4FejiGSi2HyBuzuyHg7SuOPmjSvoU9NRg==
+ dependencies:
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@aws-sdk/util-endpoints@3.980.0":
+ version "3.980.0"
+ resolved "https://registry.yarnpkg.com/@aws-sdk/util-endpoints/-/util-endpoints-3.980.0.tgz#0d2665ad75f92f3f208541f6fa88451d2a2e96ce"
+ integrity sha512-AjKBNEc+rjOZQE1HwcD9aCELqg1GmUj1rtICKuY8cgwB73xJ4U/kNyqKKpN2k9emGqlfDY2D8itIp/vDc6OKpw==
+ dependencies:
+ "@aws-sdk/types" "^3.973.1"
+ "@smithy/types" "^4.12.0"
+ "@smithy/url-parser" "^4.2.8"
+ "@smithy/util-endpoints" "^3.2.8"
+ tslib "^2.6.2"
+
+"@aws-sdk/util-endpoints@3.981.0":
+ version "3.981.0"
+ resolved "https://registry.yarnpkg.com/@aws-sdk/util-endpoints/-/util-endpoints-3.981.0.tgz#b303a735877e0e63dcc9da1ab9c2a84153a6ce96"
+ integrity sha512-a8nXh/H3/4j+sxhZk+N3acSDlgwTVSZbX9i55dx41gI1H+geuonuRG+Shv3GZsCb46vzc08RK2qC78ypO8uRlg==
+ dependencies:
+ "@aws-sdk/types" "^3.973.1"
+ "@smithy/types" "^4.12.0"
+ "@smithy/url-parser" "^4.2.8"
+ "@smithy/util-endpoints" "^3.2.8"
+ tslib "^2.6.2"
+
+"@aws-sdk/util-locate-window@^3.0.0":
+ version "3.965.4"
+ resolved "https://registry.yarnpkg.com/@aws-sdk/util-locate-window/-/util-locate-window-3.965.4.tgz#f62d279e1905f6939b6dffb0f76ab925440f72bf"
+ integrity sha512-H1onv5SkgPBK2P6JR2MjGgbOnttoNzSPIRoeZTNPZYyaplwGg50zS3amXvXqF0/qfXpWEC9rLWU564QTB9bSog==
+ dependencies:
+ tslib "^2.6.2"
+
+"@aws-sdk/util-user-agent-browser@^3.972.3":
+ version "3.972.3"
+ resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.3.tgz#1363b388cb3af86c5322ef752c0cf8d7d25efa8a"
+ integrity sha512-JurOwkRUcXD/5MTDBcqdyQ9eVedtAsZgw5rBwktsPTN7QtPiS2Ld1jkJepNgYoCufz1Wcut9iup7GJDoIHp8Fw==
+ dependencies:
+ "@aws-sdk/types" "^3.973.1"
+ "@smithy/types" "^4.12.0"
+ bowser "^2.11.0"
+ tslib "^2.6.2"
+
+"@aws-sdk/util-user-agent-node@^3.972.3":
+ version "3.972.3"
+ resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.972.3.tgz#1290769802e61f11a63cfd7ae059387e0ca74d70"
+ integrity sha512-gqG+02/lXQtO0j3US6EVnxtwwoXQC5l2qkhLCrqUrqdtcQxV7FDMbm9wLjKqoronSHyELGTjbFKK/xV5q1bZNA==
+ dependencies:
+ "@aws-sdk/middleware-user-agent" "^3.972.5"
+ "@aws-sdk/types" "^3.973.1"
+ "@smithy/node-config-provider" "^4.3.8"
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@aws-sdk/xml-builder@^3.972.2":
+ version "3.972.3"
+ resolved "https://registry.yarnpkg.com/@aws-sdk/xml-builder/-/xml-builder-3.972.3.tgz#b9a036be6010715748d9710c34653060b370f3b5"
+ integrity sha512-bCk63RsBNCWW4tt5atv5Sbrh+3J3e8YzgyF6aZb1JeXcdzG4k5SlPLeTMFOIXFuuFHIwgphUhn4i3uS/q49eww==
+ dependencies:
+ "@smithy/types" "^4.12.0"
+ fast-xml-parser "5.3.4"
+ tslib "^2.6.2"
+
+"@aws/lambda-invoke-store@^0.2.2":
+ version "0.2.3"
+ resolved "https://registry.yarnpkg.com/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.3.tgz#f1137f56209ccc69c15f826242cbf37f828617dd"
+ integrity sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw==
+
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a"
@@ -9,6 +582,15 @@
dependencies:
"@babel/highlight" "^7.10.4"
+"@babel/code-frame@^7.12.13", "@babel/code-frame@^7.28.6", "@babel/code-frame@^7.29.0":
+ version "7.29.0"
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.29.0.tgz#7cd7a59f15b3cc0dcd803038f7792712a7d0b15c"
+ integrity sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.28.5"
+ js-tokens "^4.0.0"
+ picocolors "^1.1.1"
+
"@babel/code-frame@^7.14.5":
version "7.14.5"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb"
@@ -16,32 +598,44 @@
dependencies:
"@babel/highlight" "^7.14.5"
+"@babel/code-frame@^7.22.13":
+ version "7.22.13"
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e"
+ integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==
+ dependencies:
+ "@babel/highlight" "^7.22.13"
+ chalk "^2.4.2"
+
"@babel/compat-data@^7.15.0":
version "7.15.0"
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.15.0.tgz#2dbaf8b85334796cafbb0f5793a90a2fc010b176"
integrity sha512-0NqAC1IJE0S0+lL1SWFMxMkz1pKCNCjI4tr2Zx4LJSXxCLAdr6KyArnY+sno5m3yH9g737ygOyPABDsnXkpxiA==
-"@babel/core@^7.1.0", "@babel/core@^7.7.5":
- version "7.11.6"
- resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.6.tgz#3a9455dc7387ff1bac45770650bc13ba04a15651"
- integrity sha512-Wpcv03AGnmkgm6uS6k8iwhIwTrcP0m17TL1n1sy7qD0qelDu4XNeW0dN0mHfa+Gei211yDaLoEe/VlbXQzM4Bg==
- dependencies:
- "@babel/code-frame" "^7.10.4"
- "@babel/generator" "^7.11.6"
- "@babel/helper-module-transforms" "^7.11.0"
- "@babel/helpers" "^7.10.4"
- "@babel/parser" "^7.11.5"
- "@babel/template" "^7.10.4"
- "@babel/traverse" "^7.11.5"
- "@babel/types" "^7.11.5"
- convert-source-map "^1.7.0"
+"@babel/compat-data@^7.28.6":
+ version "7.29.0"
+ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.29.0.tgz#00d03e8c0ac24dd9be942c5370990cbe1f17d88d"
+ integrity sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==
+
+"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9":
+ version "7.29.0"
+ resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.29.0.tgz#5286ad785df7f79d656e88ce86e650d16ca5f322"
+ integrity sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==
+ dependencies:
+ "@babel/code-frame" "^7.29.0"
+ "@babel/generator" "^7.29.0"
+ "@babel/helper-compilation-targets" "^7.28.6"
+ "@babel/helper-module-transforms" "^7.28.6"
+ "@babel/helpers" "^7.28.6"
+ "@babel/parser" "^7.29.0"
+ "@babel/template" "^7.28.6"
+ "@babel/traverse" "^7.29.0"
+ "@babel/types" "^7.29.0"
+ "@jridgewell/remapping" "^2.3.5"
+ convert-source-map "^2.0.0"
debug "^4.1.0"
- gensync "^1.0.0-beta.1"
- json5 "^2.1.2"
- lodash "^4.17.19"
- resolve "^1.3.2"
- semver "^5.4.1"
- source-map "^0.5.0"
+ gensync "^1.0.0-beta.2"
+ json5 "^2.2.3"
+ semver "^6.3.1"
"@babel/core@^7.15.0":
version "7.15.0"
@@ -80,15 +674,6 @@
dependencies:
eslint-rule-composer "^0.3.0"
-"@babel/generator@^7.11.5", "@babel/generator@^7.11.6":
- version "7.11.6"
- resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.6.tgz#b868900f81b163b4d464ea24545c61cbac4dc620"
- integrity sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA==
- dependencies:
- "@babel/types" "^7.11.5"
- jsesc "^2.5.1"
- source-map "^0.5.0"
-
"@babel/generator@^7.15.0":
version "7.15.0"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.15.0.tgz#a7d0c172e0d814974bad5aa77ace543b97917f15"
@@ -98,6 +683,27 @@
jsesc "^2.5.1"
source-map "^0.5.0"
+"@babel/generator@^7.23.0":
+ version "7.23.0"
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420"
+ integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==
+ dependencies:
+ "@babel/types" "^7.23.0"
+ "@jridgewell/gen-mapping" "^0.3.2"
+ "@jridgewell/trace-mapping" "^0.3.17"
+ jsesc "^2.5.1"
+
+"@babel/generator@^7.29.0", "@babel/generator@^7.7.2":
+ version "7.29.0"
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.29.0.tgz#4cba5a76b3c71d8be31761b03329d5dc7768447f"
+ integrity sha512-vSH118/wwM/pLR38g/Sgk05sNtro6TlTJKuiMXDaZqPUfjTFcudpCOt00IhOfj+1BFAX+UFAlzCU+6WXr3GLFQ==
+ dependencies:
+ "@babel/parser" "^7.29.0"
+ "@babel/types" "^7.29.0"
+ "@jridgewell/gen-mapping" "^0.3.12"
+ "@jridgewell/trace-mapping" "^0.3.28"
+ jsesc "^3.0.2"
+
"@babel/helper-compilation-targets@^7.15.0":
version "7.15.0"
resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.0.tgz#973df8cbd025515f3ff25db0c05efc704fa79818"
@@ -108,51 +714,41 @@
browserslist "^4.16.6"
semver "^6.3.0"
-"@babel/helper-function-name@^7.10.4":
- version "7.10.4"
- resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz#d2d3b20c59ad8c47112fa7d2a94bc09d5ef82f1a"
- integrity sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==
- dependencies:
- "@babel/helper-get-function-arity" "^7.10.4"
- "@babel/template" "^7.10.4"
- "@babel/types" "^7.10.4"
-
-"@babel/helper-function-name@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz#89e2c474972f15d8e233b52ee8c480e2cfcd50c4"
- integrity sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ==
+"@babel/helper-compilation-targets@^7.28.6":
+ version "7.28.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz#32c4a3f41f12ed1532179b108a4d746e105c2b25"
+ integrity sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==
dependencies:
- "@babel/helper-get-function-arity" "^7.14.5"
- "@babel/template" "^7.14.5"
- "@babel/types" "^7.14.5"
+ "@babel/compat-data" "^7.28.6"
+ "@babel/helper-validator-option" "^7.27.1"
+ browserslist "^4.24.0"
+ lru-cache "^5.1.1"
+ semver "^6.3.1"
-"@babel/helper-get-function-arity@^7.10.4":
- version "7.10.4"
- resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2"
- integrity sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==
- dependencies:
- "@babel/types" "^7.10.4"
+"@babel/helper-environment-visitor@^7.22.20":
+ version "7.22.20"
+ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167"
+ integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==
-"@babel/helper-get-function-arity@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz#25fbfa579b0937eee1f3b805ece4ce398c431815"
- integrity sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg==
+"@babel/helper-function-name@^7.23.0":
+ version "7.23.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759"
+ integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==
dependencies:
- "@babel/types" "^7.14.5"
+ "@babel/template" "^7.22.15"
+ "@babel/types" "^7.23.0"
-"@babel/helper-hoist-variables@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz#e0dd27c33a78e577d7c8884916a3e7ef1f7c7f8d"
- integrity sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ==
- dependencies:
- "@babel/types" "^7.14.5"
+"@babel/helper-globals@^7.28.0":
+ version "7.28.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz#b9430df2aa4e17bc28665eadeae8aa1d985e6674"
+ integrity sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==
-"@babel/helper-member-expression-to-functions@^7.10.4":
- version "7.11.0"
- resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz#ae69c83d84ee82f4b42f96e2a09410935a8f26df"
- integrity sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q==
+"@babel/helper-hoist-variables@^7.22.5":
+ version "7.22.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb"
+ integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==
dependencies:
- "@babel/types" "^7.11.0"
+ "@babel/types" "^7.22.5"
"@babel/helper-member-expression-to-functions@^7.15.0":
version "7.15.0"
@@ -161,13 +757,6 @@
dependencies:
"@babel/types" "^7.15.0"
-"@babel/helper-module-imports@^7.10.4":
- version "7.10.4"
- resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz#4c5c54be04bd31670a7382797d75b9fa2e5b5620"
- integrity sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==
- dependencies:
- "@babel/types" "^7.10.4"
-
"@babel/helper-module-imports@^7.14.5":
version "7.14.5"
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz#6d1a44df6a38c957aa7c312da076429f11b422f3"
@@ -175,18 +764,13 @@
dependencies:
"@babel/types" "^7.14.5"
-"@babel/helper-module-transforms@^7.11.0":
- version "7.11.0"
- resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz#b16f250229e47211abdd84b34b64737c2ab2d359"
- integrity sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg==
- dependencies:
- "@babel/helper-module-imports" "^7.10.4"
- "@babel/helper-replace-supers" "^7.10.4"
- "@babel/helper-simple-access" "^7.10.4"
- "@babel/helper-split-export-declaration" "^7.11.0"
- "@babel/template" "^7.10.4"
- "@babel/types" "^7.11.0"
- lodash "^4.17.19"
+"@babel/helper-module-imports@^7.28.6":
+ version "7.28.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz#60632cbd6ffb70b22823187201116762a03e2d5c"
+ integrity sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==
+ dependencies:
+ "@babel/traverse" "^7.28.6"
+ "@babel/types" "^7.28.6"
"@babel/helper-module-transforms@^7.15.0":
version "7.15.0"
@@ -202,12 +786,14 @@
"@babel/traverse" "^7.15.0"
"@babel/types" "^7.15.0"
-"@babel/helper-optimise-call-expression@^7.10.4":
- version "7.10.4"
- resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz#50dc96413d594f995a77905905b05893cd779673"
- integrity sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==
+"@babel/helper-module-transforms@^7.28.6":
+ version "7.28.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz#9312d9d9e56edc35aeb6e95c25d4106b50b9eb1e"
+ integrity sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==
dependencies:
- "@babel/types" "^7.10.4"
+ "@babel/helper-module-imports" "^7.28.6"
+ "@babel/helper-validator-identifier" "^7.28.5"
+ "@babel/traverse" "^7.28.6"
"@babel/helper-optimise-call-expression@^7.14.5":
version "7.14.5"
@@ -221,15 +807,10 @@
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375"
integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==
-"@babel/helper-replace-supers@^7.10.4":
- version "7.10.4"
- resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz#d585cd9388ea06e6031e4cd44b6713cbead9e6cf"
- integrity sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A==
- dependencies:
- "@babel/helper-member-expression-to-functions" "^7.10.4"
- "@babel/helper-optimise-call-expression" "^7.10.4"
- "@babel/traverse" "^7.10.4"
- "@babel/types" "^7.10.4"
+"@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.28.6":
+ version "7.28.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz#6f13ea251b68c8532e985fd532f28741a8af9ac8"
+ integrity sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==
"@babel/helper-replace-supers@^7.15.0":
version "7.15.0"
@@ -241,14 +822,6 @@
"@babel/traverse" "^7.15.0"
"@babel/types" "^7.15.0"
-"@babel/helper-simple-access@^7.10.4":
- version "7.10.4"
- resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz#0f5ccda2945277a2a7a2d3a821e15395edcf3461"
- integrity sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw==
- dependencies:
- "@babel/template" "^7.10.4"
- "@babel/types" "^7.10.4"
-
"@babel/helper-simple-access@^7.14.8":
version "7.14.8"
resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.14.8.tgz#82e1fec0644a7e775c74d305f212c39f8fe73924"
@@ -256,13 +829,6 @@
dependencies:
"@babel/types" "^7.14.8"
-"@babel/helper-split-export-declaration@^7.11.0":
- version "7.11.0"
- resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz#f8a491244acf6a676158ac42072911ba83ad099f"
- integrity sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==
- dependencies:
- "@babel/types" "^7.11.0"
-
"@babel/helper-split-export-declaration@^7.14.5":
version "7.14.5"
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz#22b23a54ef51c2b7605d851930c1976dd0bc693a"
@@ -270,6 +836,23 @@
dependencies:
"@babel/types" "^7.14.5"
+"@babel/helper-split-export-declaration@^7.22.6":
+ version "7.22.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c"
+ integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==
+ dependencies:
+ "@babel/types" "^7.22.5"
+
+"@babel/helper-string-parser@^7.22.5":
+ version "7.22.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f"
+ integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==
+
+"@babel/helper-string-parser@^7.27.1":
+ version "7.27.1"
+ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687"
+ integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==
+
"@babel/helper-validator-identifier@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2"
@@ -280,19 +863,25 @@
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz#6654d171b2024f6d8ee151bf2509699919131d48"
integrity sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g==
+"@babel/helper-validator-identifier@^7.22.20":
+ version "7.22.20"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0"
+ integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==
+
+"@babel/helper-validator-identifier@^7.28.5":
+ version "7.28.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4"
+ integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==
+
"@babel/helper-validator-option@^7.14.5":
version "7.14.5"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3"
integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==
-"@babel/helpers@^7.10.4":
- version "7.10.4"
- resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.4.tgz#2abeb0d721aff7c0a97376b9e1f6f65d7a475044"
- integrity sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA==
- dependencies:
- "@babel/template" "^7.10.4"
- "@babel/traverse" "^7.10.4"
- "@babel/types" "^7.10.4"
+"@babel/helper-validator-option@^7.27.1":
+ version "7.27.1"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f"
+ integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==
"@babel/helpers@^7.14.8":
version "7.15.3"
@@ -303,6 +892,14 @@
"@babel/traverse" "^7.15.0"
"@babel/types" "^7.15.0"
+"@babel/helpers@^7.28.6":
+ version "7.28.6"
+ resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.28.6.tgz#fca903a313ae675617936e8998b814c415cbf5d7"
+ integrity sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==
+ dependencies:
+ "@babel/template" "^7.28.6"
+ "@babel/types" "^7.28.6"
+
"@babel/highlight@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143"
@@ -321,7 +918,16 @@
chalk "^2.0.0"
js-tokens "^4.0.0"
-"@babel/parser@^7.1.0", "@babel/parser@^7.10.4", "@babel/parser@^7.11.5":
+"@babel/highlight@^7.22.13":
+ version "7.22.20"
+ resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54"
+ integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.22.20"
+ chalk "^2.4.2"
+ js-tokens "^4.0.0"
+
+"@babel/parser@^7.1.0", "@babel/parser@^7.10.4":
version "7.11.5"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.5.tgz#c7ff6303df71080ec7a4f5b8c003c58f1cf51037"
integrity sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==
@@ -331,6 +937,18 @@
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.15.3.tgz#3416d9bea748052cfcb63dbcc27368105b1ed862"
integrity sha512-O0L6v/HvqbdJawj0iBEfVQMc3/6WP+AeOsovsIgBFyJaG+W2w7eqvZB7puddATmWuARlm1SX7DwxJ/JJUnDpEA==
+"@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.28.6", "@babel/parser@^7.29.0":
+ version "7.29.0"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.29.0.tgz#669ef345add7d057e92b7ed15f0bac07611831b6"
+ integrity sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==
+ dependencies:
+ "@babel/types" "^7.29.0"
+
+"@babel/parser@^7.22.15", "@babel/parser@^7.23.0":
+ version "7.23.0"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719"
+ integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==
+
"@babel/plugin-syntax-async-generators@^7.8.4":
version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d"
@@ -345,14 +963,28 @@
dependencies:
"@babel/helper-plugin-utils" "^7.8.0"
-"@babel/plugin-syntax-class-properties@^7.8.3":
- version "7.10.4"
- resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz#6644e6a0baa55a61f9e3231f6c9eeb6ee46c124c"
- integrity sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA==
+"@babel/plugin-syntax-class-properties@^7.12.13":
+ version "7.12.13"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10"
+ integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==
dependencies:
- "@babel/helper-plugin-utils" "^7.10.4"
+ "@babel/helper-plugin-utils" "^7.12.13"
-"@babel/plugin-syntax-import-meta@^7.8.3":
+"@babel/plugin-syntax-class-static-block@^7.14.5":
+ version "7.14.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406"
+ integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.14.5"
+
+"@babel/plugin-syntax-import-attributes@^7.24.7":
+ version "7.28.6"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz#b71d5914665f60124e133696f17cd7669062c503"
+ integrity sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.28.6"
+
+"@babel/plugin-syntax-import-meta@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51"
integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==
@@ -366,7 +998,14 @@
dependencies:
"@babel/helper-plugin-utils" "^7.8.0"
-"@babel/plugin-syntax-logical-assignment-operators@^7.8.3":
+"@babel/plugin-syntax-jsx@^7.7.2":
+ version "7.28.6"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz#f8ca28bbd84883b5fea0e447c635b81ba73997ee"
+ integrity sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.28.6"
+
+"@babel/plugin-syntax-logical-assignment-operators@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699"
integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==
@@ -380,7 +1019,7 @@
dependencies:
"@babel/helper-plugin-utils" "^7.8.0"
-"@babel/plugin-syntax-numeric-separator@^7.8.3":
+"@babel/plugin-syntax-numeric-separator@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97"
integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==
@@ -408,6 +1047,27 @@
dependencies:
"@babel/helper-plugin-utils" "^7.8.0"
+"@babel/plugin-syntax-private-property-in-object@^7.14.5":
+ version "7.14.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad"
+ integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.14.5"
+
+"@babel/plugin-syntax-top-level-await@^7.14.5":
+ version "7.14.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c"
+ integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.14.5"
+
+"@babel/plugin-syntax-typescript@^7.7.2":
+ version "7.28.6"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz#c7b2ddf1d0a811145b1de800d1abd146af92e3a2"
+ integrity sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.28.6"
+
"@babel/runtime-corejs3@^7.10.2":
version "7.11.2"
resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.11.2.tgz#02c3029743150188edeb66541195f54600278419"
@@ -423,15 +1083,6 @@
dependencies:
regenerator-runtime "^0.13.4"
-"@babel/template@^7.10.4", "@babel/template@^7.3.3":
- version "7.10.4"
- resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278"
- integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==
- dependencies:
- "@babel/code-frame" "^7.10.4"
- "@babel/parser" "^7.10.4"
- "@babel/types" "^7.10.4"
-
"@babel/template@^7.14.5":
version "7.14.5"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4"
@@ -441,37 +1092,63 @@
"@babel/parser" "^7.14.5"
"@babel/types" "^7.14.5"
-"@babel/traverse@^7.1.0", "@babel/traverse@^7.10.4", "@babel/traverse@^7.11.5":
- version "7.11.5"
- resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.11.5.tgz#be777b93b518eb6d76ee2e1ea1d143daa11e61c3"
- integrity sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ==
+"@babel/template@^7.22.15":
+ version "7.22.15"
+ resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38"
+ integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==
+ dependencies:
+ "@babel/code-frame" "^7.22.13"
+ "@babel/parser" "^7.22.15"
+ "@babel/types" "^7.22.15"
+
+"@babel/template@^7.28.6":
+ version "7.28.6"
+ resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.28.6.tgz#0e7e56ecedb78aeef66ce7972b082fce76a23e57"
+ integrity sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==
+ dependencies:
+ "@babel/code-frame" "^7.28.6"
+ "@babel/parser" "^7.28.6"
+ "@babel/types" "^7.28.6"
+
+"@babel/template@^7.3.3":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278"
+ integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==
dependencies:
"@babel/code-frame" "^7.10.4"
- "@babel/generator" "^7.11.5"
- "@babel/helper-function-name" "^7.10.4"
- "@babel/helper-split-export-declaration" "^7.11.0"
- "@babel/parser" "^7.11.5"
- "@babel/types" "^7.11.5"
- debug "^4.1.0"
- globals "^11.1.0"
- lodash "^4.17.19"
+ "@babel/parser" "^7.10.4"
+ "@babel/types" "^7.10.4"
"@babel/traverse@^7.15.0":
- version "7.15.0"
- resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.15.0.tgz#4cca838fd1b2a03283c1f38e141f639d60b3fc98"
- integrity sha512-392d8BN0C9eVxVWd8H6x9WfipgVH5IaIoLp23334Sc1vbKKWINnvwRpb4us0xtPaCumlwbTtIYNA0Dv/32sVFw==
- dependencies:
- "@babel/code-frame" "^7.14.5"
- "@babel/generator" "^7.15.0"
- "@babel/helper-function-name" "^7.14.5"
- "@babel/helper-hoist-variables" "^7.14.5"
- "@babel/helper-split-export-declaration" "^7.14.5"
- "@babel/parser" "^7.15.0"
- "@babel/types" "^7.15.0"
+ version "7.23.2"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8"
+ integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==
+ dependencies:
+ "@babel/code-frame" "^7.22.13"
+ "@babel/generator" "^7.23.0"
+ "@babel/helper-environment-visitor" "^7.22.20"
+ "@babel/helper-function-name" "^7.23.0"
+ "@babel/helper-hoist-variables" "^7.22.5"
+ "@babel/helper-split-export-declaration" "^7.22.6"
+ "@babel/parser" "^7.23.0"
+ "@babel/types" "^7.23.0"
debug "^4.1.0"
globals "^11.1.0"
-"@babel/types@^7.0.0", "@babel/types@^7.10.4", "@babel/types@^7.11.0", "@babel/types@^7.11.5", "@babel/types@^7.3.0", "@babel/types@^7.3.3":
+"@babel/traverse@^7.28.6", "@babel/traverse@^7.29.0":
+ version "7.29.0"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.29.0.tgz#f323d05001440253eead3c9c858adbe00b90310a"
+ integrity sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==
+ dependencies:
+ "@babel/code-frame" "^7.29.0"
+ "@babel/generator" "^7.29.0"
+ "@babel/helper-globals" "^7.28.0"
+ "@babel/parser" "^7.29.0"
+ "@babel/template" "^7.28.6"
+ "@babel/types" "^7.29.0"
+ debug "^4.3.1"
+
+"@babel/types@^7.0.0", "@babel/types@^7.10.4", "@babel/types@^7.3.0", "@babel/types@^7.3.3":
version "7.11.5"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.5.tgz#d9de577d01252d77c6800cee039ee64faf75662d"
integrity sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==
@@ -488,19 +1165,28 @@
"@babel/helper-validator-identifier" "^7.14.9"
to-fast-properties "^2.0.0"
+"@babel/types@^7.20.7", "@babel/types@^7.28.6", "@babel/types@^7.29.0":
+ version "7.29.0"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.29.0.tgz#9f5b1e838c446e72cf3cd4b918152b8c605e37c7"
+ integrity sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==
+ dependencies:
+ "@babel/helper-string-parser" "^7.27.1"
+ "@babel/helper-validator-identifier" "^7.28.5"
+
+"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0":
+ version "7.23.0"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb"
+ integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==
+ dependencies:
+ "@babel/helper-string-parser" "^7.22.5"
+ "@babel/helper-validator-identifier" "^7.22.20"
+ to-fast-properties "^2.0.0"
+
"@bcoe/v8-coverage@^0.2.3":
version "0.2.3"
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
-"@cnakazawa/watch@^1.0.3":
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a"
- integrity sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==
- dependencies:
- exec-sh "^0.3.2"
- minimist "^1.2.0"
-
"@eslint/eslintrc@^0.1.3":
version "0.1.3"
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.1.3.tgz#7d1a2b2358552cc04834c0979bd4275362e37085"
@@ -533,204 +1219,301 @@
resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd"
integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==
-"@jest/console@^26.5.2":
- version "26.5.2"
- resolved "https://registry.yarnpkg.com/@jest/console/-/console-26.5.2.tgz#94fc4865b1abed7c352b5e21e6c57be4b95604a6"
- integrity sha512-lJELzKINpF1v74DXHbCRIkQ/+nUV1M+ntj+X1J8LxCgpmJZjfLmhFejiMSbjjD66fayxl5Z06tbs3HMyuik6rw==
+"@istanbuljs/schema@^0.1.3":
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98"
+ integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==
+
+"@jest/console@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc"
+ integrity sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==
dependencies:
- "@jest/types" "^26.5.2"
+ "@jest/types" "^29.6.3"
"@types/node" "*"
chalk "^4.0.0"
- jest-message-util "^26.5.2"
- jest-util "^26.5.2"
+ jest-message-util "^29.7.0"
+ jest-util "^29.7.0"
slash "^3.0.0"
-"@jest/core@^26.5.3":
- version "26.5.3"
- resolved "https://registry.yarnpkg.com/@jest/core/-/core-26.5.3.tgz#712ed4adb64c3bda256a3f400ff1d3eb2a031f13"
- integrity sha512-CiU0UKFF1V7KzYTVEtFbFmGLdb2g4aTtY0WlyUfLgj/RtoTnJFhh50xKKr7OYkdmBUlGFSa2mD1TU3UZ6OLd4g==
+"@jest/core@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f"
+ integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==
dependencies:
- "@jest/console" "^26.5.2"
- "@jest/reporters" "^26.5.3"
- "@jest/test-result" "^26.5.2"
- "@jest/transform" "^26.5.2"
- "@jest/types" "^26.5.2"
+ "@jest/console" "^29.7.0"
+ "@jest/reporters" "^29.7.0"
+ "@jest/test-result" "^29.7.0"
+ "@jest/transform" "^29.7.0"
+ "@jest/types" "^29.6.3"
"@types/node" "*"
ansi-escapes "^4.2.1"
chalk "^4.0.0"
+ ci-info "^3.2.0"
exit "^0.1.2"
- graceful-fs "^4.2.4"
- jest-changed-files "^26.5.2"
- jest-config "^26.5.3"
- jest-haste-map "^26.5.2"
- jest-message-util "^26.5.2"
- jest-regex-util "^26.0.0"
- jest-resolve "^26.5.2"
- jest-resolve-dependencies "^26.5.3"
- jest-runner "^26.5.3"
- jest-runtime "^26.5.3"
- jest-snapshot "^26.5.3"
- jest-util "^26.5.2"
- jest-validate "^26.5.3"
- jest-watcher "^26.5.2"
- micromatch "^4.0.2"
- p-each-series "^2.1.0"
- rimraf "^3.0.0"
+ graceful-fs "^4.2.9"
+ jest-changed-files "^29.7.0"
+ jest-config "^29.7.0"
+ jest-haste-map "^29.7.0"
+ jest-message-util "^29.7.0"
+ jest-regex-util "^29.6.3"
+ jest-resolve "^29.7.0"
+ jest-resolve-dependencies "^29.7.0"
+ jest-runner "^29.7.0"
+ jest-runtime "^29.7.0"
+ jest-snapshot "^29.7.0"
+ jest-util "^29.7.0"
+ jest-validate "^29.7.0"
+ jest-watcher "^29.7.0"
+ micromatch "^4.0.4"
+ pretty-format "^29.7.0"
slash "^3.0.0"
strip-ansi "^6.0.0"
-"@jest/environment@^26.5.2":
- version "26.5.2"
- resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-26.5.2.tgz#eba3cfc698f6e03739628f699c28e8a07f5e65fe"
- integrity sha512-YjhCD/Zhkz0/1vdlS/QN6QmuUdDkpgBdK4SdiVg4Y19e29g4VQYN5Xg8+YuHjdoWGY7wJHMxc79uDTeTOy9Ngw==
+"@jest/environment@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7"
+ integrity sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==
dependencies:
- "@jest/fake-timers" "^26.5.2"
- "@jest/types" "^26.5.2"
+ "@jest/fake-timers" "^29.7.0"
+ "@jest/types" "^29.6.3"
"@types/node" "*"
- jest-mock "^26.5.2"
+ jest-mock "^29.7.0"
+
+"@jest/expect-utils@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6"
+ integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==
+ dependencies:
+ jest-get-type "^29.6.3"
+
+"@jest/expect@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2"
+ integrity sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==
+ dependencies:
+ expect "^29.7.0"
+ jest-snapshot "^29.7.0"
-"@jest/fake-timers@^26.5.2":
- version "26.5.2"
- resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-26.5.2.tgz#1291ac81680ceb0dc7daa1f92c059307eea6400a"
- integrity sha512-09Hn5Oraqt36V1akxQeWMVL0fR9c6PnEhpgLaYvREXZJAh2H2Y+QLCsl0g7uMoJeoWJAuz4tozk1prbR1Fc1sw==
+"@jest/fake-timers@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565"
+ integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==
dependencies:
- "@jest/types" "^26.5.2"
- "@sinonjs/fake-timers" "^6.0.1"
+ "@jest/types" "^29.6.3"
+ "@sinonjs/fake-timers" "^10.0.2"
"@types/node" "*"
- jest-message-util "^26.5.2"
- jest-mock "^26.5.2"
- jest-util "^26.5.2"
+ jest-message-util "^29.7.0"
+ jest-mock "^29.7.0"
+ jest-util "^29.7.0"
-"@jest/globals@^26.5.3":
- version "26.5.3"
- resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-26.5.3.tgz#90769b40e0af3fa0b28f6d8c5bbe3712467243fd"
- integrity sha512-7QztI0JC2CuB+Wx1VdnOUNeIGm8+PIaqngYsZXQCkH2QV0GFqzAYc9BZfU0nuqA6cbYrWh5wkuMzyii3P7deug==
+"@jest/globals@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d"
+ integrity sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==
dependencies:
- "@jest/environment" "^26.5.2"
- "@jest/types" "^26.5.2"
- expect "^26.5.3"
+ "@jest/environment" "^29.7.0"
+ "@jest/expect" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ jest-mock "^29.7.0"
-"@jest/reporters@^26.5.3":
- version "26.5.3"
- resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-26.5.3.tgz#e810e9c2b670f33f1c09e9975749260ca12f1c17"
- integrity sha512-X+vR0CpfMQzYcYmMFKNY9n4jklcb14Kffffp7+H/MqitWnb0440bW2L76NGWKAa+bnXhNoZr+lCVtdtPmfJVOQ==
+"@jest/reporters@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7"
+ integrity sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==
dependencies:
"@bcoe/v8-coverage" "^0.2.3"
- "@jest/console" "^26.5.2"
- "@jest/test-result" "^26.5.2"
- "@jest/transform" "^26.5.2"
- "@jest/types" "^26.5.2"
+ "@jest/console" "^29.7.0"
+ "@jest/test-result" "^29.7.0"
+ "@jest/transform" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ "@jridgewell/trace-mapping" "^0.3.18"
+ "@types/node" "*"
chalk "^4.0.0"
collect-v8-coverage "^1.0.0"
exit "^0.1.2"
- glob "^7.1.2"
- graceful-fs "^4.2.4"
+ glob "^7.1.3"
+ graceful-fs "^4.2.9"
istanbul-lib-coverage "^3.0.0"
- istanbul-lib-instrument "^4.0.3"
+ istanbul-lib-instrument "^6.0.0"
istanbul-lib-report "^3.0.0"
istanbul-lib-source-maps "^4.0.0"
- istanbul-reports "^3.0.2"
- jest-haste-map "^26.5.2"
- jest-resolve "^26.5.2"
- jest-util "^26.5.2"
- jest-worker "^26.5.0"
+ istanbul-reports "^3.1.3"
+ jest-message-util "^29.7.0"
+ jest-util "^29.7.0"
+ jest-worker "^29.7.0"
slash "^3.0.0"
- source-map "^0.6.0"
string-length "^4.0.1"
- terminal-link "^2.0.0"
- v8-to-istanbul "^6.0.1"
- optionalDependencies:
- node-notifier "^8.0.0"
+ strip-ansi "^6.0.0"
+ v8-to-istanbul "^9.0.1"
+
+"@jest/schemas@^29.6.3":
+ version "29.6.3"
+ resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03"
+ integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==
+ dependencies:
+ "@sinclair/typebox" "^0.27.8"
-"@jest/source-map@^26.5.0":
- version "26.5.0"
- resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-26.5.0.tgz#98792457c85bdd902365cd2847b58fff05d96367"
- integrity sha512-jWAw9ZwYHJMe9eZq/WrsHlwF8E3hM9gynlcDpOyCb9bR8wEd9ZNBZCi7/jZyzHxC7t3thZ10gO2IDhu0bPKS5g==
+"@jest/source-map@^29.6.3":
+ version "29.6.3"
+ resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4"
+ integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==
dependencies:
+ "@jridgewell/trace-mapping" "^0.3.18"
callsites "^3.0.0"
- graceful-fs "^4.2.4"
- source-map "^0.6.0"
+ graceful-fs "^4.2.9"
-"@jest/test-result@^26.5.2":
- version "26.5.2"
- resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-26.5.2.tgz#cc1a44cfd4db2ecee3fb0bc4e9fe087aa54b5230"
- integrity sha512-E/Zp6LURJEGSCWpoMGmCFuuEI1OWuI3hmZwmULV0GsgJBh7u0rwqioxhRU95euUuviqBDN8ruX/vP/4bwYolXw==
+"@jest/test-result@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c"
+ integrity sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==
dependencies:
- "@jest/console" "^26.5.2"
- "@jest/types" "^26.5.2"
+ "@jest/console" "^29.7.0"
+ "@jest/types" "^29.6.3"
"@types/istanbul-lib-coverage" "^2.0.0"
collect-v8-coverage "^1.0.0"
-"@jest/test-sequencer@^26.5.3":
- version "26.5.3"
- resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-26.5.3.tgz#9ae0ab9bc37d5171b28424029192e50229814f8d"
- integrity sha512-Wqzb7aQ13L3T47xHdpUqYMOpiqz6Dx2QDDghp5AV/eUDXR7JieY+E1s233TQlNyl+PqtqgjVokmyjzX/HA51BA==
- dependencies:
- "@jest/test-result" "^26.5.2"
- graceful-fs "^4.2.4"
- jest-haste-map "^26.5.2"
- jest-runner "^26.5.3"
- jest-runtime "^26.5.3"
-
-"@jest/transform@^26.5.2":
- version "26.5.2"
- resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-26.5.2.tgz#6a0033a1d24316a1c75184d010d864f2c681bef5"
- integrity sha512-AUNjvexh+APhhmS8S+KboPz+D3pCxPvEAGduffaAJYxIFxGi/ytZQkrqcKDUU0ERBAo5R7087fyOYr2oms1seg==
- dependencies:
- "@babel/core" "^7.1.0"
- "@jest/types" "^26.5.2"
- babel-plugin-istanbul "^6.0.0"
+"@jest/test-sequencer@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce"
+ integrity sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==
+ dependencies:
+ "@jest/test-result" "^29.7.0"
+ graceful-fs "^4.2.9"
+ jest-haste-map "^29.7.0"
+ slash "^3.0.0"
+
+"@jest/transform@^29.7.0":
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c"
+ integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==
+ dependencies:
+ "@babel/core" "^7.11.6"
+ "@jest/types" "^29.6.3"
+ "@jridgewell/trace-mapping" "^0.3.18"
+ babel-plugin-istanbul "^6.1.1"
chalk "^4.0.0"
- convert-source-map "^1.4.0"
- fast-json-stable-stringify "^2.0.0"
- graceful-fs "^4.2.4"
- jest-haste-map "^26.5.2"
- jest-regex-util "^26.0.0"
- jest-util "^26.5.2"
- micromatch "^4.0.2"
- pirates "^4.0.1"
+ convert-source-map "^2.0.0"
+ fast-json-stable-stringify "^2.1.0"
+ graceful-fs "^4.2.9"
+ jest-haste-map "^29.7.0"
+ jest-regex-util "^29.6.3"
+ jest-util "^29.7.0"
+ micromatch "^4.0.4"
+ pirates "^4.0.4"
slash "^3.0.0"
- source-map "^0.6.1"
- write-file-atomic "^3.0.0"
+ write-file-atomic "^4.0.2"
-"@jest/types@^26.5.2":
- version "26.5.2"
- resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.5.2.tgz#44c24f30c8ee6c7f492ead9ec3f3c62a5289756d"
- integrity sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==
+"@jest/types@^29.6.3":
+ version "29.6.3"
+ resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59"
+ integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==
dependencies:
+ "@jest/schemas" "^29.6.3"
"@types/istanbul-lib-coverage" "^2.0.0"
"@types/istanbul-reports" "^3.0.0"
"@types/node" "*"
- "@types/yargs" "^15.0.0"
+ "@types/yargs" "^17.0.8"
chalk "^4.0.0"
-"@shelf/jest-mongodb@^1.2.3":
- version "1.2.3"
- resolved "https://registry.yarnpkg.com/@shelf/jest-mongodb/-/jest-mongodb-1.2.3.tgz#7cb34f0bcb71871b0d1c8d16a4f1fdb18b1620df"
- integrity sha512-RGECov7b9anpHqrEoegYeZFWN3WEOw/3hPu3fQUi4gnNIGH0jyMVCQd4DgB37n2aoEWFfe7Kq59aQUrgIQRITA==
+"@jridgewell/gen-mapping@^0.3.12", "@jridgewell/gen-mapping@^0.3.5":
+ version "0.3.13"
+ resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f"
+ integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==
+ dependencies:
+ "@jridgewell/sourcemap-codec" "^1.5.0"
+ "@jridgewell/trace-mapping" "^0.3.24"
+
+"@jridgewell/gen-mapping@^0.3.2":
+ version "0.3.3"
+ resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098"
+ integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==
+ dependencies:
+ "@jridgewell/set-array" "^1.0.1"
+ "@jridgewell/sourcemap-codec" "^1.4.10"
+ "@jridgewell/trace-mapping" "^0.3.9"
+
+"@jridgewell/remapping@^2.3.5":
+ version "2.3.5"
+ resolved "https://registry.yarnpkg.com/@jridgewell/remapping/-/remapping-2.3.5.tgz#375c476d1972947851ba1e15ae8f123047445aa1"
+ integrity sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==
dependencies:
- debug "4.1.1"
- mongodb-memory-server "6.6.7"
- uuid "8.3.0"
+ "@jridgewell/gen-mapping" "^0.3.5"
+ "@jridgewell/trace-mapping" "^0.3.24"
+
+"@jridgewell/resolve-uri@^3.1.0":
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721"
+ integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==
+
+"@jridgewell/set-array@^1.0.1":
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
+ integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
+
+"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14":
+ version "1.4.15"
+ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
+ integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
+
+"@jridgewell/sourcemap-codec@^1.5.0":
+ version "1.5.5"
+ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba"
+ integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==
+
+"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.28":
+ version "0.3.31"
+ resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0"
+ integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==
+ dependencies:
+ "@jridgewell/resolve-uri" "^3.1.0"
+ "@jridgewell/sourcemap-codec" "^1.4.14"
+
+"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9":
+ version "0.3.19"
+ resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz#f8a3249862f91be48d3127c3cfe992f79b4b8811"
+ integrity sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==
+ dependencies:
+ "@jridgewell/resolve-uri" "^3.1.0"
+ "@jridgewell/sourcemap-codec" "^1.4.14"
+
+"@mongodb-js/saslprep@^1.1.0":
+ version "1.4.5"
+ resolved "https://registry.yarnpkg.com/@mongodb-js/saslprep/-/saslprep-1.4.5.tgz#0f53a6c5a350fbe4bfa12cc80b69e8d358f1bbc0"
+ integrity sha512-k64Lbyb7ycCSXHSLzxVdb2xsKGPMvYZfCICXvDsI8Z65CeWQzTEKS4YmGbnqw+U9RBvLPTsB6UCmwkgsDTGWIw==
+ dependencies:
+ sparse-bitfield "^3.0.3"
+
+"@mongodb-js/saslprep@^1.3.0":
+ version "1.4.4"
+ resolved "https://registry.yarnpkg.com/@mongodb-js/saslprep/-/saslprep-1.4.4.tgz#34a946ff6ae142e8f2259b87f2935f8284ba874d"
+ integrity sha512-p7X/ytJDIdwUfFL/CLOhKgdfJe1Fa8uw9seJYvdOmnP9JBWGWHW69HkOixXS6Wy9yvGf1MbhcS6lVmrhy4jm2g==
+ dependencies:
+ sparse-bitfield "^3.0.3"
+
+"@sinclair/typebox@^0.27.8":
+ version "0.27.8"
+ resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e"
+ integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==
"@sindresorhus/is@^0.14.0":
version "0.14.0"
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==
-"@sinonjs/commons@^1.7.0":
- version "1.8.1"
- resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.1.tgz#e7df00f98a203324f6dc7cc606cad9d4a8ab2217"
- integrity sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==
+"@sinonjs/commons@^3.0.0":
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd"
+ integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==
dependencies:
type-detect "4.0.8"
-"@sinonjs/fake-timers@^6.0.1":
- version "6.0.1"
- resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40"
- integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==
+"@sinonjs/fake-timers@^10.0.2":
+ version "10.3.0"
+ resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66"
+ integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==
dependencies:
- "@sinonjs/commons" "^1.7.0"
+ "@sinonjs/commons" "^3.0.0"
"@slack/bolt@^2.2.3":
version "2.4.1"
@@ -792,6 +1575,409 @@
p-queue "^2.4.2"
p-retry "^4.0.0"
+"@smithy/abort-controller@^4.2.8":
+ version "4.2.8"
+ resolved "https://registry.yarnpkg.com/@smithy/abort-controller/-/abort-controller-4.2.8.tgz#3bfd7a51acce88eaec9a65c3382542be9f3a053a"
+ integrity sha512-peuVfkYHAmS5ybKxWcfraK7WBBP0J+rkfUcbHJJKQ4ir3UAUNQI+Y4Vt/PqSzGqgloJ5O1dk7+WzNL8wcCSXbw==
+ dependencies:
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@smithy/config-resolver@^4.4.6":
+ version "4.4.6"
+ resolved "https://registry.yarnpkg.com/@smithy/config-resolver/-/config-resolver-4.4.6.tgz#bd7f65b3da93f37f1c97a399ade0124635c02297"
+ integrity sha512-qJpzYC64kaj3S0fueiu3kXm8xPrR3PcXDPEgnaNMRn0EjNSZFoFjvbUp0YUDsRhN1CB90EnHJtbxWKevnH99UQ==
+ dependencies:
+ "@smithy/node-config-provider" "^4.3.8"
+ "@smithy/types" "^4.12.0"
+ "@smithy/util-config-provider" "^4.2.0"
+ "@smithy/util-endpoints" "^3.2.8"
+ "@smithy/util-middleware" "^4.2.8"
+ tslib "^2.6.2"
+
+"@smithy/core@^3.22.0", "@smithy/core@^3.22.1":
+ version "3.22.1"
+ resolved "https://registry.yarnpkg.com/@smithy/core/-/core-3.22.1.tgz#c34180d541c9dc5d29412809a6aa497ea47d74f8"
+ integrity sha512-x3ie6Crr58MWrm4viHqqy2Du2rHYZjwu8BekasrQx4ca+Y24dzVAwq3yErdqIbc2G3I0kLQA13PQ+/rde+u65g==
+ dependencies:
+ "@smithy/middleware-serde" "^4.2.9"
+ "@smithy/protocol-http" "^5.3.8"
+ "@smithy/types" "^4.12.0"
+ "@smithy/util-base64" "^4.3.0"
+ "@smithy/util-body-length-browser" "^4.2.0"
+ "@smithy/util-middleware" "^4.2.8"
+ "@smithy/util-stream" "^4.5.11"
+ "@smithy/util-utf8" "^4.2.0"
+ "@smithy/uuid" "^1.1.0"
+ tslib "^2.6.2"
+
+"@smithy/credential-provider-imds@^4.2.8":
+ version "4.2.8"
+ resolved "https://registry.yarnpkg.com/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.8.tgz#b2f4bf759ab1c35c0dd00fa3470263c749ebf60f"
+ integrity sha512-FNT0xHS1c/CPN8upqbMFP83+ul5YgdisfCfkZ86Jh2NSmnqw/AJ6x5pEogVCTVvSm7j9MopRU89bmDelxuDMYw==
+ dependencies:
+ "@smithy/node-config-provider" "^4.3.8"
+ "@smithy/property-provider" "^4.2.8"
+ "@smithy/types" "^4.12.0"
+ "@smithy/url-parser" "^4.2.8"
+ tslib "^2.6.2"
+
+"@smithy/fetch-http-handler@^5.3.9":
+ version "5.3.9"
+ resolved "https://registry.yarnpkg.com/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.9.tgz#edfc9e90e0c7538c81e22e748d62c0066cc91d58"
+ integrity sha512-I4UhmcTYXBrct03rwzQX1Y/iqQlzVQaPxWjCjula++5EmWq9YGBrx6bbGqluGc1f0XEfhSkiY4jhLgbsJUMKRA==
+ dependencies:
+ "@smithy/protocol-http" "^5.3.8"
+ "@smithy/querystring-builder" "^4.2.8"
+ "@smithy/types" "^4.12.0"
+ "@smithy/util-base64" "^4.3.0"
+ tslib "^2.6.2"
+
+"@smithy/hash-node@^4.2.8":
+ version "4.2.8"
+ resolved "https://registry.yarnpkg.com/@smithy/hash-node/-/hash-node-4.2.8.tgz#c21eb055041716cd492dda3a109852a94b6d47bb"
+ integrity sha512-7ZIlPbmaDGxVoxErDZnuFG18WekhbA/g2/i97wGj+wUBeS6pcUeAym8u4BXh/75RXWhgIJhyC11hBzig6MljwA==
+ dependencies:
+ "@smithy/types" "^4.12.0"
+ "@smithy/util-buffer-from" "^4.2.0"
+ "@smithy/util-utf8" "^4.2.0"
+ tslib "^2.6.2"
+
+"@smithy/invalid-dependency@^4.2.8":
+ version "4.2.8"
+ resolved "https://registry.yarnpkg.com/@smithy/invalid-dependency/-/invalid-dependency-4.2.8.tgz#c578bc6d5540c877aaed5034b986b5f6bd896451"
+ integrity sha512-N9iozRybwAQ2dn9Fot9kI6/w9vos2oTXLhtK7ovGqwZjlOcxu6XhPlpLpC+INsxktqHinn5gS2DXDjDF2kG5sQ==
+ dependencies:
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@smithy/is-array-buffer@^2.2.0":
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz#f84f0d9f9a36601a9ca9381688bd1b726fd39111"
+ integrity sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==
+ dependencies:
+ tslib "^2.6.2"
+
+"@smithy/is-array-buffer@^4.2.0":
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-4.2.0.tgz#b0f874c43887d3ad44f472a0f3f961bcce0550c2"
+ integrity sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==
+ dependencies:
+ tslib "^2.6.2"
+
+"@smithy/middleware-content-length@^4.2.8":
+ version "4.2.8"
+ resolved "https://registry.yarnpkg.com/@smithy/middleware-content-length/-/middleware-content-length-4.2.8.tgz#82c1df578fa70fe5800cf305b8788b9d2836a3e4"
+ integrity sha512-RO0jeoaYAB1qBRhfVyq0pMgBoUK34YEJxVxyjOWYZiOKOq2yMZ4MnVXMZCUDenpozHue207+9P5ilTV1zeda0A==
+ dependencies:
+ "@smithy/protocol-http" "^5.3.8"
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@smithy/middleware-endpoint@^4.4.12", "@smithy/middleware-endpoint@^4.4.13":
+ version "4.4.13"
+ resolved "https://registry.yarnpkg.com/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.13.tgz#8a5dda67cbf8e63155a908a724e7ae09b763baad"
+ integrity sha512-x6vn0PjYmGdNuKh/juUJJewZh7MoQ46jYaJ2mvekF4EesMuFfrl4LaW/k97Zjf8PTCPQmPgMvwewg7eNoH9n5w==
+ dependencies:
+ "@smithy/core" "^3.22.1"
+ "@smithy/middleware-serde" "^4.2.9"
+ "@smithy/node-config-provider" "^4.3.8"
+ "@smithy/shared-ini-file-loader" "^4.4.3"
+ "@smithy/types" "^4.12.0"
+ "@smithy/url-parser" "^4.2.8"
+ "@smithy/util-middleware" "^4.2.8"
+ tslib "^2.6.2"
+
+"@smithy/middleware-retry@^4.4.29":
+ version "4.4.30"
+ resolved "https://registry.yarnpkg.com/@smithy/middleware-retry/-/middleware-retry-4.4.30.tgz#a0548803044069b53a332606d4b4f803f07f8963"
+ integrity sha512-CBGyFvN0f8hlnqKH/jckRDz78Snrp345+PVk8Ux7pnkUCW97Iinse59lY78hBt04h1GZ6hjBN94BRwZy1xC8Bg==
+ dependencies:
+ "@smithy/node-config-provider" "^4.3.8"
+ "@smithy/protocol-http" "^5.3.8"
+ "@smithy/service-error-classification" "^4.2.8"
+ "@smithy/smithy-client" "^4.11.2"
+ "@smithy/types" "^4.12.0"
+ "@smithy/util-middleware" "^4.2.8"
+ "@smithy/util-retry" "^4.2.8"
+ "@smithy/uuid" "^1.1.0"
+ tslib "^2.6.2"
+
+"@smithy/middleware-serde@^4.2.9":
+ version "4.2.9"
+ resolved "https://registry.yarnpkg.com/@smithy/middleware-serde/-/middleware-serde-4.2.9.tgz#fd9d9b02b265aef67c9a30f55c2a5038fc9ca791"
+ integrity sha512-eMNiej0u/snzDvlqRGSN3Vl0ESn3838+nKyVfF2FKNXFbi4SERYT6PR392D39iczngbqqGG0Jl1DlCnp7tBbXQ==
+ dependencies:
+ "@smithy/protocol-http" "^5.3.8"
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@smithy/middleware-stack@^4.2.8":
+ version "4.2.8"
+ resolved "https://registry.yarnpkg.com/@smithy/middleware-stack/-/middleware-stack-4.2.8.tgz#4fa9cfaaa05f664c9bb15d45608f3cb4f6da2b76"
+ integrity sha512-w6LCfOviTYQjBctOKSwy6A8FIkQy7ICvglrZFl6Bw4FmcQ1Z420fUtIhxaUZZshRe0VCq4kvDiPiXrPZAe8oRA==
+ dependencies:
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@smithy/node-config-provider@^4.3.8":
+ version "4.3.8"
+ resolved "https://registry.yarnpkg.com/@smithy/node-config-provider/-/node-config-provider-4.3.8.tgz#85a0683448262b2eb822f64c14278d4887526377"
+ integrity sha512-aFP1ai4lrbVlWjfpAfRSL8KFcnJQYfTl5QxLJXY32vghJrDuFyPZ6LtUL+JEGYiFRG1PfPLHLoxj107ulncLIg==
+ dependencies:
+ "@smithy/property-provider" "^4.2.8"
+ "@smithy/shared-ini-file-loader" "^4.4.3"
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@smithy/node-http-handler@^4.4.8", "@smithy/node-http-handler@^4.4.9":
+ version "4.4.9"
+ resolved "https://registry.yarnpkg.com/@smithy/node-http-handler/-/node-http-handler-4.4.9.tgz#c167e5b8aed33c5edaf25b903ed9866858499c93"
+ integrity sha512-KX5Wml5mF+luxm1szW4QDz32e3NObgJ4Fyw+irhph4I/2geXwUy4jkIMUs5ZPGflRBeR6BUkC2wqIab4Llgm3w==
+ dependencies:
+ "@smithy/abort-controller" "^4.2.8"
+ "@smithy/protocol-http" "^5.3.8"
+ "@smithy/querystring-builder" "^4.2.8"
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@smithy/property-provider@^4.2.8":
+ version "4.2.8"
+ resolved "https://registry.yarnpkg.com/@smithy/property-provider/-/property-provider-4.2.8.tgz#6e37b30923d2d31370c50ce303a4339020031472"
+ integrity sha512-EtCTbyIveCKeOXDSWSdze3k612yCPq1YbXsbqX3UHhkOSW8zKsM9NOJG5gTIya0vbY2DIaieG8pKo1rITHYL0w==
+ dependencies:
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@smithy/protocol-http@^5.3.8":
+ version "5.3.8"
+ resolved "https://registry.yarnpkg.com/@smithy/protocol-http/-/protocol-http-5.3.8.tgz#0938f69a3c3673694c2f489a640fce468ce75006"
+ integrity sha512-QNINVDhxpZ5QnP3aviNHQFlRogQZDfYlCkQT+7tJnErPQbDhysondEjhikuANxgMsZrkGeiAxXy4jguEGsDrWQ==
+ dependencies:
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@smithy/querystring-builder@^4.2.8":
+ version "4.2.8"
+ resolved "https://registry.yarnpkg.com/@smithy/querystring-builder/-/querystring-builder-4.2.8.tgz#2fa72d29eb1844a6a9933038bbbb14d6fe385e93"
+ integrity sha512-Xr83r31+DrE8CP3MqPgMJl+pQlLLmOfiEUnoyAlGzzJIrEsbKsPy1hqH0qySaQm4oWrCBlUqRt+idEgunKB+iw==
+ dependencies:
+ "@smithy/types" "^4.12.0"
+ "@smithy/util-uri-escape" "^4.2.0"
+ tslib "^2.6.2"
+
+"@smithy/querystring-parser@^4.2.8":
+ version "4.2.8"
+ resolved "https://registry.yarnpkg.com/@smithy/querystring-parser/-/querystring-parser-4.2.8.tgz#aa3f2456180ce70242e89018d0b1ebd4782a6347"
+ integrity sha512-vUurovluVy50CUlazOiXkPq40KGvGWSdmusa3130MwrR1UNnNgKAlj58wlOe61XSHRpUfIIh6cE0zZ8mzKaDPA==
+ dependencies:
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@smithy/service-error-classification@^4.2.8":
+ version "4.2.8"
+ resolved "https://registry.yarnpkg.com/@smithy/service-error-classification/-/service-error-classification-4.2.8.tgz#6d89dbad4f4978d7b75a44af8c18c22455a16cdc"
+ integrity sha512-mZ5xddodpJhEt3RkCjbmUQuXUOaPNTkbMGR0bcS8FE0bJDLMZlhmpgrvPNCYglVw5rsYTpSnv19womw9WWXKQQ==
+ dependencies:
+ "@smithy/types" "^4.12.0"
+
+"@smithy/shared-ini-file-loader@^4.4.3":
+ version "4.4.3"
+ resolved "https://registry.yarnpkg.com/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.3.tgz#6054215ecb3a6532b13aa49a9fbda640b63be50e"
+ integrity sha512-DfQjxXQnzC5UbCUPeC3Ie8u+rIWZTvuDPAGU/BxzrOGhRvgUanaP68kDZA+jaT3ZI+djOf+4dERGlm9mWfFDrg==
+ dependencies:
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@smithy/signature-v4@^5.3.8":
+ version "5.3.8"
+ resolved "https://registry.yarnpkg.com/@smithy/signature-v4/-/signature-v4-5.3.8.tgz#796619b10b7cc9467d0625b0ebd263ae04fdfb76"
+ integrity sha512-6A4vdGj7qKNRF16UIcO8HhHjKW27thsxYci+5r/uVRkdcBEkOEiY8OMPuydLX4QHSrJqGHPJzPRwwVTqbLZJhg==
+ dependencies:
+ "@smithy/is-array-buffer" "^4.2.0"
+ "@smithy/protocol-http" "^5.3.8"
+ "@smithy/types" "^4.12.0"
+ "@smithy/util-hex-encoding" "^4.2.0"
+ "@smithy/util-middleware" "^4.2.8"
+ "@smithy/util-uri-escape" "^4.2.0"
+ "@smithy/util-utf8" "^4.2.0"
+ tslib "^2.6.2"
+
+"@smithy/smithy-client@^4.11.1", "@smithy/smithy-client@^4.11.2":
+ version "4.11.2"
+ resolved "https://registry.yarnpkg.com/@smithy/smithy-client/-/smithy-client-4.11.2.tgz#1f6a4d75625dbaa16bafbe9b10cf6a41c98fe3da"
+ integrity sha512-SCkGmFak/xC1n7hKRsUr6wOnBTJ3L22Qd4e8H1fQIuKTAjntwgU8lrdMe7uHdiT2mJAOWA/60qaW9tiMu69n1A==
+ dependencies:
+ "@smithy/core" "^3.22.1"
+ "@smithy/middleware-endpoint" "^4.4.13"
+ "@smithy/middleware-stack" "^4.2.8"
+ "@smithy/protocol-http" "^5.3.8"
+ "@smithy/types" "^4.12.0"
+ "@smithy/util-stream" "^4.5.11"
+ tslib "^2.6.2"
+
+"@smithy/types@^4.12.0":
+ version "4.12.0"
+ resolved "https://registry.yarnpkg.com/@smithy/types/-/types-4.12.0.tgz#55d2479080922bda516092dbf31916991d9c6fee"
+ integrity sha512-9YcuJVTOBDjg9LWo23Qp0lTQ3D7fQsQtwle0jVfpbUHy9qBwCEgKuVH4FqFB3VYu0nwdHKiEMA+oXz7oV8X1kw==
+ dependencies:
+ tslib "^2.6.2"
+
+"@smithy/url-parser@^4.2.8":
+ version "4.2.8"
+ resolved "https://registry.yarnpkg.com/@smithy/url-parser/-/url-parser-4.2.8.tgz#b44267cd704abe114abcd00580acdd9e4acc1177"
+ integrity sha512-NQho9U68TGMEU639YkXnVMV3GEFFULmmaWdlu1E9qzyIePOHsoSnagTGSDv1Zi8DCNN6btxOSdgmy5E/hsZwhA==
+ dependencies:
+ "@smithy/querystring-parser" "^4.2.8"
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@smithy/util-base64@^4.3.0":
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/@smithy/util-base64/-/util-base64-4.3.0.tgz#5e287b528793aa7363877c1a02cd880d2e76241d"
+ integrity sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==
+ dependencies:
+ "@smithy/util-buffer-from" "^4.2.0"
+ "@smithy/util-utf8" "^4.2.0"
+ tslib "^2.6.2"
+
+"@smithy/util-body-length-browser@^4.2.0":
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.0.tgz#04e9fc51ee7a3e7f648a4b4bcdf96c350cfa4d61"
+ integrity sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==
+ dependencies:
+ tslib "^2.6.2"
+
+"@smithy/util-body-length-node@^4.2.1":
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/@smithy/util-body-length-node/-/util-body-length-node-4.2.1.tgz#79c8a5d18e010cce6c42d5cbaf6c1958523e6fec"
+ integrity sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==
+ dependencies:
+ tslib "^2.6.2"
+
+"@smithy/util-buffer-from@^2.2.0":
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz#6fc88585165ec73f8681d426d96de5d402021e4b"
+ integrity sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==
+ dependencies:
+ "@smithy/is-array-buffer" "^2.2.0"
+ tslib "^2.6.2"
+
+"@smithy/util-buffer-from@^4.2.0":
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/@smithy/util-buffer-from/-/util-buffer-from-4.2.0.tgz#7abd12c4991b546e7cee24d1e8b4bfaa35c68a9d"
+ integrity sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==
+ dependencies:
+ "@smithy/is-array-buffer" "^4.2.0"
+ tslib "^2.6.2"
+
+"@smithy/util-config-provider@^4.2.0":
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz#2e4722937f8feda4dcb09672c59925a4e6286cfc"
+ integrity sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==
+ dependencies:
+ tslib "^2.6.2"
+
+"@smithy/util-defaults-mode-browser@^4.3.28":
+ version "4.3.29"
+ resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.29.tgz#fd4f9563ffd1fb49d092e5b86bacc7796170763e"
+ integrity sha512-nIGy3DNRmOjaYaaKcQDzmWsro9uxlaqUOhZDHQed9MW/GmkBZPtnU70Pu1+GT9IBmUXwRdDuiyaeiy9Xtpn3+Q==
+ dependencies:
+ "@smithy/property-provider" "^4.2.8"
+ "@smithy/smithy-client" "^4.11.2"
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@smithy/util-defaults-mode-node@^4.2.31":
+ version "4.2.32"
+ resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.32.tgz#bc3e9ee1711a9ac3b1c29ea0bef0e785c1da30da"
+ integrity sha512-7dtFff6pu5fsjqrVve0YMhrnzJtccCWDacNKOkiZjJ++fmjGExmmSu341x+WU6Oc1IccL7lDuaUj7SfrHpWc5Q==
+ dependencies:
+ "@smithy/config-resolver" "^4.4.6"
+ "@smithy/credential-provider-imds" "^4.2.8"
+ "@smithy/node-config-provider" "^4.3.8"
+ "@smithy/property-provider" "^4.2.8"
+ "@smithy/smithy-client" "^4.11.2"
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@smithy/util-endpoints@^3.2.8":
+ version "3.2.8"
+ resolved "https://registry.yarnpkg.com/@smithy/util-endpoints/-/util-endpoints-3.2.8.tgz#5650bda2adac989ff2e562606088c5de3dcb1b36"
+ integrity sha512-8JaVTn3pBDkhZgHQ8R0epwWt+BqPSLCjdjXXusK1onwJlRuN69fbvSK66aIKKO7SwVFM6x2J2ox5X8pOaWcUEw==
+ dependencies:
+ "@smithy/node-config-provider" "^4.3.8"
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@smithy/util-hex-encoding@^4.2.0":
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.0.tgz#1c22ea3d1e2c3a81ff81c0a4f9c056a175068a7b"
+ integrity sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==
+ dependencies:
+ tslib "^2.6.2"
+
+"@smithy/util-middleware@^4.2.8":
+ version "4.2.8"
+ resolved "https://registry.yarnpkg.com/@smithy/util-middleware/-/util-middleware-4.2.8.tgz#1da33f29a74c7ebd9e584813cb7e12881600a80a"
+ integrity sha512-PMqfeJxLcNPMDgvPbbLl/2Vpin+luxqTGPpW3NAQVLbRrFRzTa4rNAASYeIGjRV9Ytuhzny39SpyU04EQreF+A==
+ dependencies:
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@smithy/util-retry@^4.2.8":
+ version "4.2.8"
+ resolved "https://registry.yarnpkg.com/@smithy/util-retry/-/util-retry-4.2.8.tgz#23f3f47baf0681233fd0c37b259e60e268c73b11"
+ integrity sha512-CfJqwvoRY0kTGe5AkQokpURNCT1u/MkRzMTASWMPPo2hNSnKtF1D45dQl3DE2LKLr4m+PW9mCeBMJr5mCAVThg==
+ dependencies:
+ "@smithy/service-error-classification" "^4.2.8"
+ "@smithy/types" "^4.12.0"
+ tslib "^2.6.2"
+
+"@smithy/util-stream@^4.5.10", "@smithy/util-stream@^4.5.11":
+ version "4.5.11"
+ resolved "https://registry.yarnpkg.com/@smithy/util-stream/-/util-stream-4.5.11.tgz#69bf0816c2a396b389a48a64455dacdb57893984"
+ integrity sha512-lKmZ0S/3Qj2OF5H1+VzvDLb6kRxGzZHq6f3rAsoSu5cTLGsn3v3VQBA8czkNNXlLjoFEtVu3OQT2jEeOtOE2CA==
+ dependencies:
+ "@smithy/fetch-http-handler" "^5.3.9"
+ "@smithy/node-http-handler" "^4.4.9"
+ "@smithy/types" "^4.12.0"
+ "@smithy/util-base64" "^4.3.0"
+ "@smithy/util-buffer-from" "^4.2.0"
+ "@smithy/util-hex-encoding" "^4.2.0"
+ "@smithy/util-utf8" "^4.2.0"
+ tslib "^2.6.2"
+
+"@smithy/util-uri-escape@^4.2.0":
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz#096a4cec537d108ac24a68a9c60bee73fc7e3a9e"
+ integrity sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==
+ dependencies:
+ tslib "^2.6.2"
+
+"@smithy/util-utf8@^2.0.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-2.3.0.tgz#dd96d7640363259924a214313c3cf16e7dd329c5"
+ integrity sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==
+ dependencies:
+ "@smithy/util-buffer-from" "^2.2.0"
+ tslib "^2.6.2"
+
+"@smithy/util-utf8@^4.2.0":
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-4.2.0.tgz#8b19d1514f621c44a3a68151f3d43e51087fed9d"
+ integrity sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==
+ dependencies:
+ "@smithy/util-buffer-from" "^4.2.0"
+ tslib "^2.6.2"
+
+"@smithy/uuid@^1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@smithy/uuid/-/uuid-1.1.0.tgz#9fd09d3f91375eab94f478858123387df1cda987"
+ integrity sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==
+ dependencies:
+ tslib "^2.6.2"
+
"@szmarczak/http-timer@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421"
@@ -799,13 +1985,13 @@
dependencies:
defer-to-connect "^1.0.1"
-"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7":
- version "7.1.10"
- resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.10.tgz#ca58fc195dd9734e77e57c6f2df565623636ab40"
- integrity sha512-x8OM8XzITIMyiwl5Vmo2B1cR1S1Ipkyv4mdlbJjMa1lmuKvKY9FrBbEANIaMlnWn5Rf7uO+rC/VgYabNkE17Hw==
+"@types/babel__core@^7.1.14":
+ version "7.20.5"
+ resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017"
+ integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==
dependencies:
- "@babel/parser" "^7.1.0"
- "@babel/types" "^7.0.0"
+ "@babel/parser" "^7.20.7"
+ "@babel/types" "^7.20.7"
"@types/babel__generator" "*"
"@types/babel__template" "*"
"@types/babel__traverse" "*"
@@ -825,7 +2011,7 @@
"@babel/parser" "^7.1.0"
"@babel/types" "^7.0.0"
-"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6":
+"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6":
version "7.0.15"
resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.15.tgz#db9e4238931eb69ef8aab0ad6523d4d4caa39d03"
integrity sha512-Pzh9O3sTK8V6I1olsXpCfj2k/ygO2q1X0vhhnDrEQyYLHZesWz+zMZMVcwXLCYf0U36EtmyYaFGPfXlTtDHe3A==
@@ -847,23 +2033,6 @@
dependencies:
"@types/node" "*"
-"@types/cross-spawn@^6.0.2":
- version "6.0.2"
- resolved "https://registry.yarnpkg.com/@types/cross-spawn/-/cross-spawn-6.0.2.tgz#168309de311cd30a2b8ae720de6475c2fbf33ac7"
- integrity sha512-KuwNhp3eza+Rhu8IFI5HUXRP0LIhqH5cAjubUvGXXthh4YYBuP2ntwEX+Cz8GJoZUHlKo247wPWOfA9LYEq4cw==
- dependencies:
- "@types/node" "*"
-
-"@types/debug@^4.1.5":
- version "4.1.5"
- resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.5.tgz#b14efa8852b7768d898906613c23f688713e02cd"
- integrity sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==
-
-"@types/dedent@^0.7.0":
- version "0.7.0"
- resolved "https://registry.yarnpkg.com/@types/dedent/-/dedent-0.7.0.tgz#155f339ca404e6dd90b9ce46a3f78fd69ca9b050"
- integrity sha512-EGlKlgMhnLt/cM4DbUSafFdrkeJoC9Mvnj0PUCU7tFmTjMjNRT957kXCx0wYm3JuEq4o4ZsS5vG+NlkM2DMd2A==
-
"@types/express-serve-static-core@*":
version "4.17.13"
resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.13.tgz#d9af025e925fc8b089be37423b8d1eac781be084"
@@ -883,22 +2052,10 @@
"@types/qs" "*"
"@types/serve-static" "*"
-"@types/find-cache-dir@^3.2.0":
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/@types/find-cache-dir/-/find-cache-dir-3.2.0.tgz#eaaf331699dccf52c47926e4d4f8f3ed8db33f3c"
- integrity sha512-+JeT9qb2Jwzw72WdjU+TSvD5O1QRPWCeRpDJV+guiIq+2hwR0DFGw+nZNbTFjMIVe6Bf4GgAKeB/6Ytx6+MbeQ==
-
-"@types/find-package-json@^1.1.1":
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/@types/find-package-json/-/find-package-json-1.1.1.tgz#c0d296ac74fe3309ed0fe75a9c3edb42a776d30c"
- integrity sha512-XMCocYkg6VUpkbOQMKa3M5cgc3MvU/LJKQwd3VUJrWZbLr2ARUggupsCAF8DxjEEIuSO6HlnH+vl+XV4bgVeEQ==
- dependencies:
- "@types/node" "*"
-
-"@types/graceful-fs@^4.1.2":
- version "4.1.3"
- resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.3.tgz#039af35fe26bec35003e8d86d2ee9c586354348f"
- integrity sha512-AiHRaEB50LQg0pZmm659vNBb9f4SJ0qrAnteuzhSeAUcJKxoYgEnprg/83kppCnc2zvtCKbdZry1a5pVY3lOTQ==
+"@types/graceful-fs@^4.1.3":
+ version "4.1.9"
+ resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4"
+ integrity sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==
dependencies:
"@types/node" "*"
@@ -940,16 +2097,6 @@
dependencies:
"@types/node" "*"
-"@types/lockfile@^1.0.1":
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/@types/lockfile/-/lockfile-1.0.1.tgz#434a3455e89843312f01976e010c60f1bcbd56f7"
- integrity sha512-65WZedEm4AnOsBDdsapJJG42MhROu3n4aSSiu87JXF/pSdlubxZxp3S1yz3kTfkJ2KBPud4CpjoHVAptOm9Zmw==
-
-"@types/md5-file@^4.0.2":
- version "4.0.2"
- resolved "https://registry.yarnpkg.com/@types/md5-file/-/md5-file-4.0.2.tgz#c7241e88f4aa17218c774befb0fc34f33f21fe36"
- integrity sha512-8gacRfEqLrmZ6KofpFfxyjsm/LYepeWUWUJGaf5A9W9J5B2/dRZMdkDqFDL6YDa9IweH12IO76jO7mpsK2B3wg==
-
"@types/mime@*":
version "2.0.3"
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.3.tgz#c893b73721db73699943bfc3653b1deb7faa4a3a"
@@ -960,33 +2107,16 @@
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
-"@types/mkdirp@^1.0.1":
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/@types/mkdirp/-/mkdirp-1.0.1.tgz#0930b948914a78587de35458b86c907b6e98bbf6"
- integrity sha512-HkGSK7CGAXncr8Qn/0VqNtExEE+PHMWb+qlR1faHMao7ng6P3tAaoWWBMdva0gL5h4zprjIO89GJOLXsMcDm1Q==
- dependencies:
- "@types/node" "*"
-
"@types/node@*", "@types/node@>=10", "@types/node@>=6.0.0", "@types/node@>=8.9.0":
version "14.11.8"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.11.8.tgz#fe2012f2355e4ce08bca44aeb3abbb21cf88d33f"
integrity sha512-KPcKqKm5UKDkaYPTuXSx8wEP7vE9GnuaXIZKijwRYcePpZFDVuy2a57LarFKiORbHOuTOOwYzxVxcUzsh2P2Pw==
-"@types/normalize-package-data@^2.4.0":
- version "2.4.0"
- resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e"
- integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==
-
"@types/p-queue@^2.3.2":
version "2.3.2"
resolved "https://registry.yarnpkg.com/@types/p-queue/-/p-queue-2.3.2.tgz#16bc5fece69ef85efaf2bce8b13f3ebe39c5a1c8"
integrity sha512-eKAv5Ql6k78dh3ULCsSBxX6bFNuGjTmof5Q/T6PiECDq0Yf8IIn46jCyp3RJvCi8owaEmm3DZH1PEImjBMd/vQ==
-"@types/prettier@^2.0.0":
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.1.2.tgz#4929992f87a0129f4960a110faeb526210562e7b"
- integrity sha512-IiPhNnenzkqdSdQH3ifk9LoX7oQe61ZlDdDO4+MUv6FyWdPGDPr26gCPVs3oguZEMq//nFZZpwUZcVuNJsG+DQ==
-
"@types/promise.allsettled@^1.0.3":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@types/promise.allsettled/-/promise.allsettled-1.0.3.tgz#6f3166618226a570b98c8250fc78687a912e56d5"
@@ -1007,11 +2137,6 @@
resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d"
integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==
-"@types/semver@^7.3.3":
- version "7.3.4"
- resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.4.tgz#43d7168fec6fa0988bb1a513a697b29296721afb"
- integrity sha512-+nVsLKlcUCeMzD2ufHEYuJ9a2ovstb6Dp52A5VsoKxDXgvE051XgHI/33I1EymwkRGQkwnA0LkhnUzituGs4EQ==
-
"@types/serve-static@*":
version "1.13.5"
resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.5.tgz#3d25d941a18415d3ab092def846e135a08bbcf53"
@@ -1025,33 +2150,38 @@
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff"
integrity sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==
-"@types/tmp@^0.2.0":
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/@types/tmp/-/tmp-0.2.0.tgz#e3f52b4d7397eaa9193592ef3fdd44dc0af4298c"
- integrity sha512-flgpHJjntpBAdJD43ShRosQvNC0ME97DCfGvZEDlAThQmnerRXrLbX6YgzRBQCZTthET9eAWFAMaYP0m0Y4HzQ==
+"@types/webidl-conversions@*":
+ version "7.0.3"
+ resolved "https://registry.yarnpkg.com/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz#1306dbfa53768bcbcfc95a1c8cde367975581859"
+ integrity sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==
-"@types/uuid@^8.0.0":
- version "8.3.0"
- resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.0.tgz#215c231dff736d5ba92410e6d602050cce7e273f"
- integrity sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==
+"@types/whatwg-url@^11.0.2":
+ version "11.0.5"
+ resolved "https://registry.yarnpkg.com/@types/whatwg-url/-/whatwg-url-11.0.5.tgz#aaa2546e60f0c99209ca13360c32c78caf2c409f"
+ integrity sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==
+ dependencies:
+ "@types/webidl-conversions" "*"
+
+"@types/whatwg-url@^8.2.1":
+ version "8.2.2"
+ resolved "https://registry.yarnpkg.com/@types/whatwg-url/-/whatwg-url-8.2.2.tgz#749d5b3873e845897ada99be4448041d4cc39e63"
+ integrity sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==
+ dependencies:
+ "@types/node" "*"
+ "@types/webidl-conversions" "*"
"@types/yargs-parser@*":
version "15.0.0"
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d"
integrity sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==
-"@types/yargs@^15.0.0":
- version "15.0.8"
- resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.8.tgz#7644904cad7427eb704331ea9bf1ee5499b82e23"
- integrity sha512-b0BYzFUzBpOhPjpl1wtAHU994jBeKF4TKVlT7ssFv44T617XNcPdRoG4AzHLVshLzlrF7i3lTelH7UbuNYV58Q==
+"@types/yargs@^17.0.8":
+ version "17.0.35"
+ resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.35.tgz#07013e46aa4d7d7d50a49e15604c1c5340d4eb24"
+ integrity sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==
dependencies:
"@types/yargs-parser" "*"
-abab@^2.0.3:
- version "2.0.5"
- resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a"
- integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==
-
abbrev@1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
@@ -1064,33 +2194,20 @@ abort-controller@^3.0.0:
dependencies:
event-target-shim "^5.0.0"
-accepts@~1.3.7:
- version "1.3.7"
- resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd"
- integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==
- dependencies:
- mime-types "~2.1.24"
- negotiator "0.6.2"
-
-acorn-globals@^6.0.0:
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45"
- integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==
+accepts@~1.3.8:
+ version "1.3.8"
+ resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
+ integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==
dependencies:
- acorn "^7.1.1"
- acorn-walk "^7.1.1"
+ mime-types "~2.1.34"
+ negotiator "0.6.3"
acorn-jsx@^5.2.0:
version "5.3.1"
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b"
integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==
-acorn-walk@^7.1.1:
- version "7.2.0"
- resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc"
- integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==
-
-acorn@^7.1.1, acorn@^7.4.0:
+acorn@^7.4.0:
version "7.4.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
@@ -1102,7 +2219,7 @@ agent-base@6:
dependencies:
debug "4"
-ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4:
+ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.4:
version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
@@ -1141,6 +2258,11 @@ ansi-regex@^5.0.0:
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75"
integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==
+ansi-regex@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
+ integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
+
ansi-styles@^3.2.0, ansi-styles@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
@@ -1155,13 +2277,10 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
dependencies:
color-convert "^2.0.1"
-anymatch@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb"
- integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==
- dependencies:
- micromatch "^3.1.4"
- normalize-path "^2.1.1"
+ansi-styles@^5.0.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b"
+ integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
anymatch@^3.0.3, anymatch@~3.1.1:
version "3.1.1"
@@ -1186,21 +2305,6 @@ aria-query@^4.2.2:
"@babel/runtime" "^7.10.2"
"@babel/runtime-corejs3" "^7.10.2"
-arr-diff@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
- integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=
-
-arr-flatten@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1"
- integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==
-
-arr-union@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4"
- integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=
-
array-differ@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-3.0.0.tgz#3cbb3d0f316810eafcc47624734237d6aee4ae6b"
@@ -1225,11 +2329,6 @@ array-union@^2.1.0:
resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
-array-unique@^0.3.2:
- version "0.3.2"
- resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
- integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
-
array.prototype.flat@^1.2.3:
version "1.2.3"
resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz#0de82b426b0318dbfdb940089e38b043d37f6c7b"
@@ -1262,28 +2361,11 @@ arrify@^2.0.0, arrify@^2.0.1:
resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa"
integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==
-asn1@~0.2.3:
- version "0.2.4"
- resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
- integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==
- dependencies:
- safer-buffer "~2.1.0"
-
assert-env@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/assert-env/-/assert-env-0.6.0.tgz#93f7657a27a17b288b19f56e72122d15bbc1e384"
integrity sha1-k/dleieheyiLGfVuchItFbvB44Q=
-assert-plus@1.0.0, assert-plus@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
- integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
-
-assign-symbols@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
- integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=
-
ast-types-flow@^0.0.7:
version "0.0.7"
resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad"
@@ -1294,6 +2376,13 @@ astral-regex@^1.0.0:
resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9"
integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==
+async-mutex@^0.3.2:
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.3.2.tgz#1485eda5bda1b0ec7c8df1ac2e815757ad1831df"
+ integrity sha512-HuTK7E7MT7jZEh1P9GtRW9+aTWiDWWi9InbZ5hjxrnRa39KS4BW04+xLBhYNS2aXhHUIKZSw3gj4Pn1pj+qGAA==
+ dependencies:
+ tslib "^2.3.1"
+
async@^3.2.2:
version "3.2.2"
resolved "https://registry.yarnpkg.com/async/-/async-3.2.2.tgz#2eb7671034bb2194d45d30e31e24ec7e7f9670cd"
@@ -1304,21 +2393,6 @@ asynckit@^0.4.0:
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
-atob@^2.1.2:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
- integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
-
-aws-sign2@~0.7.0:
- version "0.7.0"
- resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
- integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=
-
-aws4@^1.8.0:
- version "1.10.1"
- resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.1.tgz#e1e82e4f3e999e2cfd61b161280d16a111f86428"
- integrity sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==
-
axe-core@^3.5.4:
version "3.5.5"
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-3.5.5.tgz#84315073b53fa3c0c51676c588d59da09a192227"
@@ -1336,65 +2410,68 @@ axobject-query@^2.1.2:
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be"
integrity sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==
-babel-jest@^26.5.2:
- version "26.5.2"
- resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.5.2.tgz#164f367a35946c6cf54eaccde8762dec50422250"
- integrity sha512-U3KvymF3SczA3vOL/cgiUFOznfMET+XDIXiWnoJV45siAp2pLMG8i2+/MGZlAC3f/F6Q40LR4M4qDrWZ9wkK8A==
+babel-jest@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5"
+ integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==
dependencies:
- "@jest/transform" "^26.5.2"
- "@jest/types" "^26.5.2"
- "@types/babel__core" "^7.1.7"
- babel-plugin-istanbul "^6.0.0"
- babel-preset-jest "^26.5.0"
+ "@jest/transform" "^29.7.0"
+ "@types/babel__core" "^7.1.14"
+ babel-plugin-istanbul "^6.1.1"
+ babel-preset-jest "^29.6.3"
chalk "^4.0.0"
- graceful-fs "^4.2.4"
+ graceful-fs "^4.2.9"
slash "^3.0.0"
-babel-plugin-istanbul@^6.0.0:
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz#e159ccdc9af95e0b570c75b4573b7c34d671d765"
- integrity sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==
+babel-plugin-istanbul@^6.1.1:
+ version "6.1.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73"
+ integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
"@istanbuljs/load-nyc-config" "^1.0.0"
"@istanbuljs/schema" "^0.1.2"
- istanbul-lib-instrument "^4.0.0"
+ istanbul-lib-instrument "^5.0.4"
test-exclude "^6.0.0"
-babel-plugin-jest-hoist@^26.5.0:
- version "26.5.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.5.0.tgz#3916b3a28129c29528de91e5784a44680db46385"
- integrity sha512-ck17uZFD3CDfuwCLATWZxkkuGGFhMij8quP8CNhwj8ek1mqFgbFzRJ30xwC04LLscj/aKsVFfRST+b5PT7rSuw==
+babel-plugin-jest-hoist@^29.6.3:
+ version "29.6.3"
+ resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626"
+ integrity sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==
dependencies:
"@babel/template" "^7.3.3"
"@babel/types" "^7.3.3"
- "@types/babel__core" "^7.0.0"
+ "@types/babel__core" "^7.1.14"
"@types/babel__traverse" "^7.0.6"
-babel-preset-current-node-syntax@^0.1.3:
- version "0.1.4"
- resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.4.tgz#826f1f8e7245ad534714ba001f84f7e906c3b615"
- integrity sha512-5/INNCYhUGqw7VbVjT/hb3ucjgkVHKXY7lX3ZjlN4gm565VyFmJUrJ/h+h16ECVB38R/9SF6aACydpKMLZ/c9w==
+babel-preset-current-node-syntax@^1.0.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz#20730d6cdc7dda5d89401cab10ac6a32067acde6"
+ integrity sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==
dependencies:
"@babel/plugin-syntax-async-generators" "^7.8.4"
"@babel/plugin-syntax-bigint" "^7.8.3"
- "@babel/plugin-syntax-class-properties" "^7.8.3"
- "@babel/plugin-syntax-import-meta" "^7.8.3"
+ "@babel/plugin-syntax-class-properties" "^7.12.13"
+ "@babel/plugin-syntax-class-static-block" "^7.14.5"
+ "@babel/plugin-syntax-import-attributes" "^7.24.7"
+ "@babel/plugin-syntax-import-meta" "^7.10.4"
"@babel/plugin-syntax-json-strings" "^7.8.3"
- "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3"
+ "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4"
"@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3"
- "@babel/plugin-syntax-numeric-separator" "^7.8.3"
+ "@babel/plugin-syntax-numeric-separator" "^7.10.4"
"@babel/plugin-syntax-object-rest-spread" "^7.8.3"
"@babel/plugin-syntax-optional-catch-binding" "^7.8.3"
"@babel/plugin-syntax-optional-chaining" "^7.8.3"
+ "@babel/plugin-syntax-private-property-in-object" "^7.14.5"
+ "@babel/plugin-syntax-top-level-await" "^7.14.5"
-babel-preset-jest@^26.5.0:
- version "26.5.0"
- resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-26.5.0.tgz#f1b166045cd21437d1188d29f7fba470d5bdb0e7"
- integrity sha512-F2vTluljhqkiGSJGBg/jOruA8vIIIL11YrxRcO7nviNTMbbofPSHwnm8mgP7d/wS7wRSexRoI6X1A6T74d4LQA==
+babel-preset-jest@^29.6.3:
+ version "29.6.3"
+ resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c"
+ integrity sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==
dependencies:
- babel-plugin-jest-hoist "^26.5.0"
- babel-preset-current-node-syntax "^0.1.3"
+ babel-plugin-jest-hoist "^29.6.3"
+ babel-preset-current-node-syntax "^1.0.0"
balanced-match@^1.0.0:
version "1.0.0"
@@ -1406,18 +2483,15 @@ base64-js@^1.0.2, base64-js@^1.3.0:
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==
-base@^0.11.1:
- version "0.11.2"
- resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
- integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==
- dependencies:
- cache-base "^1.0.1"
- class-utils "^0.3.5"
- component-emitter "^1.2.1"
- define-property "^1.0.0"
- isobject "^3.0.1"
- mixin-deep "^1.2.0"
- pascalcase "^0.1.1"
+base64-js@^1.3.1:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
+ integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
+
+baseline-browser-mapping@^2.9.0:
+ version "2.9.19"
+ resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz#3e508c43c46d961eb4d7d2e5b8d1dd0f9ee4f488"
+ integrity sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==
basic-auth@~2.0.1:
version "2.0.1"
@@ -1426,13 +2500,6 @@ basic-auth@~2.0.1:
dependencies:
safe-buffer "5.1.2"
-bcrypt-pbkdf@^1.0.0:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
- integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=
- dependencies:
- tweetnacl "^0.14.3"
-
bignumber.js@^9.0.0:
version "9.0.1"
resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5"
@@ -1443,14 +2510,6 @@ binary-extensions@^2.0.0:
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9"
integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==
-bl@^2.2.1:
- version "2.2.1"
- resolved "https://registry.yarnpkg.com/bl/-/bl-2.2.1.tgz#8c11a7b730655c5d56898cdc871224f40fd901d5"
- integrity sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==
- dependencies:
- readable-stream "^2.3.5"
- safe-buffer "^5.1.1"
-
bl@^4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/bl/-/bl-4.0.3.tgz#12d6287adc29080e22a705e5764b2a9522cdc489"
@@ -1460,32 +2519,34 @@ bl@^4.0.3:
inherits "^2.0.4"
readable-stream "^3.4.0"
-bluebird@3.5.1:
- version "3.5.1"
- resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9"
- integrity sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==
-
-body-parser@1.19.0, body-parser@^1.19.0:
- version "1.19.0"
- resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a"
- integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==
+body-parser@1.20.3:
+ version "1.20.3"
+ resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6"
+ integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==
dependencies:
- bytes "3.1.0"
- content-type "~1.0.4"
+ bytes "3.1.2"
+ content-type "~1.0.5"
debug "2.6.9"
- depd "~1.1.2"
- http-errors "1.7.2"
+ depd "2.0.0"
+ destroy "1.2.0"
+ http-errors "2.0.0"
iconv-lite "0.4.24"
- on-finished "~2.3.0"
- qs "6.7.0"
- raw-body "2.4.0"
- type-is "~1.6.17"
+ on-finished "2.4.1"
+ qs "6.13.0"
+ raw-body "2.5.2"
+ type-is "~1.6.18"
+ unpipe "1.0.0"
bowser@2.9.0:
version "2.9.0"
resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.9.0.tgz#3bed854233b419b9a7422d9ee3e85504373821c9"
integrity sha512-2ld76tuLBNFekRgmJfT2+3j5MIrP6bFict8WAIT3beq+srz1gcKNAdNKMqHqauQt63NmAa88HfP1/Ypa9Er3HA==
+bowser@^2.11.0:
+ version "2.13.1"
+ resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.13.1.tgz#5a4c652de1d002f847dd011819f5fc729f308a7e"
+ integrity sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw==
+
boxen@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64"
@@ -1501,41 +2562,27 @@ boxen@^4.2.0:
widest-line "^3.1.0"
brace-expansion@^1.1.7:
- version "1.1.11"
- resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
- integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+ version "1.1.12"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843"
+ integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==
dependencies:
balanced-match "^1.0.0"
concat-map "0.0.1"
-braces@^2.3.1:
- version "2.3.2"
- resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729"
- integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==
- dependencies:
- arr-flatten "^1.1.0"
- array-unique "^0.3.2"
- extend-shallow "^2.0.1"
- fill-range "^4.0.0"
- isobject "^3.0.1"
- repeat-element "^1.1.2"
- snapdragon "^0.8.1"
- snapdragon-node "^2.0.1"
- split-string "^3.0.2"
- to-regex "^3.0.1"
-
-braces@^3.0.1, braces@~3.0.2:
+braces@^3.0.3:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
+ integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
+ dependencies:
+ fill-range "^7.1.1"
+
+braces@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
dependencies:
fill-range "^7.0.1"
-browser-process-hrtime@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626"
- integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==
-
browserslist@^4.16.6:
version "4.16.8"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.8.tgz#cb868b0b554f137ba6e33de0ecff2eda403c4fb0"
@@ -1547,6 +2594,17 @@ browserslist@^4.16.6:
escalade "^3.1.1"
node-releases "^1.1.75"
+browserslist@^4.24.0:
+ version "4.28.1"
+ resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.1.tgz#7f534594628c53c63101079e27e40de490456a95"
+ integrity sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==
+ dependencies:
+ baseline-browser-mapping "^2.9.0"
+ caniuse-lite "^1.0.30001759"
+ electron-to-chromium "^1.5.263"
+ node-releases "^2.0.27"
+ update-browserslist-db "^1.2.0"
+
bser@2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05"
@@ -1554,10 +2612,17 @@ bser@2.1.1:
dependencies:
node-int64 "^0.4.0"
-bson@^1.1.4:
- version "1.1.5"
- resolved "https://registry.yarnpkg.com/bson/-/bson-1.1.5.tgz#2aaae98fcdf6750c0848b0cba1ddec3c73060a34"
- integrity sha512-kDuEzldR21lHciPQAIulLs1LZlCXdLziXI6Mb/TDkwXhb//UORJNPXgcRs2CuO4H0DcMkpfT3/ySsP3unoZjBg==
+bson@^4.7.2:
+ version "4.7.2"
+ resolved "https://registry.yarnpkg.com/bson/-/bson-4.7.2.tgz#320f4ad0eaf5312dd9b45dc369cc48945e2a5f2e"
+ integrity sha512-Ry9wCtIZ5kGqkJoi6aD8KjxFZEx78guTQDnpXWiNthsxzrxAK/i8E6pCHAIZTbaEFWcOCvbecMukfK7XUvyLpQ==
+ dependencies:
+ buffer "^5.6.0"
+
+bson@^6.10.4:
+ version "6.10.4"
+ resolved "https://registry.yarnpkg.com/bson/-/bson-6.10.4.tgz#d530733bb5bb16fb25c162e01a3344fab332fd2b"
+ integrity sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==
buffer-crc32@~0.2.3:
version "0.2.13"
@@ -1582,25 +2647,23 @@ buffer@^5.5.0:
base64-js "^1.0.2"
ieee754 "^1.1.4"
+buffer@^5.6.0:
+ version "5.7.1"
+ resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
+ integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
+ dependencies:
+ base64-js "^1.3.1"
+ ieee754 "^1.1.13"
+
bytes@3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==
-cache-base@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2"
- integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==
- dependencies:
- collection-visit "^1.0.0"
- component-emitter "^1.2.1"
- get-value "^2.0.6"
- has-value "^1.0.0"
- isobject "^3.0.1"
- set-value "^2.0.0"
- to-object-path "^0.3.0"
- union-value "^1.0.0"
- unset-value "^1.0.0"
+bytes@3.1.2:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
+ integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
cacheable-request@^6.0.0:
version "6.1.0"
@@ -1615,6 +2678,17 @@ cacheable-request@^6.0.0:
normalize-url "^4.1.0"
responselike "^1.0.2"
+call-bind@^1.0.7:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9"
+ integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==
+ dependencies:
+ es-define-property "^1.0.0"
+ es-errors "^1.3.0"
+ function-bind "^1.1.2"
+ get-intrinsic "^1.2.4"
+ set-function-length "^1.2.1"
+
callsites@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
@@ -1625,10 +2699,10 @@ camelcase@^5.0.0, camelcase@^5.3.1:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
-camelcase@^6.0.0:
- version "6.1.0"
- resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.1.0.tgz#27dc176173725fb0adf8a48b647f4d7871944d78"
- integrity sha512-WCMml9ivU60+8rEJgELlFp1gxFcEGxwYleE3bziHEDeqsqAWGHdimB7beBFGjLzVNgPGyDsfgXLQEYMpmIFnVQ==
+camelcase@^6.2.0, camelcase@^6.3.0:
+ version "6.3.0"
+ resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
+ integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
camelize@1.0.0:
version "1.0.0"
@@ -1640,17 +2714,10 @@ caniuse-lite@^1.0.30001251:
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001456.tgz"
integrity sha512-XFHJY5dUgmpMV25UqaD4kVq2LsiaU5rS8fb0f17pCoXQiQslzmFgnfOxfvo1bTpTqf7dwG/N/05CnLCnOEKmzA==
-capture-exit@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4"
- integrity sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==
- dependencies:
- rsvp "^4.8.4"
-
-caseless@~0.12.0:
- version "0.12.0"
- resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
- integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
+caniuse-lite@^1.0.30001759:
+ version "1.0.30001767"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001767.tgz#0279c498e862efb067938bba0a0aabafe8d0b730"
+ integrity sha512-34+zUAMhSH+r+9eKmYG+k2Rpt8XttfE4yXAjoZvkAPs15xcYQhyBYdalJ65BzivAvGRMViEjy6oKr/S91loekQ==
chalk@^2.0.0, chalk@^2.4.2:
version "2.4.2"
@@ -1702,15 +2769,15 @@ ci-info@^2.0.0:
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46"
integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==
-class-utils@^0.3.5:
- version "0.3.6"
- resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463"
- integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==
- dependencies:
- arr-union "^3.1.0"
- define-property "^0.2.5"
- isobject "^3.0.0"
- static-extend "^0.1.1"
+ci-info@^3.2.0:
+ version "3.9.0"
+ resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4"
+ integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==
+
+cjs-module-lexer@^1.0.0:
+ version "1.4.3"
+ resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz#0f79731eb8cfe1ec72acd4066efac9d61991b00d"
+ integrity sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==
cli-boxes@^2.2.0:
version "2.2.1"
@@ -1726,14 +2793,14 @@ cliui@^5.0.0:
strip-ansi "^5.2.0"
wrap-ansi "^5.1.0"
-cliui@^6.0.0:
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1"
- integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==
+cliui@^8.0.1:
+ version "8.0.1"
+ resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa"
+ integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==
dependencies:
string-width "^4.2.0"
- strip-ansi "^6.0.0"
- wrap-ansi "^6.2.0"
+ strip-ansi "^6.0.1"
+ wrap-ansi "^7.0.0"
clone-response@^1.0.2:
version "1.0.2"
@@ -1752,14 +2819,6 @@ collect-v8-coverage@^1.0.0:
resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59"
integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==
-collection-visit@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
- integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=
- dependencies:
- map-visit "^1.0.0"
- object-visit "^1.0.0"
-
color-convert@^1.9.0:
version "1.9.3"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
@@ -1789,7 +2848,7 @@ colorette@^1.3.0:
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.3.0.tgz#ff45d2f0edb244069d3b772adeb04fed38d0a0af"
integrity sha512-ecORCqbSFP7Wm8Y6lyqMJjexBQqXSF7SSeaTyGGphogUjBlFP9m9o08wy86HL2uB7fMTxtOUzLMk7ogKcxMg1w==
-combined-stream@^1.0.6, combined-stream@~1.0.6:
+combined-stream@^1.0.6:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
@@ -1801,7 +2860,7 @@ commondir@^1.0.1:
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=
-component-emitter@^1.2.0, component-emitter@^1.2.1:
+component-emitter@^1.2.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
@@ -1848,30 +2907,35 @@ contains-path@^0.1.0:
resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a"
integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=
-content-disposition@0.5.3:
- version "0.5.3"
- resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd"
- integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==
+content-disposition@0.5.4:
+ version "0.5.4"
+ resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"
+ integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==
dependencies:
- safe-buffer "5.1.2"
+ safe-buffer "5.2.1"
content-security-policy-builder@2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/content-security-policy-builder/-/content-security-policy-builder-2.1.0.tgz#0a2364d769a3d7014eec79ff7699804deb8cfcbb"
integrity sha512-/MtLWhJVvJNkA9dVLAp6fg9LxD2gfI6R2Fi1hPmfjYXSahJJzcfvoeDOxSyp4NvxMuwWv3WMssE9o31DoULHrQ==
-content-type@~1.0.4:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
- integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
+content-type@~1.0.4, content-type@~1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918"
+ integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
-convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0:
+convert-source-map@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442"
integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==
dependencies:
safe-buffer "~5.1.1"
+convert-source-map@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a"
+ integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==
+
cookie-parser@^1.4.5:
version "1.4.5"
resolved "https://registry.yarnpkg.com/cookie-parser/-/cookie-parser-1.4.5.tgz#3e572d4b7c0c80f9c61daf604e4336831b5d1d49"
@@ -1890,22 +2954,22 @@ cookie@0.4.0:
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba"
integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==
+cookie@0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051"
+ integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==
+
cookiejar@^2.1.0:
version "2.1.4"
resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.4.tgz#ee669c1fea2cf42dc31585469d193fef0d65771b"
integrity sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==
-copy-descriptor@^0.1.0:
- version "0.1.1"
- resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
- integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
-
core-js-pure@^3.0.0:
version "3.6.5"
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813"
integrity sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==
-core-util-is@1.0.2, core-util-is@~1.0.0:
+core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
@@ -1918,18 +2982,20 @@ cors@^2.8.5:
object-assign "^4"
vary "^1"
-cross-spawn@^6.0.0:
- version "6.0.5"
- resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
- integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
+create-jest@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320"
+ integrity sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==
dependencies:
- nice-try "^1.0.4"
- path-key "^2.0.1"
- semver "^5.5.0"
- shebang-command "^1.2.0"
- which "^1.2.9"
+ "@jest/types" "^29.6.3"
+ chalk "^4.0.0"
+ exit "^0.1.2"
+ graceful-fs "^4.2.9"
+ jest-config "^29.7.0"
+ jest-util "^29.7.0"
+ prompts "^2.0.1"
-cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
+cross-spawn@^7.0.0, cross-spawn@^7.0.2:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
@@ -1938,86 +3004,62 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
shebang-command "^2.0.0"
which "^2.0.1"
+cross-spawn@^7.0.3:
+ version "7.0.6"
+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
+ integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
+ dependencies:
+ path-key "^3.1.0"
+ shebang-command "^2.0.0"
+ which "^2.0.1"
+
crypto-random-string@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5"
integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==
-cssom@^0.4.4:
- version "0.4.4"
- resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10"
- integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==
-
-cssom@~0.3.6:
- version "0.3.8"
- resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a"
- integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==
-
-cssstyle@^2.2.0:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852"
- integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==
- dependencies:
- cssom "~0.3.6"
-
damerau-levenshtein@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz#143c1641cb3d85c60c32329e26899adea8701791"
integrity sha512-JVrozIeElnj3QzfUIt8tB8YMluBJom4Vw9qTPpjGYQ9fYlB3D/rb6OordUxf3xeFB35LKWs0xqcO5U6ySvBtug==
-dashdash@^1.12.0:
- version "1.14.1"
- resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
- integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=
- dependencies:
- assert-plus "^1.0.0"
-
dasherize@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/dasherize/-/dasherize-2.0.0.tgz#6d809c9cd0cf7bb8952d80fc84fa13d47ddb1308"
integrity sha1-bYCcnNDPe7iVLYD8hPoT1H3bEwg=
-data-urls@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b"
- integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==
- dependencies:
- abab "^2.0.3"
- whatwg-mimetype "^2.3.0"
- whatwg-url "^8.0.0"
-
date-fns@^2.0.1:
version "2.16.1"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.16.1.tgz#05775792c3f3331da812af253e1a935851d3834b"
integrity sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ==
-debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9:
+debug@2.6.9, debug@^2.2.0, debug@^2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
dependencies:
ms "2.0.0"
-debug@3.1.0, debug@=3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
- integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
+debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1:
+ version "4.3.1"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
+ integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
dependencies:
- ms "2.0.0"
+ ms "2.1.2"
-debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1"
- integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==
+debug@4.x, debug@^4.3.4:
+ version "4.4.3"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a"
+ integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==
dependencies:
- ms "2.1.2"
+ ms "^2.1.3"
-debug@4.1.1:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
- integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
+debug@=3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
+ integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
dependencies:
- ms "^2.1.1"
+ ms "2.0.0"
debug@^3.1.0, debug@^3.2.6:
version "3.2.6"
@@ -2031,16 +3073,6 @@ decamelize@^1.2.0:
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
-decimal.js@^10.2.0:
- version "10.2.1"
- resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.1.tgz#238ae7b0f0c793d3e3cea410108b35a2c01426a3"
- integrity sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw==
-
-decode-uri-component@^0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
- integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
-
decompress-response@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3"
@@ -2048,12 +3080,17 @@ decompress-response@^3.3.0:
dependencies:
mimic-response "^1.0.0"
+dedent@^1.0.0:
+ version "1.7.1"
+ resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.7.1.tgz#364661eea3d73f3faba7089214420ec2f8f13e15"
+ integrity sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==
+
deep-extend@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
-deep-is@^0.1.3, deep-is@~0.1.3:
+deep-is@^0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
@@ -2068,6 +3105,15 @@ defer-to-connect@^1.0.1:
resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591"
integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==
+define-data-property@^1.1.4:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e"
+ integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==
+ dependencies:
+ es-define-property "^1.0.0"
+ es-errors "^1.3.0"
+ gopd "^1.0.1"
+
define-properties@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
@@ -2075,38 +3121,11 @@ define-properties@^1.1.3:
dependencies:
object-keys "^1.0.12"
-define-property@^0.2.5:
- version "0.2.5"
- resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116"
- integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=
- dependencies:
- is-descriptor "^0.1.0"
-
-define-property@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6"
- integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY=
- dependencies:
- is-descriptor "^1.0.0"
-
-define-property@^2.0.2:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d"
- integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==
- dependencies:
- is-descriptor "^1.0.2"
- isobject "^3.0.1"
-
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
-denque@^1.4.1:
- version "1.4.1"
- resolved "https://registry.yarnpkg.com/denque/-/denque-1.4.1.tgz#6744ff7641c148c3f8a69c307e51235c1f4a37cf"
- integrity sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==
-
depd@2.0.0, depd@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
@@ -2117,20 +3136,20 @@ depd@~1.1.2:
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
-destroy@~1.0.4:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
- integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
+destroy@1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015"
+ integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==
detect-newline@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651"
integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==
-diff-sequences@^26.5.0:
- version "26.5.0"
- resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.5.0.tgz#ef766cf09d43ed40406611f11c6d8d9dd8b2fefd"
- integrity sha512-ZXx86srb/iYy6jG71k++wBN9P9J05UNQ5hQHQd9MtMPvcqXPx/vKU69jfHV637D00Q2gSgPk2D+jSx3l1lDW/Q==
+diff-sequences@^29.6.3:
+ version "29.6.3"
+ resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921"
+ integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==
doctrine@1.5.0:
version "1.5.0"
@@ -2154,13 +3173,6 @@ doctrine@^3.0.0:
dependencies:
esutils "^2.0.2"
-domexception@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304"
- integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==
- dependencies:
- webidl-conversions "^5.0.0"
-
dont-sniff-mimetype@1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/dont-sniff-mimetype/-/dont-sniff-mimetype-1.1.0.tgz#c7d0427f8bcb095762751252af59d148b0a623b2"
@@ -2188,14 +3200,6 @@ duplexer3@^0.1.4:
resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=
-ecc-jsbn@~0.1.1:
- version "0.1.2"
- resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
- integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=
- dependencies:
- jsbn "~0.1.0"
- safer-buffer "^2.1.0"
-
ecdsa-sig-formatter@1.0.11, ecdsa-sig-formatter@^1.0.11:
version "1.0.11"
resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf"
@@ -2213,10 +3217,15 @@ electron-to-chromium@^1.3.811:
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.813.tgz#751a007d71c00faed8b5e9edaf3634c14b9c5a1f"
integrity sha512-YcSRImHt6JZZ2sSuQ4Bzajtk98igQ0iKkksqlzZLzbh4p0OIyJRSvUbsgqfcR8txdfsoYCc4ym306t4p2kP/aw==
-emittery@^0.7.1:
- version "0.7.1"
- resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.1.tgz#c02375a927a40948c0345cc903072597f5270451"
- integrity sha512-d34LN4L6h18Bzz9xpoku2nPwKxCPlPMr3EEKTkoEBi+1/+b0lcRkRJ1UVyyZaKNeqGR3swcGl6s390DNO4YVgQ==
+electron-to-chromium@^1.5.263:
+ version "1.5.283"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.283.tgz#51d492c37c2d845a0dccb113fe594880c8616de8"
+ integrity sha512-3vifjt1HgrGW/h76UEeny+adYApveS9dH2h3p57JYzBSXJIKUJAvtmIytDKjcSCt9xHfrNCFJ7gts6vkhuq++w==
+
+emittery@^0.13.1:
+ version "0.13.1"
+ resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad"
+ integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==
emoji-regex@^7.0.1:
version "7.0.3"
@@ -2238,6 +3247,11 @@ encodeurl@~1.0.2:
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
+encodeurl@~2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58"
+ integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==
+
end-of-stream@^1.1.0, end-of-stream@^1.4.1:
version "1.4.4"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
@@ -2299,6 +3313,18 @@ es-array-method-boxes-properly@^1.0.0:
resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e"
integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==
+es-define-property@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845"
+ integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==
+ dependencies:
+ get-intrinsic "^1.2.4"
+
+es-errors@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
+ integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
+
es-get-iterator@^1.0.2:
version "1.1.0"
resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.0.tgz#bb98ad9d6d63b31aacdc8f89d5d0ee57bcb5b4c8"
@@ -2326,6 +3352,11 @@ escalade@^3.1.1:
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
+escalade@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5"
+ integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==
+
escape-goat@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675"
@@ -2346,18 +3377,6 @@ escape-string-regexp@^2.0.0:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344"
integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==
-escodegen@^1.14.1:
- version "1.14.3"
- resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503"
- integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==
- dependencies:
- esprima "^4.0.1"
- estraverse "^4.2.0"
- esutils "^2.0.2"
- optionator "^0.8.1"
- optionalDependencies:
- source-map "~0.6.1"
-
eslint-config-airbnb-base@^14.2.0:
version "14.2.0"
resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.0.tgz#fe89c24b3f9dc8008c9c0d0d88c28f95ed65e9c4"
@@ -2539,7 +3558,7 @@ espree@^7.3.0:
acorn-jsx "^5.2.0"
eslint-visitor-keys "^1.3.0"
-esprima@^4.0.0, esprima@^4.0.1:
+esprima@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
@@ -2558,7 +3577,7 @@ esrecurse@^4.3.0:
dependencies:
estraverse "^5.2.0"
-estraverse@^4.1.1, estraverse@^4.2.0:
+estraverse@^4.1.1:
version "4.3.0"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
@@ -2588,24 +3607,6 @@ eventemitter3@^3.1.0:
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7"
integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==
-exec-sh@^0.3.2:
- version "0.3.4"
- resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.4.tgz#3a018ceb526cc6f6df2bb504b2bfe8e3a4934ec5"
- integrity sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==
-
-execa@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8"
- integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==
- dependencies:
- cross-spawn "^6.0.0"
- get-stream "^4.0.0"
- is-stream "^1.1.0"
- npm-run-path "^2.0.0"
- p-finally "^1.0.0"
- signal-exit "^3.0.0"
- strip-eof "^1.0.0"
-
execa@^4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/execa/-/execa-4.0.3.tgz#0a34dabbad6d66100bd6f2c576c8669403f317f2"
@@ -2621,35 +3622,36 @@ execa@^4.0.0:
signal-exit "^3.0.2"
strip-final-newline "^2.0.0"
+execa@^5.0.0:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
+ integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==
+ dependencies:
+ cross-spawn "^7.0.3"
+ get-stream "^6.0.0"
+ human-signals "^2.1.0"
+ is-stream "^2.0.0"
+ merge-stream "^2.0.0"
+ npm-run-path "^4.0.1"
+ onetime "^5.1.2"
+ signal-exit "^3.0.3"
+ strip-final-newline "^2.0.0"
+
exit@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c"
integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=
-expand-brackets@^2.1.4:
- version "2.1.4"
- resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622"
- integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI=
- dependencies:
- debug "^2.3.3"
- define-property "^0.2.5"
- extend-shallow "^2.0.1"
- posix-character-classes "^0.1.0"
- regex-not "^1.0.0"
- snapdragon "^0.8.1"
- to-regex "^3.0.1"
-
-expect@^26.5.3:
- version "26.5.3"
- resolved "https://registry.yarnpkg.com/expect/-/expect-26.5.3.tgz#89d9795036f7358b0a9a5243238eb8086482d741"
- integrity sha512-kkpOhGRWGOr+TEFUnYAjfGvv35bfP+OlPtqPIJpOCR9DVtv8QV+p8zG0Edqafh80fsjeE+7RBcVUq1xApnYglw==
- dependencies:
- "@jest/types" "^26.5.2"
- ansi-styles "^4.0.0"
- jest-get-type "^26.3.0"
- jest-matcher-utils "^26.5.2"
- jest-message-util "^26.5.2"
- jest-regex-util "^26.0.0"
+expect@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc"
+ integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==
+ dependencies:
+ "@jest/expect-utils" "^29.7.0"
+ jest-get-type "^29.6.3"
+ jest-matcher-utils "^29.7.0"
+ jest-message-util "^29.7.0"
+ jest-util "^29.7.0"
express-validator@^6.6.1:
version "6.6.1"
@@ -2659,97 +3661,59 @@ express-validator@^6.6.1:
lodash "^4.17.19"
validator "^13.1.1"
-express@^4.16.4, express@^4.17.1:
- version "4.17.1"
- resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134"
- integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==
+express@^4.16.4, express@^4.20.0:
+ version "4.20.0"
+ resolved "https://registry.yarnpkg.com/express/-/express-4.20.0.tgz#f1d08e591fcec770c07be4767af8eb9bcfd67c48"
+ integrity sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw==
dependencies:
- accepts "~1.3.7"
+ accepts "~1.3.8"
array-flatten "1.1.1"
- body-parser "1.19.0"
- content-disposition "0.5.3"
+ body-parser "1.20.3"
+ content-disposition "0.5.4"
content-type "~1.0.4"
- cookie "0.4.0"
+ cookie "0.6.0"
cookie-signature "1.0.6"
debug "2.6.9"
- depd "~1.1.2"
- encodeurl "~1.0.2"
+ depd "2.0.0"
+ encodeurl "~2.0.0"
escape-html "~1.0.3"
etag "~1.8.1"
- finalhandler "~1.1.2"
+ finalhandler "1.2.0"
fresh "0.5.2"
- merge-descriptors "1.0.1"
+ http-errors "2.0.0"
+ merge-descriptors "1.0.3"
methods "~1.1.2"
- on-finished "~2.3.0"
+ on-finished "2.4.1"
parseurl "~1.3.3"
- path-to-regexp "0.1.7"
- proxy-addr "~2.0.5"
- qs "6.7.0"
+ path-to-regexp "0.1.10"
+ proxy-addr "~2.0.7"
+ qs "6.11.0"
range-parser "~1.2.1"
- safe-buffer "5.1.2"
- send "0.17.1"
- serve-static "1.14.1"
- setprototypeof "1.1.1"
- statuses "~1.5.0"
+ safe-buffer "5.2.1"
+ send "0.19.0"
+ serve-static "1.16.0"
+ setprototypeof "1.2.0"
+ statuses "2.0.1"
type-is "~1.6.18"
utils-merge "1.0.1"
vary "~1.1.2"
-extend-shallow@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f"
- integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=
- dependencies:
- is-extendable "^0.1.0"
-
-extend-shallow@^3.0.0, extend-shallow@^3.0.2:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8"
- integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=
- dependencies:
- assign-symbols "^1.0.0"
- is-extendable "^1.0.1"
-
-extend@^3.0.0, extend@^3.0.2, extend@~3.0.2:
+extend@^3.0.0, extend@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
-extglob@^2.0.4:
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543"
- integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==
- dependencies:
- array-unique "^0.3.2"
- define-property "^1.0.0"
- expand-brackets "^2.1.4"
- extend-shallow "^2.0.1"
- fragment-cache "^0.2.1"
- regex-not "^1.0.0"
- snapdragon "^0.8.1"
- to-regex "^3.0.1"
-
-extsprintf@1.3.0:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
- integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=
-
-extsprintf@^1.2.0:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
- integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
-
fast-deep-equal@^3.1.1:
version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
-fast-json-stable-stringify@^2.0.0:
+fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
-fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6:
+fast-levenshtein@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
@@ -2759,6 +3723,13 @@ fast-text-encoding@^1.0.0:
resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz#ec02ac8e01ab8a319af182dae2681213cfe9ce53"
integrity sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig==
+fast-xml-parser@5.3.4:
+ version "5.3.4"
+ resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-5.3.4.tgz#06f39aafffdbc97bef0321e626c7ddd06a043ecf"
+ integrity sha512-EFd6afGmXlCx8H8WTZHhAoDaWaGyuIBoZJ2mknrNxug+aZKjkp0a0dlars9Izl+jF+7Gu1/5f/2h68cQpe0IiA==
+ dependencies:
+ strnum "^2.1.0"
+
fb-watchman@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85"
@@ -2785,16 +3756,6 @@ file-entry-cache@^5.0.1:
dependencies:
flat-cache "^2.0.1"
-fill-range@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7"
- integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=
- dependencies:
- extend-shallow "^2.0.1"
- is-number "^3.0.0"
- repeat-string "^1.6.1"
- to-regex-range "^2.1.0"
-
fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
@@ -2802,33 +3763,35 @@ fill-range@^7.0.1:
dependencies:
to-regex-range "^5.0.1"
-finalhandler@~1.1.2:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d"
- integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==
+fill-range@^7.1.1:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
+ integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
+ dependencies:
+ to-regex-range "^5.0.1"
+
+finalhandler@1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32"
+ integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==
dependencies:
debug "2.6.9"
encodeurl "~1.0.2"
escape-html "~1.0.3"
- on-finished "~2.3.0"
+ on-finished "2.4.1"
parseurl "~1.3.3"
- statuses "~1.5.0"
+ statuses "2.0.1"
unpipe "~1.0.0"
-find-cache-dir@^3.3.1:
- version "3.3.1"
- resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.1.tgz#89b33fad4a4670daa94f855f7fbe31d6d84fe880"
- integrity sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==
+find-cache-dir@^3.3.2:
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b"
+ integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==
dependencies:
commondir "^1.0.1"
make-dir "^3.0.2"
pkg-dir "^4.1.0"
-find-package-json@^1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/find-package-json/-/find-package-json-1.2.0.tgz#4057d1b943f82d8445fe52dc9cf456f6b8b58083"
- integrity sha512-+SOGcLGYDJHtyqHd87ysBhmaeQ95oWspDKnMXBrnQ9Eq4OkLNqejgoaD8xVWu6GPa0B6roa6KinCMEMcVeqONw==
-
find-up@^2.0.0, find-up@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
@@ -2872,15 +3835,10 @@ follow-redirects@1.5.10:
dependencies:
debug "=3.1.0"
-for-in@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
- integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=
-
-forever-agent@~0.6.1:
- version "0.6.1"
- resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
- integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
+follow-redirects@^1.15.2:
+ version "1.15.11"
+ resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.11.tgz#777d73d72a92f8ec4d2e410eb47352a56b8e8340"
+ integrity sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==
form-data@^2.3.1, form-data@^2.5.0:
version "2.5.1"
@@ -2891,31 +3849,15 @@ form-data@^2.3.1, form-data@^2.5.0:
combined-stream "^1.0.6"
mime-types "^2.1.12"
-form-data@~2.3.2:
- version "2.3.3"
- resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
- integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==
- dependencies:
- asynckit "^0.4.0"
- combined-stream "^1.0.6"
- mime-types "^2.1.12"
-
formidable@^1.2.0:
version "1.2.2"
resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.2.tgz#bf69aea2972982675f00865342b982986f6b8dd9"
integrity sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==
-forwarded@~0.1.2:
- version "0.1.2"
- resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
- integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=
-
-fragment-cache@^0.2.1:
- version "0.2.1"
- resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19"
- integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=
- dependencies:
- map-cache "^0.2.2"
+forwarded@0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
+ integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
fresh@0.5.2:
version "0.5.2"
@@ -2932,7 +3874,12 @@ fs.realpath@^1.0.0:
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
-fsevents@^2.1.2, fsevents@~2.1.2:
+fsevents@^2.3.2:
+ version "2.3.3"
+ resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
+ integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
+
+fsevents@~2.1.2:
version "2.1.3"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e"
integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==
@@ -2942,6 +3889,11 @@ function-bind@^1.1.1:
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
+function-bind@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
+ integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
+
functional-red-black-tree@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
@@ -2966,21 +3918,27 @@ gcp-metadata@^4.1.0:
gaxios "^3.0.0"
json-bigint "^1.0.0"
-gensync@^1.0.0-beta.1:
- version "1.0.0-beta.1"
- resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269"
- integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==
-
gensync@^1.0.0-beta.2:
version "1.0.0-beta.2"
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
-get-caller-file@^2.0.1:
+get-caller-file@^2.0.1, get-caller-file@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
+get-intrinsic@^1.1.3, get-intrinsic@^1.2.4:
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd"
+ integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==
+ dependencies:
+ es-errors "^1.3.0"
+ function-bind "^1.1.2"
+ has-proto "^1.0.1"
+ has-symbols "^1.0.3"
+ hasown "^2.0.0"
+
get-package-type@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a"
@@ -2996,7 +3954,7 @@ get-stdin@^6.0.0:
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b"
integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==
-get-stream@^4.0.0, get-stream@^4.1.0:
+get-stream@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5"
integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==
@@ -3010,17 +3968,10 @@ get-stream@^5.0.0, get-stream@^5.1.0:
dependencies:
pump "^3.0.0"
-get-value@^2.0.3, get-value@^2.0.6:
- version "2.0.6"
- resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28"
- integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=
-
-getpass@^0.1.1:
- version "0.1.7"
- resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
- integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=
- dependencies:
- assert-plus "^1.0.0"
+get-stream@^6.0.0:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
+ integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==
glob-parent@^5.0.0, glob-parent@~5.1.0:
version "5.1.2"
@@ -3029,7 +3980,7 @@ glob-parent@^5.0.0, glob-parent@~5.1.0:
dependencies:
is-glob "^4.0.1"
-glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4:
+glob@^7.1.3, glob@^7.1.4:
version "7.1.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
@@ -3102,6 +4053,13 @@ googleapis@^59.0.0:
google-auth-library "^6.0.0"
googleapis-common "^4.4.0"
+gopd@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c"
+ integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==
+ dependencies:
+ get-intrinsic "^1.1.3"
+
got@^9.6.0:
version "9.6.0"
resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85"
@@ -3119,15 +4077,15 @@ got@^9.6.0:
to-readable-stream "^1.0.0"
url-parse-lax "^3.0.0"
-graceful-fs@^4.1.2, graceful-fs@^4.2.4:
+graceful-fs@^4.1.2:
version "4.2.4"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
-growly@^1.3.0:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
- integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=
+graceful-fs@^4.2.9:
+ version "4.2.11"
+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
+ integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
gtoken@^5.0.4:
version "5.0.4"
@@ -3139,19 +4097,6 @@ gtoken@^5.0.4:
jws "^4.0.0"
mime "^2.2.0"
-har-schema@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
- integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
-
-har-validator@~5.1.3:
- version "5.1.5"
- resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd"
- integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==
- dependencies:
- ajv "^6.12.3"
- har-schema "^2.0.0"
-
has-flag@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
@@ -3162,41 +4107,27 @@ has-flag@^4.0.0:
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+has-property-descriptors@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854"
+ integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==
+ dependencies:
+ es-define-property "^1.0.0"
+
+has-proto@^1.0.1:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd"
+ integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==
+
has-symbols@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8"
integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==
-has-value@^0.3.1:
- version "0.3.1"
- resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f"
- integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=
- dependencies:
- get-value "^2.0.3"
- has-values "^0.1.4"
- isobject "^2.0.0"
-
-has-value@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177"
- integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=
- dependencies:
- get-value "^2.0.6"
- has-values "^1.0.0"
- isobject "^3.0.0"
-
-has-values@^0.1.4:
- version "0.1.4"
- resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771"
- integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E=
-
-has-values@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f"
- integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=
- dependencies:
- is-number "^3.0.0"
- kind-of "^4.0.0"
+has-symbols@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
+ integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
has-yarn@^2.1.0:
version "2.1.0"
@@ -3210,6 +4141,13 @@ has@^1.0.3:
dependencies:
function-bind "^1.1.1"
+hasown@^2.0.0, hasown@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
+ integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
+ dependencies:
+ function-bind "^1.1.2"
+
helmet-crossdomain@0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/helmet-crossdomain/-/helmet-crossdomain-0.4.0.tgz#5f1fe5a836d0325f1da0a78eaa5fd8429078894e"
@@ -3264,13 +4202,6 @@ hsts@2.2.0:
dependencies:
depd "2.0.0"
-html-encoding-sniffer@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3"
- integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==
- dependencies:
- whatwg-encoding "^1.0.5"
-
html-escaper@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
@@ -3281,18 +4212,7 @@ http-cache-semantics@^4.0.0:
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"
integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==
-http-errors@1.7.2:
- version "1.7.2"
- resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f"
- integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==
- dependencies:
- depd "~1.1.2"
- inherits "2.0.3"
- setprototypeof "1.1.1"
- statuses ">= 1.5.0 < 2"
- toidentifier "1.0.0"
-
-http-errors@1.7.3, http-errors@~1.7.2:
+http-errors@1.7.3:
version "1.7.3"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06"
integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==
@@ -3303,14 +4223,16 @@ http-errors@1.7.3, http-errors@~1.7.2:
statuses ">= 1.5.0 < 2"
toidentifier "1.0.0"
-http-signature@~1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
- integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=
+http-errors@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"
+ integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==
dependencies:
- assert-plus "^1.0.0"
- jsprim "^1.2.2"
- sshpk "^1.7.0"
+ depd "2.0.0"
+ inherits "2.0.4"
+ setprototypeof "1.2.0"
+ statuses "2.0.1"
+ toidentifier "1.0.1"
https-proxy-agent@^5.0.0:
version "5.0.0"
@@ -3320,11 +4242,24 @@ https-proxy-agent@^5.0.0:
agent-base "6"
debug "4"
+https-proxy-agent@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
+ integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==
+ dependencies:
+ agent-base "6"
+ debug "4"
+
human-signals@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3"
integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==
+human-signals@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
+ integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
+
iconv-lite@0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
@@ -3332,6 +4267,11 @@ iconv-lite@0.4.24:
dependencies:
safer-buffer ">= 2.1.2 < 3"
+ieee754@^1.1.13:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
+ integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
+
ieee754@^1.1.4:
version "1.1.13"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
@@ -3391,11 +4331,6 @@ inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3:
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
-inherits@2.0.3:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
- integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
-
ini@^1.3.5, ini@~1.3.0:
version "1.3.8"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
@@ -3410,30 +4345,16 @@ internal-slot@^1.0.2:
has "^1.0.3"
side-channel "^1.0.2"
-ip-regex@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9"
- integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=
+ip-address@^10.0.1:
+ version "10.1.0"
+ resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-10.1.0.tgz#d8dcffb34d0e02eb241427444a6e23f5b0595aa4"
+ integrity sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==
ipaddr.js@1.9.1:
version "1.9.1"
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
-is-accessor-descriptor@^0.1.6:
- version "0.1.6"
- resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6"
- integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=
- dependencies:
- kind-of "^3.0.2"
-
-is-accessor-descriptor@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656"
- integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==
- dependencies:
- kind-of "^6.0.0"
-
is-arguments@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3"
@@ -3451,11 +4372,6 @@ is-binary-path@~2.1.0:
dependencies:
binary-extensions "^2.0.0"
-is-buffer@^1.1.5:
- version "1.1.6"
- resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
- integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
-
is-callable@^1.1.4, is-callable@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9"
@@ -3468,60 +4384,18 @@ is-ci@^2.0.0:
dependencies:
ci-info "^2.0.0"
-is-data-descriptor@^0.1.4:
- version "0.1.4"
- resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56"
- integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=
- dependencies:
- kind-of "^3.0.2"
-
-is-data-descriptor@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7"
- integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==
+is-core-module@^2.16.1:
+ version "2.16.1"
+ resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4"
+ integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==
dependencies:
- kind-of "^6.0.0"
+ hasown "^2.0.2"
is-date-object@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e"
integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==
-is-descriptor@^0.1.0:
- version "0.1.6"
- resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca"
- integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==
- dependencies:
- is-accessor-descriptor "^0.1.6"
- is-data-descriptor "^0.1.4"
- kind-of "^5.0.0"
-
-is-descriptor@^1.0.0, is-descriptor@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec"
- integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==
- dependencies:
- is-accessor-descriptor "^1.0.0"
- is-data-descriptor "^1.0.0"
- kind-of "^6.0.2"
-
-is-docker@^2.0.0:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.1.1.tgz#4125a88e44e450d384e09047ede71adc2d144156"
- integrity sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==
-
-is-extendable@^0.1.0, is-extendable@^0.1.1:
- version "0.1.1"
- resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89"
- integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=
-
-is-extendable@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4"
- integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==
- dependencies:
- is-plain-object "^2.0.4"
-
is-extglob@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
@@ -3572,13 +4446,6 @@ is-npm@^4.0.0:
resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-4.0.0.tgz#c90dd8380696df87a7a6d823c20d0b12bbe3c84d"
integrity sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==
-is-number@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
- integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=
- dependencies:
- kind-of "^3.0.2"
-
is-number@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
@@ -3594,18 +4461,6 @@ is-path-inside@^3.0.1:
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.2.tgz#f5220fc82a3e233757291dddc9c5877f2a1f3017"
integrity sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==
-is-plain-object@^2.0.3, is-plain-object@^2.0.4:
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
- integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==
- dependencies:
- isobject "^3.0.1"
-
-is-potential-custom-element-name@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz#0c52e54bcca391bb2c494b21e8626d7336c6e397"
- integrity sha1-DFLlS8yjkbssSUsh6GJtczbG45c=
-
is-regex@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9"
@@ -3640,29 +4495,17 @@ is-symbol@^1.0.2:
dependencies:
has-symbols "^1.0.1"
-is-typedarray@^1.0.0, is-typedarray@~1.0.0:
+is-typedarray@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
-is-windows@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
- integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==
-
-is-wsl@^2.2.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271"
- integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==
- dependencies:
- is-docker "^2.0.0"
-
is-yarn-global@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232"
integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==
-isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0:
+isarray@^1.0.0, isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
@@ -3677,38 +4520,38 @@ isexe@^2.0.0:
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
-isobject@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89"
- integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=
- dependencies:
- isarray "1.0.0"
-
-isobject@^3.0.0, isobject@^3.0.1:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
- integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
-
-isstream@~0.1.2:
- version "0.1.2"
- resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
- integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
-
istanbul-lib-coverage@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec"
integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==
-istanbul-lib-instrument@^4.0.0, istanbul-lib-instrument@^4.0.3:
- version "4.0.3"
- resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d"
- integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==
+istanbul-lib-coverage@^3.2.0:
+ version "3.2.2"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756"
+ integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==
+
+istanbul-lib-instrument@^5.0.4:
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d"
+ integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==
dependencies:
- "@babel/core" "^7.7.5"
+ "@babel/core" "^7.12.3"
+ "@babel/parser" "^7.14.7"
"@istanbuljs/schema" "^0.1.2"
- istanbul-lib-coverage "^3.0.0"
+ istanbul-lib-coverage "^3.2.0"
semver "^6.3.0"
+istanbul-lib-instrument@^6.0.0:
+ version "6.0.3"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz#fa15401df6c15874bcb2105f773325d78c666765"
+ integrity sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==
+ dependencies:
+ "@babel/core" "^7.23.9"
+ "@babel/parser" "^7.23.9"
+ "@istanbuljs/schema" "^0.1.3"
+ istanbul-lib-coverage "^3.2.0"
+ semver "^7.5.4"
+
istanbul-lib-report@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6"
@@ -3727,10 +4570,10 @@ istanbul-lib-source-maps@^4.0.0:
istanbul-lib-coverage "^3.0.0"
source-map "^0.6.1"
-istanbul-reports@^3.0.2:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.2.tgz#d593210e5000683750cb09fc0644e4b6e27fd53b"
- integrity sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==
+istanbul-reports@^3.1.3:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.2.0.tgz#cb4535162b5784aa623cee21a7252cf2c807ac93"
+ integrity sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==
dependencies:
html-escaper "^2.0.0"
istanbul-lib-report "^3.0.0"
@@ -3748,376 +4591,363 @@ iterate-value@^1.0.0:
es-get-iterator "^1.0.2"
iterate-iterator "^1.0.1"
-jest-changed-files@^26.5.2:
- version "26.5.2"
- resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-26.5.2.tgz#330232c6a5c09a7f040a5870e8f0a9c6abcdbed5"
- integrity sha512-qSmssmiIdvM5BWVtyK/nqVpN3spR5YyvkvPqz1x3BR1bwIxsWmU/MGwLoCrPNLbkG2ASAKfvmJpOduEApBPh2w==
+jest-changed-files@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a"
+ integrity sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==
dependencies:
- "@jest/types" "^26.5.2"
- execa "^4.0.0"
- throat "^5.0.0"
+ execa "^5.0.0"
+ jest-util "^29.7.0"
+ p-limit "^3.1.0"
-jest-cli@^26.5.3:
- version "26.5.3"
- resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-26.5.3.tgz#f936b98f247b76b7bc89c7af50af82c88e356a80"
- integrity sha512-HkbSvtugpSXBf2660v9FrNVUgxvPkssN8CRGj9gPM8PLhnaa6zziFiCEKQAkQS4uRzseww45o0TR+l6KeRYV9A==
+jest-circus@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a"
+ integrity sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==
dependencies:
- "@jest/core" "^26.5.3"
- "@jest/test-result" "^26.5.2"
- "@jest/types" "^26.5.2"
+ "@jest/environment" "^29.7.0"
+ "@jest/expect" "^29.7.0"
+ "@jest/test-result" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ "@types/node" "*"
chalk "^4.0.0"
- exit "^0.1.2"
- graceful-fs "^4.2.4"
- import-local "^3.0.2"
- is-ci "^2.0.0"
- jest-config "^26.5.3"
- jest-util "^26.5.2"
- jest-validate "^26.5.3"
- prompts "^2.0.1"
- yargs "^15.4.1"
+ co "^4.6.0"
+ dedent "^1.0.0"
+ is-generator-fn "^2.0.0"
+ jest-each "^29.7.0"
+ jest-matcher-utils "^29.7.0"
+ jest-message-util "^29.7.0"
+ jest-runtime "^29.7.0"
+ jest-snapshot "^29.7.0"
+ jest-util "^29.7.0"
+ p-limit "^3.1.0"
+ pretty-format "^29.7.0"
+ pure-rand "^6.0.0"
+ slash "^3.0.0"
+ stack-utils "^2.0.3"
-jest-config@^26.5.3:
- version "26.5.3"
- resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-26.5.3.tgz#baf51c9be078c2c755c8f8a51ec0f06c762c1d3f"
- integrity sha512-NVhZiIuN0GQM6b6as4CI5FSCyXKxdrx5ACMCcv/7Pf+TeCajJhJc+6dwgdAVPyerUFB9pRBIz3bE7clSrRge/w==
+jest-cli@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995"
+ integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==
dependencies:
- "@babel/core" "^7.1.0"
- "@jest/test-sequencer" "^26.5.3"
- "@jest/types" "^26.5.2"
- babel-jest "^26.5.2"
+ "@jest/core" "^29.7.0"
+ "@jest/test-result" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ chalk "^4.0.0"
+ create-jest "^29.7.0"
+ exit "^0.1.2"
+ import-local "^3.0.2"
+ jest-config "^29.7.0"
+ jest-util "^29.7.0"
+ jest-validate "^29.7.0"
+ yargs "^17.3.1"
+
+jest-config@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f"
+ integrity sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==
+ dependencies:
+ "@babel/core" "^7.11.6"
+ "@jest/test-sequencer" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ babel-jest "^29.7.0"
chalk "^4.0.0"
+ ci-info "^3.2.0"
deepmerge "^4.2.2"
- glob "^7.1.1"
- graceful-fs "^4.2.4"
- jest-environment-jsdom "^26.5.2"
- jest-environment-node "^26.5.2"
- jest-get-type "^26.3.0"
- jest-jasmine2 "^26.5.3"
- jest-regex-util "^26.0.0"
- jest-resolve "^26.5.2"
- jest-util "^26.5.2"
- jest-validate "^26.5.3"
- micromatch "^4.0.2"
- pretty-format "^26.5.2"
-
-jest-diff@^26.5.2:
- version "26.5.2"
- resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.5.2.tgz#8e26cb32dc598e8b8a1b9deff55316f8313c8053"
- integrity sha512-HCSWDUGwsov5oTlGzrRM+UPJI/Dpqi9jzeV0fdRNi3Ch5bnoXhnyJMmVg2juv9081zLIy3HGPI5mcuGgXM2xRA==
+ glob "^7.1.3"
+ graceful-fs "^4.2.9"
+ jest-circus "^29.7.0"
+ jest-environment-node "^29.7.0"
+ jest-get-type "^29.6.3"
+ jest-regex-util "^29.6.3"
+ jest-resolve "^29.7.0"
+ jest-runner "^29.7.0"
+ jest-util "^29.7.0"
+ jest-validate "^29.7.0"
+ micromatch "^4.0.4"
+ parse-json "^5.2.0"
+ pretty-format "^29.7.0"
+ slash "^3.0.0"
+ strip-json-comments "^3.1.1"
+
+jest-diff@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a"
+ integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==
dependencies:
chalk "^4.0.0"
- diff-sequences "^26.5.0"
- jest-get-type "^26.3.0"
- pretty-format "^26.5.2"
+ diff-sequences "^29.6.3"
+ jest-get-type "^29.6.3"
+ pretty-format "^29.7.0"
-jest-docblock@^26.0.0:
- version "26.0.0"
- resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-26.0.0.tgz#3e2fa20899fc928cb13bd0ff68bd3711a36889b5"
- integrity sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==
+jest-docblock@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a"
+ integrity sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==
dependencies:
detect-newline "^3.0.0"
-jest-each@^26.5.2:
- version "26.5.2"
- resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-26.5.2.tgz#35e68d6906a7f826d3ca5803cfe91d17a5a34c31"
- integrity sha512-w7D9FNe0m2D3yZ0Drj9CLkyF/mGhmBSULMQTypzAKR746xXnjUrK8GUJdlLTWUF6dd0ks3MtvGP7/xNFr9Aphg==
+jest-each@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1"
+ integrity sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==
dependencies:
- "@jest/types" "^26.5.2"
+ "@jest/types" "^29.6.3"
chalk "^4.0.0"
- jest-get-type "^26.3.0"
- jest-util "^26.5.2"
- pretty-format "^26.5.2"
-
-jest-environment-jsdom@^26.5.2:
- version "26.5.2"
- resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-26.5.2.tgz#5feab05b828fd3e4b96bee5e0493464ddd2bb4bc"
- integrity sha512-fWZPx0bluJaTQ36+PmRpvUtUlUFlGGBNyGX1SN3dLUHHMcQ4WseNEzcGGKOw4U5towXgxI4qDoI3vwR18H0RTw==
- dependencies:
- "@jest/environment" "^26.5.2"
- "@jest/fake-timers" "^26.5.2"
- "@jest/types" "^26.5.2"
- "@types/node" "*"
- jest-mock "^26.5.2"
- jest-util "^26.5.2"
- jsdom "^16.4.0"
-
-jest-environment-node@^26.5.2:
- version "26.5.2"
- resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-26.5.2.tgz#275a0f01b5e47447056f1541a15ed4da14acca03"
- integrity sha512-YHjnDsf/GKFCYMGF1V+6HF7jhY1fcLfLNBDjhAOvFGvt6d8vXvNdJGVM7uTZ2VO/TuIyEFhPGaXMX5j3h7fsrA==
- dependencies:
- "@jest/environment" "^26.5.2"
- "@jest/fake-timers" "^26.5.2"
- "@jest/types" "^26.5.2"
+ jest-get-type "^29.6.3"
+ jest-util "^29.7.0"
+ pretty-format "^29.7.0"
+
+jest-environment-node@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376"
+ integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==
+ dependencies:
+ "@jest/environment" "^29.7.0"
+ "@jest/fake-timers" "^29.7.0"
+ "@jest/types" "^29.6.3"
"@types/node" "*"
- jest-mock "^26.5.2"
- jest-util "^26.5.2"
+ jest-mock "^29.7.0"
+ jest-util "^29.7.0"
-jest-get-type@^26.3.0:
- version "26.3.0"
- resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0"
- integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==
+jest-get-type@^29.6.3:
+ version "29.6.3"
+ resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1"
+ integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==
-jest-haste-map@^26.5.2:
- version "26.5.2"
- resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.5.2.tgz#a15008abfc502c18aa56e4919ed8c96304ceb23d"
- integrity sha512-lJIAVJN3gtO3k4xy+7i2Xjtwh8CfPcH08WYjZpe9xzveDaqGw9fVNCpkYu6M525wKFVkLmyi7ku+DxCAP1lyMA==
+jest-haste-map@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104"
+ integrity sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==
dependencies:
- "@jest/types" "^26.5.2"
- "@types/graceful-fs" "^4.1.2"
+ "@jest/types" "^29.6.3"
+ "@types/graceful-fs" "^4.1.3"
"@types/node" "*"
anymatch "^3.0.3"
fb-watchman "^2.0.0"
- graceful-fs "^4.2.4"
- jest-regex-util "^26.0.0"
- jest-serializer "^26.5.0"
- jest-util "^26.5.2"
- jest-worker "^26.5.0"
- micromatch "^4.0.2"
- sane "^4.0.3"
- walker "^1.0.7"
+ graceful-fs "^4.2.9"
+ jest-regex-util "^29.6.3"
+ jest-util "^29.7.0"
+ jest-worker "^29.7.0"
+ micromatch "^4.0.4"
+ walker "^1.0.8"
optionalDependencies:
- fsevents "^2.1.2"
-
-jest-jasmine2@^26.5.3:
- version "26.5.3"
- resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-26.5.3.tgz#baad2114ce32d16aff25aeb877d18bb4e332dc4c"
- integrity sha512-nFlZOpnGlNc7y/+UkkeHnvbOM+rLz4wB1AimgI9QhtnqSZte0wYjbAm8hf7TCwXlXgDwZxAXo6z0a2Wzn9FoOg==
- dependencies:
- "@babel/traverse" "^7.1.0"
- "@jest/environment" "^26.5.2"
- "@jest/source-map" "^26.5.0"
- "@jest/test-result" "^26.5.2"
- "@jest/types" "^26.5.2"
- "@types/node" "*"
- chalk "^4.0.0"
- co "^4.6.0"
- expect "^26.5.3"
- is-generator-fn "^2.0.0"
- jest-each "^26.5.2"
- jest-matcher-utils "^26.5.2"
- jest-message-util "^26.5.2"
- jest-runtime "^26.5.3"
- jest-snapshot "^26.5.3"
- jest-util "^26.5.2"
- pretty-format "^26.5.2"
- throat "^5.0.0"
-
-jest-leak-detector@^26.5.2:
- version "26.5.2"
- resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-26.5.2.tgz#83fcf9a4a6ef157549552cb4f32ca1d6221eea69"
- integrity sha512-h7ia3dLzBFItmYERaLPEtEKxy3YlcbcRSjj0XRNJgBEyODuu+3DM2o62kvIFvs3PsaYoIIv+e+nLRI61Dj1CNw==
- dependencies:
- jest-get-type "^26.3.0"
- pretty-format "^26.5.2"
-
-jest-matcher-utils@^26.5.2:
- version "26.5.2"
- resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-26.5.2.tgz#6aa2c76ce8b9c33e66f8856ff3a52bab59e6c85a"
- integrity sha512-W9GO9KBIC4gIArsNqDUKsLnhivaqf8MSs6ujO/JDcPIQrmY+aasewweXVET8KdrJ6ADQaUne5UzysvF/RR7JYA==
+ fsevents "^2.3.2"
+
+jest-leak-detector@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728"
+ integrity sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==
+ dependencies:
+ jest-get-type "^29.6.3"
+ pretty-format "^29.7.0"
+
+jest-matcher-utils@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12"
+ integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==
dependencies:
chalk "^4.0.0"
- jest-diff "^26.5.2"
- jest-get-type "^26.3.0"
- pretty-format "^26.5.2"
+ jest-diff "^29.7.0"
+ jest-get-type "^29.6.3"
+ pretty-format "^29.7.0"
-jest-message-util@^26.5.2:
- version "26.5.2"
- resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.5.2.tgz#6c4c4c46dcfbabb47cd1ba2f6351559729bc11bb"
- integrity sha512-Ocp9UYZ5Jl15C5PNsoDiGEk14A4NG0zZKknpWdZGoMzJuGAkVt10e97tnEVMYpk7LnQHZOfuK2j/izLBMcuCZw==
+jest-message-util@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3"
+ integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==
dependencies:
- "@babel/code-frame" "^7.0.0"
- "@jest/types" "^26.5.2"
+ "@babel/code-frame" "^7.12.13"
+ "@jest/types" "^29.6.3"
"@types/stack-utils" "^2.0.0"
chalk "^4.0.0"
- graceful-fs "^4.2.4"
- micromatch "^4.0.2"
+ graceful-fs "^4.2.9"
+ micromatch "^4.0.4"
+ pretty-format "^29.7.0"
slash "^3.0.0"
- stack-utils "^2.0.2"
+ stack-utils "^2.0.3"
-jest-mock@^26.5.2:
- version "26.5.2"
- resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-26.5.2.tgz#c9302e8ef807f2bfc749ee52e65ad11166a1b6a1"
- integrity sha512-9SiU4b5PtO51v0MtJwVRqeGEroH66Bnwtq4ARdNP7jNXbpT7+ByeWNAk4NeT/uHfNSVDXEXgQo1XRuwEqS6Rdw==
+jest-mock@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347"
+ integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==
dependencies:
- "@jest/types" "^26.5.2"
+ "@jest/types" "^29.6.3"
"@types/node" "*"
+ jest-util "^29.7.0"
jest-pnp-resolver@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c"
integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==
-jest-regex-util@^26.0.0:
- version "26.0.0"
- resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28"
- integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==
+jest-regex-util@^29.6.3:
+ version "29.6.3"
+ resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52"
+ integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==
-jest-resolve-dependencies@^26.5.3:
- version "26.5.3"
- resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-26.5.3.tgz#11483f91e534bdcd257ab21e8622799e59701aba"
- integrity sha512-+KMDeke/BFK+mIQ2IYSyBz010h7zQaVt4Xie6cLqUGChorx66vVeQVv4ErNoMwInnyYHi1Ud73tDS01UbXbfLQ==
+jest-resolve-dependencies@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428"
+ integrity sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==
dependencies:
- "@jest/types" "^26.5.2"
- jest-regex-util "^26.0.0"
- jest-snapshot "^26.5.3"
+ jest-regex-util "^29.6.3"
+ jest-snapshot "^29.7.0"
-jest-resolve@^26.5.2:
- version "26.5.2"
- resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-26.5.2.tgz#0d719144f61944a428657b755a0e5c6af4fc8602"
- integrity sha512-XsPxojXGRA0CoDD7Vis59ucz2p3cQFU5C+19tz3tLEAlhYKkK77IL0cjYjikY9wXnOaBeEdm1rOgSJjbZWpcZg==
+jest-resolve@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30"
+ integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==
dependencies:
- "@jest/types" "^26.5.2"
chalk "^4.0.0"
- graceful-fs "^4.2.4"
+ graceful-fs "^4.2.9"
+ jest-haste-map "^29.7.0"
jest-pnp-resolver "^1.2.2"
- jest-util "^26.5.2"
- read-pkg-up "^7.0.1"
- resolve "^1.17.0"
+ jest-util "^29.7.0"
+ jest-validate "^29.7.0"
+ resolve "^1.20.0"
+ resolve.exports "^2.0.0"
slash "^3.0.0"
-jest-runner@^26.5.3:
- version "26.5.3"
- resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-26.5.3.tgz#800787459ea59c68e7505952933e33981dc3db38"
- integrity sha512-qproP0Pq7IIule+263W57k2+8kWCszVJTC9TJWGUz0xJBr+gNiniGXlG8rotd0XxwonD5UiJloYoSO5vbUr5FQ==
+jest-runner@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e"
+ integrity sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==
dependencies:
- "@jest/console" "^26.5.2"
- "@jest/environment" "^26.5.2"
- "@jest/test-result" "^26.5.2"
- "@jest/types" "^26.5.2"
+ "@jest/console" "^29.7.0"
+ "@jest/environment" "^29.7.0"
+ "@jest/test-result" "^29.7.0"
+ "@jest/transform" "^29.7.0"
+ "@jest/types" "^29.6.3"
"@types/node" "*"
chalk "^4.0.0"
- emittery "^0.7.1"
- exit "^0.1.2"
- graceful-fs "^4.2.4"
- jest-config "^26.5.3"
- jest-docblock "^26.0.0"
- jest-haste-map "^26.5.2"
- jest-leak-detector "^26.5.2"
- jest-message-util "^26.5.2"
- jest-resolve "^26.5.2"
- jest-runtime "^26.5.3"
- jest-util "^26.5.2"
- jest-worker "^26.5.0"
- source-map-support "^0.5.6"
- throat "^5.0.0"
-
-jest-runtime@^26.5.3:
- version "26.5.3"
- resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-26.5.3.tgz#5882ae91fd88304310f069549e6bf82f3f198bea"
- integrity sha512-IDjalmn2s/Tc4GvUwhPHZ0iaXCdMRq5p6taW9P8RpU+FpG01O3+H8z+p3rDCQ9mbyyyviDgxy/LHPLzrIOKBkQ==
- dependencies:
- "@jest/console" "^26.5.2"
- "@jest/environment" "^26.5.2"
- "@jest/fake-timers" "^26.5.2"
- "@jest/globals" "^26.5.3"
- "@jest/source-map" "^26.5.0"
- "@jest/test-result" "^26.5.2"
- "@jest/transform" "^26.5.2"
- "@jest/types" "^26.5.2"
- "@types/yargs" "^15.0.0"
+ emittery "^0.13.1"
+ graceful-fs "^4.2.9"
+ jest-docblock "^29.7.0"
+ jest-environment-node "^29.7.0"
+ jest-haste-map "^29.7.0"
+ jest-leak-detector "^29.7.0"
+ jest-message-util "^29.7.0"
+ jest-resolve "^29.7.0"
+ jest-runtime "^29.7.0"
+ jest-util "^29.7.0"
+ jest-watcher "^29.7.0"
+ jest-worker "^29.7.0"
+ p-limit "^3.1.0"
+ source-map-support "0.5.13"
+
+jest-runtime@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817"
+ integrity sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==
+ dependencies:
+ "@jest/environment" "^29.7.0"
+ "@jest/fake-timers" "^29.7.0"
+ "@jest/globals" "^29.7.0"
+ "@jest/source-map" "^29.6.3"
+ "@jest/test-result" "^29.7.0"
+ "@jest/transform" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ "@types/node" "*"
chalk "^4.0.0"
+ cjs-module-lexer "^1.0.0"
collect-v8-coverage "^1.0.0"
- exit "^0.1.2"
glob "^7.1.3"
- graceful-fs "^4.2.4"
- jest-config "^26.5.3"
- jest-haste-map "^26.5.2"
- jest-message-util "^26.5.2"
- jest-mock "^26.5.2"
- jest-regex-util "^26.0.0"
- jest-resolve "^26.5.2"
- jest-snapshot "^26.5.3"
- jest-util "^26.5.2"
- jest-validate "^26.5.3"
+ graceful-fs "^4.2.9"
+ jest-haste-map "^29.7.0"
+ jest-message-util "^29.7.0"
+ jest-mock "^29.7.0"
+ jest-regex-util "^29.6.3"
+ jest-resolve "^29.7.0"
+ jest-snapshot "^29.7.0"
+ jest-util "^29.7.0"
slash "^3.0.0"
strip-bom "^4.0.0"
- yargs "^15.4.1"
-
-jest-serializer@^26.5.0:
- version "26.5.0"
- resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.5.0.tgz#f5425cc4c5f6b4b355f854b5f0f23ec6b962bc13"
- integrity sha512-+h3Gf5CDRlSLdgTv7y0vPIAoLgX/SI7T4v6hy+TEXMgYbv+ztzbg5PSN6mUXAT/hXYHvZRWm+MaObVfqkhCGxA==
- dependencies:
- "@types/node" "*"
- graceful-fs "^4.2.4"
-jest-snapshot@^26.5.3:
- version "26.5.3"
- resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-26.5.3.tgz#f6b4b4b845f85d4b0dadd7cf119c55d0c1688601"
- integrity sha512-ZgAk0Wm0JJ75WS4lGaeRfa0zIgpL0KD595+XmtwlIEMe8j4FaYHyZhP1LNOO+8fXq7HJ3hll54+sFV9X4+CGVw==
+jest-snapshot@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5"
+ integrity sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==
dependencies:
- "@babel/types" "^7.0.0"
- "@jest/types" "^26.5.2"
- "@types/babel__traverse" "^7.0.4"
- "@types/prettier" "^2.0.0"
+ "@babel/core" "^7.11.6"
+ "@babel/generator" "^7.7.2"
+ "@babel/plugin-syntax-jsx" "^7.7.2"
+ "@babel/plugin-syntax-typescript" "^7.7.2"
+ "@babel/types" "^7.3.3"
+ "@jest/expect-utils" "^29.7.0"
+ "@jest/transform" "^29.7.0"
+ "@jest/types" "^29.6.3"
+ babel-preset-current-node-syntax "^1.0.0"
chalk "^4.0.0"
- expect "^26.5.3"
- graceful-fs "^4.2.4"
- jest-diff "^26.5.2"
- jest-get-type "^26.3.0"
- jest-haste-map "^26.5.2"
- jest-matcher-utils "^26.5.2"
- jest-message-util "^26.5.2"
- jest-resolve "^26.5.2"
+ expect "^29.7.0"
+ graceful-fs "^4.2.9"
+ jest-diff "^29.7.0"
+ jest-get-type "^29.6.3"
+ jest-matcher-utils "^29.7.0"
+ jest-message-util "^29.7.0"
+ jest-util "^29.7.0"
natural-compare "^1.4.0"
- pretty-format "^26.5.2"
- semver "^7.3.2"
+ pretty-format "^29.7.0"
+ semver "^7.5.3"
-jest-util@^26.5.2:
- version "26.5.2"
- resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.5.2.tgz#8403f75677902cc52a1b2140f568e91f8ed4f4d7"
- integrity sha512-WTL675bK+GSSAYgS8z9FWdCT2nccO1yTIplNLPlP0OD8tUk/H5IrWKMMRudIQQ0qp8bb4k+1Qa8CxGKq9qnYdg==
+jest-util@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc"
+ integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==
dependencies:
- "@jest/types" "^26.5.2"
+ "@jest/types" "^29.6.3"
"@types/node" "*"
chalk "^4.0.0"
- graceful-fs "^4.2.4"
- is-ci "^2.0.0"
- micromatch "^4.0.2"
+ ci-info "^3.2.0"
+ graceful-fs "^4.2.9"
+ picomatch "^2.2.3"
-jest-validate@^26.5.3:
- version "26.5.3"
- resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.5.3.tgz#eefd5a5c87059550548c5ad8d6589746c66929e3"
- integrity sha512-LX07qKeAtY+lsU0o3IvfDdN5KH9OulEGOMN1sFo6PnEf5/qjS1LZIwNk9blcBeW94pQUI9dLN9FlDYDWI5tyaA==
+jest-validate@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c"
+ integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==
dependencies:
- "@jest/types" "^26.5.2"
- camelcase "^6.0.0"
+ "@jest/types" "^29.6.3"
+ camelcase "^6.2.0"
chalk "^4.0.0"
- jest-get-type "^26.3.0"
+ jest-get-type "^29.6.3"
leven "^3.1.0"
- pretty-format "^26.5.2"
+ pretty-format "^29.7.0"
-jest-watcher@^26.5.2:
- version "26.5.2"
- resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-26.5.2.tgz#2957f4461007e0769d74b537379ecf6b7c696916"
- integrity sha512-i3m1NtWzF+FXfJ3ljLBB/WQEp4uaNhX7QcQUWMokcifFTUQBDFyUMEwk0JkJ1kopHbx7Een3KX0Q7+9koGM/Pw==
+jest-watcher@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2"
+ integrity sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==
dependencies:
- "@jest/test-result" "^26.5.2"
- "@jest/types" "^26.5.2"
+ "@jest/test-result" "^29.7.0"
+ "@jest/types" "^29.6.3"
"@types/node" "*"
ansi-escapes "^4.2.1"
chalk "^4.0.0"
- jest-util "^26.5.2"
+ emittery "^0.13.1"
+ jest-util "^29.7.0"
string-length "^4.0.1"
-jest-worker@^26.5.0:
- version "26.5.0"
- resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.5.0.tgz#87deee86dbbc5f98d9919e0dadf2c40e3152fa30"
- integrity sha512-kTw66Dn4ZX7WpjZ7T/SUDgRhapFRKWmisVAF0Rv4Fu8SLFD7eLbqpLvbxVqYhSgaWa7I+bW7pHnbyfNsH6stug==
+jest-worker@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a"
+ integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==
dependencies:
"@types/node" "*"
+ jest-util "^29.7.0"
merge-stream "^2.0.0"
- supports-color "^7.0.0"
+ supports-color "^8.0.0"
-jest@^26.4.0:
- version "26.5.3"
- resolved "https://registry.yarnpkg.com/jest/-/jest-26.5.3.tgz#5e7a322d16f558dc565ca97639e85993ef5affe6"
- integrity sha512-uJi3FuVSLmkZrWvaDyaVTZGLL8WcfynbRnFXyAHuEtYiSZ+ijDDIMOw1ytmftK+y/+OdAtsG9QrtbF7WIBmOyA==
+jest@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613"
+ integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==
dependencies:
- "@jest/core" "^26.5.3"
+ "@jest/core" "^29.7.0"
+ "@jest/types" "^29.6.3"
import-local "^3.0.2"
- jest-cli "^26.5.3"
+ jest-cli "^29.7.0"
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0"
@@ -4125,55 +4955,23 @@ jest@^26.4.0:
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
js-yaml@^3.13.1:
- version "3.14.0"
- resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482"
- integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==
+ version "3.14.2"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.2.tgz#77485ce1dd7f33c061fd1b16ecea23b55fcb04b0"
+ integrity sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==
dependencies:
argparse "^1.0.7"
esprima "^4.0.0"
-jsbn@~0.1.0:
- version "0.1.1"
- resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
- integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
-
-jsdom@^16.4.0:
- version "16.4.0"
- resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.4.0.tgz#36005bde2d136f73eee1a830c6d45e55408edddb"
- integrity sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w==
- dependencies:
- abab "^2.0.3"
- acorn "^7.1.1"
- acorn-globals "^6.0.0"
- cssom "^0.4.4"
- cssstyle "^2.2.0"
- data-urls "^2.0.0"
- decimal.js "^10.2.0"
- domexception "^2.0.1"
- escodegen "^1.14.1"
- html-encoding-sniffer "^2.0.1"
- is-potential-custom-element-name "^1.0.0"
- nwsapi "^2.2.0"
- parse5 "5.1.1"
- request "^2.88.2"
- request-promise-native "^1.0.8"
- saxes "^5.0.0"
- symbol-tree "^3.2.4"
- tough-cookie "^3.0.1"
- w3c-hr-time "^1.0.2"
- w3c-xmlserializer "^2.0.0"
- webidl-conversions "^6.1.0"
- whatwg-encoding "^1.0.5"
- whatwg-mimetype "^2.3.0"
- whatwg-url "^8.0.0"
- ws "^7.2.3"
- xml-name-validator "^3.0.0"
-
jsesc@^2.5.1:
version "2.5.2"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
+jsesc@^3.0.2:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d"
+ integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==
+
json-bigint@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/json-bigint/-/json-bigint-1.0.0.tgz#ae547823ac0cad8398667f8cd9ef4730f5b01ff1"
@@ -4201,21 +4999,11 @@ json-schema-traverse@^0.4.1:
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
-json-schema@0.2.3:
- version "0.2.3"
- resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
- integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
-
json-stable-stringify-without-jsonify@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
-json-stringify-safe@~5.0.1:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
- integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
-
json5@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe"
@@ -4230,6 +5018,11 @@ json5@^2.1.2:
dependencies:
minimist "^1.2.5"
+json5@^2.2.3:
+ version "2.2.3"
+ resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
+ integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
+
jsonwebtoken@^8.5.1:
version "8.5.1"
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d"
@@ -4246,16 +5039,6 @@ jsonwebtoken@^8.5.1:
ms "^2.1.1"
semver "^5.6.0"
-jsprim@^1.2.2:
- version "1.4.1"
- resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
- integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=
- dependencies:
- assert-plus "1.0.0"
- extsprintf "1.3.0"
- json-schema "0.2.3"
- verror "1.10.0"
-
jsx-ast-utils@^2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.4.1.tgz#1114a4c1209481db06c690c2b4f488cc665f657e"
@@ -4306,10 +5089,10 @@ jws@^4.0.0:
jwa "^2.0.0"
safe-buffer "^5.0.1"
-kareem@2.3.1:
- version "2.3.1"
- resolved "https://registry.yarnpkg.com/kareem/-/kareem-2.3.1.tgz#def12d9c941017fabfb00f873af95e9c99e1be87"
- integrity sha512-l3hLhffs9zqoDe8zjmb/mAN4B8VT3L56EUvKNqLFVs9YlFA+zx7ke1DO8STAdDyYNkeSo1nKmjuvQeI12So8Xw==
+kareem@2.6.3:
+ version "2.6.3"
+ resolved "https://registry.yarnpkg.com/kareem/-/kareem-2.6.3.tgz#23168ec8ffb6c1abfd31b7169a6fb1dd285992ac"
+ integrity sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==
keyv@^3.0.0:
version "3.1.0"
@@ -4318,30 +5101,6 @@ keyv@^3.0.0:
dependencies:
json-buffer "3.0.0"
-kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
- version "3.2.2"
- resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
- integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=
- dependencies:
- is-buffer "^1.1.5"
-
-kind-of@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57"
- integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc=
- dependencies:
- is-buffer "^1.1.5"
-
-kind-of@^5.0.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d"
- integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==
-
-kind-of@^6.0.0, kind-of@^6.0.2:
- version "6.0.3"
- resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
- integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
-
kleur@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
@@ -4379,14 +5138,6 @@ levn@^0.4.1:
prelude-ls "^1.2.1"
type-check "~0.4.0"
-levn@~0.3.0:
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
- integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=
- dependencies:
- prelude-ls "~1.1.2"
- type-check "~0.3.2"
-
lines-and-columns@^1.1.6:
version "1.1.6"
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
@@ -4425,13 +5176,6 @@ locate-path@^5.0.0:
dependencies:
p-locate "^4.1.0"
-lockfile@^1.0.4:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/lockfile/-/lockfile-1.0.4.tgz#07f819d25ae48f87e538e6578b6964a4981a5609"
- integrity sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA==
- dependencies:
- signal-exit "^3.0.2"
-
lodash.includes@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
@@ -4467,15 +5211,10 @@ lodash.once@^4.0.0:
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=
-lodash.sortby@^4.7.0:
- version "4.7.0"
- resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
- integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
-
lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19:
- version "4.17.21"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
- integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
+ version "4.17.23"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.23.tgz#f113b0378386103be4f6893388c73d0bde7f2c5a"
+ integrity sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==
loose-envify@^1.4.0:
version "1.4.0"
@@ -4494,6 +5233,13 @@ lowercase-keys@^2.0.0:
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479"
integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==
+lru-cache@^5.1.1:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
+ integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==
+ dependencies:
+ yallist "^3.0.2"
+
lru-cache@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
@@ -4508,24 +5254,12 @@ make-dir@^3.0.0, make-dir@^3.0.2:
dependencies:
semver "^6.0.0"
-makeerror@1.0.x:
- version "1.0.11"
- resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c"
- integrity sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=
- dependencies:
- tmpl "1.0.x"
-
-map-cache@^0.2.2:
- version "0.2.2"
- resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
- integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=
-
-map-visit@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f"
- integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=
+makeerror@1.0.12:
+ version "1.0.12"
+ resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a"
+ integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==
dependencies:
- object-visit "^1.0.0"
+ tmpl "1.0.5"
md5-file@^5.0.0:
version "5.0.0"
@@ -4542,10 +5276,10 @@ memory-pager@^1.0.2:
resolved "https://registry.yarnpkg.com/memory-pager/-/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5"
integrity sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==
-merge-descriptors@1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
- integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=
+merge-descriptors@1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5"
+ integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==
merge-stream@^2.0.0:
version "2.0.0"
@@ -4557,45 +5291,38 @@ methods@^1.1.1, methods@^1.1.2, methods@~1.1.2:
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
-micromatch@^3.1.4:
- version "3.1.10"
- resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23"
- integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==
- dependencies:
- arr-diff "^4.0.0"
- array-unique "^0.3.2"
- braces "^2.3.1"
- define-property "^2.0.2"
- extend-shallow "^3.0.2"
- extglob "^2.0.4"
- fragment-cache "^0.2.1"
- kind-of "^6.0.2"
- nanomatch "^1.2.9"
- object.pick "^1.3.0"
- regex-not "^1.0.0"
- snapdragon "^0.8.1"
- to-regex "^3.0.2"
-
-micromatch@^4.0.2:
- version "4.0.2"
- resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259"
- integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==
+micromatch@^4.0.4:
+ version "4.0.8"
+ resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202"
+ integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==
dependencies:
- braces "^3.0.1"
- picomatch "^2.0.5"
+ braces "^3.0.3"
+ picomatch "^2.3.1"
mime-db@1.44.0:
version "1.44.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92"
integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==
-mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24:
+mime-db@1.52.0:
+ version "1.52.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
+ integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
+
+mime-types@^2.1.12, mime-types@~2.1.24:
version "2.1.27"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f"
integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==
dependencies:
mime-db "1.44.0"
+mime-types@~2.1.34:
+ version "2.1.35"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
+ integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
+ dependencies:
+ mime-db "1.52.0"
+
mime@1.6.0, mime@^1.4.1:
version "1.6.0"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
@@ -4623,19 +5350,11 @@ minimatch@^3.0.4:
dependencies:
brace-expansion "^1.1.7"
-minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5:
+minimist@^1.2.0, minimist@^1.2.5:
version "1.2.6"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
-mixin-deep@^1.2.0:
- version "1.3.2"
- resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566"
- integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==
- dependencies:
- for-in "^1.0.2"
- is-extendable "^1.0.1"
-
mkdirp@^0.5.1:
version "0.5.5"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
@@ -4643,117 +5362,89 @@ mkdirp@^0.5.1:
dependencies:
minimist "^1.2.5"
-mkdirp@^1.0.4:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
- integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
-
-mongodb-memory-server-core@6.6.7:
- version "6.6.7"
- resolved "https://registry.yarnpkg.com/mongodb-memory-server-core/-/mongodb-memory-server-core-6.6.7.tgz#f402bb5808f052e20d040cd9ce03a64dde94fc62"
- integrity sha512-21g2FpQdgqN3sFsj5lbGje1BhrSRGNHgz6gMAl8bvmdpRpoZErclkImVtjBXNHCNmCc1Dxr+EBvH11KaVE+9iQ==
- dependencies:
- "@types/cross-spawn" "^6.0.2"
- "@types/debug" "^4.1.5"
- "@types/dedent" "^0.7.0"
- "@types/find-cache-dir" "^3.2.0"
- "@types/find-package-json" "^1.1.1"
- "@types/lockfile" "^1.0.1"
- "@types/md5-file" "^4.0.2"
- "@types/mkdirp" "^1.0.1"
- "@types/semver" "^7.3.3"
- "@types/tmp" "^0.2.0"
- "@types/uuid" "^8.0.0"
- camelcase "^6.0.0"
- cross-spawn "^7.0.3"
- debug "^4.1.1"
- find-cache-dir "^3.3.1"
- find-package-json "^1.2.0"
- get-port "^5.1.1"
- https-proxy-agent "^5.0.0"
- lockfile "^1.0.4"
- md5-file "^5.0.0"
- mkdirp "^1.0.4"
- semver "^7.3.2"
- tar-stream "^2.1.3"
- tmp "^0.2.1"
- uuid "^8.2.0"
- yauzl "^2.10.0"
- optionalDependencies:
- mongodb "^3.5.9"
+mockdate@^3.0.5:
+ version "3.0.5"
+ resolved "https://registry.yarnpkg.com/mockdate/-/mockdate-3.0.5.tgz#789be686deb3149e7df2b663d2bc4392bc3284fb"
+ integrity sha512-iniQP4rj1FhBdBYS/+eQv7j1tadJ9lJtdzgOpvsOHng/GbcDh2Fhdeq+ZRldrPYdXvCyfFUmFeEwEGXZB5I/AQ==
-mongodb-memory-server-core@6.9.2:
- version "6.9.2"
- resolved "https://registry.yarnpkg.com/mongodb-memory-server-core/-/mongodb-memory-server-core-6.9.2.tgz#a064602e85c065c63776cef20ec7311d2b2da206"
- integrity sha512-0naMEESKsJNBg4/djN9qc+Argmg5UElJ/EFP9M4opTH//GZ1Rn6SI5S43NFHJrizOPGojAAp21gn7rNOru7Ypw==
+mongodb-connection-string-url@^2.6.0:
+ version "2.6.0"
+ resolved "https://registry.yarnpkg.com/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz#57901bf352372abdde812c81be47b75c6b2ec5cf"
+ integrity sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==
dependencies:
- "@types/tmp" "^0.2.0"
- camelcase "^6.0.0"
- cross-spawn "^7.0.3"
- debug "^4.2.0"
- find-cache-dir "^3.3.1"
- find-package-json "^1.2.0"
+ "@types/whatwg-url" "^8.2.1"
+ whatwg-url "^11.0.0"
+
+mongodb-connection-string-url@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz#e223089dfa0a5fa9bf505f8aedcbc67b077b33e7"
+ integrity sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==
+ dependencies:
+ "@types/whatwg-url" "^11.0.2"
+ whatwg-url "^14.1.0 || ^13.0.0"
+
+mongodb-memory-server-core@8.16.1:
+ version "8.16.1"
+ resolved "https://registry.yarnpkg.com/mongodb-memory-server-core/-/mongodb-memory-server-core-8.16.1.tgz#dd96945dc42f0780797f5af5828aed2b70978888"
+ integrity sha512-skRGr7vzVIyefKm/YTn73sWI/7ghIb+gBxYNt42kGO7zeOfy+3S2Xg3kHYLkBz1IrOmTyV2HpFVzbZ1HF8grsQ==
+ dependencies:
+ async-mutex "^0.3.2"
+ camelcase "^6.3.0"
+ debug "^4.3.4"
+ find-cache-dir "^3.3.2"
+ follow-redirects "^1.15.2"
get-port "^5.1.1"
- https-proxy-agent "^5.0.0"
- lockfile "^1.0.4"
+ https-proxy-agent "^5.0.1"
md5-file "^5.0.0"
- mkdirp "^1.0.4"
- semver "^7.3.2"
+ mongodb "^4.16.0"
+ new-find-package-json "^2.0.0"
+ semver "^7.5.4"
tar-stream "^2.1.4"
- tmp "^0.2.1"
- uuid "8.3.0"
+ tslib "^2.6.1"
+ uuid "^9.0.0"
yauzl "^2.10.0"
- optionalDependencies:
- mongodb "3.6.2"
-
-mongodb-memory-server@6.6.7:
- version "6.6.7"
- resolved "https://registry.yarnpkg.com/mongodb-memory-server/-/mongodb-memory-server-6.6.7.tgz#fa5f1e8153f4248c7c166f99b5143662699f40d6"
- integrity sha512-azRGr5csTAl0MCLR/amPCJrmV5TFwRcVtal56dHrPy1o2T8wZRc3AaJyukob8a/JP38JYa/pQnw1AQH7lFA2Cg==
- dependencies:
- mongodb-memory-server-core "6.6.7"
-mongodb-memory-server@^6.9.0:
- version "6.9.2"
- resolved "https://registry.yarnpkg.com/mongodb-memory-server/-/mongodb-memory-server-6.9.2.tgz#75880bf5f485deceba2d7df20659b2796ff703cf"
- integrity sha512-+8axA5PlO+C3H+kgsxt6+6edcKAaY56YjYt+MWj9t1ZiKsEr+7SPsQfJcEoX+Kiz802jt1BOOIbYQVLX+08Hag==
+mongodb-memory-server@^8.2.3:
+ version "8.16.1"
+ resolved "https://registry.yarnpkg.com/mongodb-memory-server/-/mongodb-memory-server-8.16.1.tgz#acbbd2b50d35127e039d27df9d81b44c9ff80aea"
+ integrity sha512-Zje3i+xKN+nxALkOOraDfIvc9X8mNy979IvJdjUghvf5PbwvX5ZPr5gUtCcmzz2VRj97WsZbdUSkxny+GXZTIA==
dependencies:
- mongodb-memory-server-core "6.9.2"
+ mongodb-memory-server-core "8.16.1"
+ tslib "^2.6.1"
-mongodb@3.6.2, mongodb@^3.5.9:
- version "3.6.2"
- resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-3.6.2.tgz#1154a4ac107bf1375112d83a29c5cf97704e96b6"
- integrity sha512-sSZOb04w3HcnrrXC82NEh/YGCmBuRgR+C1hZgmmv4L6dBz4BkRse6Y8/q/neXer9i95fKUBbFi4KgeceXmbsOA==
+mongodb@^4.16.0:
+ version "4.17.2"
+ resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-4.17.2.tgz#237c0534e36a3449bd74c6bf6d32f87a1ca7200c"
+ integrity sha512-mLV7SEiov2LHleRJPMPrK2PMyhXFZt2UQLC4VD4pnth3jMjYKHhtqfwwkkvS/NXuo/Fp3vbhaNcXrIDaLRb9Tg==
dependencies:
- bl "^2.2.1"
- bson "^1.1.4"
- denque "^1.4.1"
- require_optional "^1.0.1"
- safe-buffer "^5.1.2"
+ bson "^4.7.2"
+ mongodb-connection-string-url "^2.6.0"
+ socks "^2.7.1"
optionalDependencies:
- saslprep "^1.0.0"
-
-mongoose-legacy-pluralize@1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz#3ba9f91fa507b5186d399fb40854bff18fb563e4"
- integrity sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==
-
-mongoose@^5.10.0:
- version "5.10.9"
- resolved "https://registry.yarnpkg.com/mongoose/-/mongoose-5.10.9.tgz#23b74debc86d2055cee4fe77f962a9c8a286cdad"
- integrity sha512-7dkr1d6Uyk87hELzoc6B7Zo7kkPTx8rKummk51Y0je2V2Ttsw0KFPwTp1G8JIbBta7Wpw8j15PJi0d33Ode2nw==
- dependencies:
- bson "^1.1.4"
- kareem "2.3.1"
- mongodb "3.6.2"
- mongoose-legacy-pluralize "1.0.2"
- mpath "0.7.0"
- mquery "3.2.2"
- ms "2.1.2"
- regexp-clone "1.0.0"
- safe-buffer "5.2.1"
- sift "7.0.1"
- sliced "1.0.1"
+ "@aws-sdk/credential-providers" "^3.186.0"
+ "@mongodb-js/saslprep" "^1.1.0"
+
+mongodb@~6.20.0:
+ version "6.20.0"
+ resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-6.20.0.tgz#5212dcf512719385287aa4574265352eefb01d8e"
+ integrity sha512-Tl6MEIU3K4Rq3TSHd+sZQqRBoGlFsOgNrH5ltAcFBV62Re3Fd+FcaVf8uSEQFOJ51SDowDVttBTONMfoYWrWlQ==
+ dependencies:
+ "@mongodb-js/saslprep" "^1.3.0"
+ bson "^6.10.4"
+ mongodb-connection-string-url "^3.0.2"
+
+mongoose@^8.9.5:
+ version "8.21.0"
+ resolved "https://registry.yarnpkg.com/mongoose/-/mongoose-8.21.0.tgz#e4b940a6b22c2fc176916667766f34656e352906"
+ integrity sha512-dW2U01gN8EVQT5KAO5AkzjbqWc8A/CsEq15jOzq/M9ISpy8jw3iq7W9ZP135h9zykFOMt3AMxq4+anvt2YNJgw==
+ dependencies:
+ bson "^6.10.4"
+ kareem "2.6.3"
+ mongodb "~6.20.0"
+ mpath "0.9.0"
+ mquery "5.0.0"
+ ms "2.1.3"
+ sift "17.1.3"
morgan@^1.10.0:
version "1.10.0"
@@ -4766,21 +5457,17 @@ morgan@^1.10.0:
on-finished "~2.3.0"
on-headers "~1.0.2"
-mpath@0.7.0:
- version "0.7.0"
- resolved "https://registry.yarnpkg.com/mpath/-/mpath-0.7.0.tgz#20e8102e276b71709d6e07e9f8d4d0f641afbfb8"
- integrity sha512-Aiq04hILxhz1L+f7sjGyn7IxYzWm1zLNNXcfhDtx04kZ2Gk7uvFdgZ8ts1cWa/6d0TQmag2yR8zSGZUmp0tFNg==
+mpath@0.9.0:
+ version "0.9.0"
+ resolved "https://registry.yarnpkg.com/mpath/-/mpath-0.9.0.tgz#0c122fe107846e31fc58c75b09c35514b3871904"
+ integrity sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==
-mquery@3.2.2:
- version "3.2.2"
- resolved "https://registry.yarnpkg.com/mquery/-/mquery-3.2.2.tgz#e1383a3951852ce23e37f619a9b350f1fb3664e7"
- integrity sha512-XB52992COp0KP230I3qloVUbkLUxJIu328HBP2t2EsxSFtf4W1HPSOBWOXf1bqxK4Xbb66lfMJ+Bpfd9/yZE1Q==
+mquery@5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/mquery/-/mquery-5.0.0.tgz#a95be5dfc610b23862df34a47d3e5d60e110695d"
+ integrity sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==
dependencies:
- bluebird "3.5.1"
- debug "3.1.0"
- regexp-clone "^1.0.0"
- safe-buffer "5.1.2"
- sliced "1.0.1"
+ debug "4.x"
mri@^1.1.5:
version "1.1.6"
@@ -4792,16 +5479,16 @@ ms@2.0.0:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
-ms@2.1.1:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
- integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
-
ms@2.1.2, ms@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
+ms@2.1.3, ms@^2.1.3:
+ version "2.1.3"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
+ integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+
multimatch@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-4.0.0.tgz#8c3c0f6e3e8449ada0af3dd29efb491a375191b3"
@@ -4813,37 +5500,22 @@ multimatch@^4.0.0:
arrify "^2.0.1"
minimatch "^3.0.4"
-nanomatch@^1.2.9:
- version "1.2.13"
- resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
- integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==
- dependencies:
- arr-diff "^4.0.0"
- array-unique "^0.3.2"
- define-property "^2.0.2"
- extend-shallow "^3.0.2"
- fragment-cache "^0.2.1"
- is-windows "^1.0.2"
- kind-of "^6.0.2"
- object.pick "^1.3.0"
- regex-not "^1.0.0"
- snapdragon "^0.8.1"
- to-regex "^3.0.1"
-
natural-compare@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
-negotiator@0.6.2:
- version "0.6.2"
- resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
- integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
+negotiator@0.6.3:
+ version "0.6.3"
+ resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
+ integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
-nice-try@^1.0.4:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
- integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
+new-find-package-json@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/new-find-package-json/-/new-find-package-json-2.0.0.tgz#96553638781db35061f351e8ccb4d07126b6407d"
+ integrity sha512-lDcBsjBSMlj3LXH2v/FW3txlh2pYTjmbOXPYJD93HI5EwuLzI11tdHSIpUMmfq/IOsldj4Ps8M8flhm+pCK4Ew==
+ dependencies:
+ debug "^4.3.4"
nocache@2.1.0:
version "2.1.0"
@@ -4875,32 +5547,20 @@ node-int64@^0.4.0:
resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=
-node-modules-regexp@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40"
- integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=
-
-node-notifier@^8.0.0:
- version "8.0.1"
- resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-8.0.1.tgz#f86e89bbc925f2b068784b31f382afdc6ca56be1"
- integrity sha512-BvEXF+UmsnAfYfoapKM9nGxnP+Wn7P91YfXmrKnfcYCx6VBeoN5Ez5Ogck6I8Bi5k4RlpqRYaw75pAwzX9OphA==
- dependencies:
- growly "^1.3.0"
- is-wsl "^2.2.0"
- semver "^7.3.2"
- shellwords "^0.1.1"
- uuid "^8.3.0"
- which "^2.0.2"
-
node-releases@^1.1.75:
version "1.1.75"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.75.tgz#6dd8c876b9897a1b8e5a02de26afa79bb54ebbfe"
integrity sha512-Qe5OUajvqrqDSy6wrWFmMwfJ0jVgwiw4T3KqmbTcZ62qW0gQkheXYhcFM1+lOVcGUoRxcEcfyvFMAnDgaF1VWw==
-nodemailer@^6.6.1:
- version "6.6.1"
- resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.6.1.tgz#2a05fbf205b897d71bf43884167b5d4d3bd01b99"
- integrity sha512-1xzFN3gqv+/qJ6YRyxBxfTYstLNt0FCtZaFRvf4Sg9wxNGWbwFmGXVpfSi6ThGK6aRxAo+KjHtYSW8NvCsNSAg==
+node-releases@^2.0.27:
+ version "2.0.27"
+ resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.27.tgz#eedca519205cf20f650f61d56b070db111231e4e"
+ integrity sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==
+
+nodemailer@^7.0.11:
+ version "7.0.11"
+ resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-7.0.11.tgz#5f7b06afaec20073cff36bea92d1c7395cc3e512"
+ integrity sha512-gnXhNRE0FNhD7wPSCGhdNh46Hs6nm+uTyg+Kq0cZukNQiYdnCsoQjodNP9BQVG9XrcK/v6/MgpAPBUFyzh9pvw==
nodemon@^2.0.2:
version "2.0.4"
@@ -4925,7 +5585,7 @@ nopt@~1.0.10:
dependencies:
abbrev "1"
-normalize-package-data@^2.3.2, normalize-package-data@^2.5.0:
+normalize-package-data@^2.3.2:
version "2.5.0"
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==
@@ -4935,13 +5595,6 @@ normalize-package-data@^2.3.2, normalize-package-data@^2.5.0:
semver "2 || 3 || 4 || 5"
validate-npm-package-license "^3.0.1"
-normalize-path@^2.1.1:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9"
- integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=
- dependencies:
- remove-trailing-separator "^1.0.1"
-
normalize-path@^3.0.0, normalize-path@~3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
@@ -4952,43 +5605,22 @@ normalize-url@^4.1.0:
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a"
integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==
-npm-run-path@^2.0.0:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
- integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=
- dependencies:
- path-key "^2.0.0"
-
-npm-run-path@^4.0.0:
+npm-run-path@^4.0.0, npm-run-path@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==
dependencies:
path-key "^3.0.0"
-nwsapi@^2.2.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7"
- integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==
-
-oauth-sign@~0.9.0:
- version "0.9.0"
- resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
- integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
-
object-assign@^4, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
-object-copy@^0.1.0:
- version "0.1.0"
- resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c"
- integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw=
- dependencies:
- copy-descriptor "^0.1.0"
- define-property "^0.2.5"
- kind-of "^3.0.3"
+object-inspect@^1.13.1:
+ version "1.13.2"
+ resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff"
+ integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==
object-inspect@^1.8.0:
version "1.8.0"
@@ -5000,13 +5632,6 @@ object-keys@^1.0.12, object-keys@^1.1.1:
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
-object-visit@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb"
- integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=
- dependencies:
- isobject "^3.0.0"
-
object.assign@^4.1.0, object.assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.1.tgz#303867a666cdd41936ecdedfb1f8f3e32a478cdd"
@@ -5036,13 +5661,6 @@ object.fromentries@^2.0.2:
function-bind "^1.1.1"
has "^1.0.3"
-object.pick@^1.3.0:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747"
- integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=
- dependencies:
- isobject "^3.0.1"
-
object.values@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e"
@@ -5053,6 +5671,13 @@ object.values@^1.1.1:
function-bind "^1.1.1"
has "^1.0.3"
+on-finished@2.4.1:
+ version "2.4.1"
+ resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f"
+ integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==
+ dependencies:
+ ee-first "1.1.1"
+
on-finished@~2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
@@ -5072,7 +5697,7 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0:
dependencies:
wrappy "1"
-onetime@^5.1.0:
+onetime@^5.1.0, onetime@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
@@ -5084,18 +5709,6 @@ opencollective-postinstall@^2.0.0:
resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz#7a0fff978f6dbfa4d006238fbac98ed4198c3259"
integrity sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==
-optionator@^0.8.1:
- version "0.8.3"
- resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495"
- integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==
- dependencies:
- deep-is "~0.1.3"
- fast-levenshtein "~2.0.6"
- levn "~0.3.0"
- prelude-ls "~1.1.2"
- type-check "~0.3.2"
- word-wrap "~1.2.3"
-
optionator@^0.9.1:
version "0.9.1"
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499"
@@ -5113,16 +5726,6 @@ p-cancelable@^1.0.0:
resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc"
integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==
-p-each-series@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.1.0.tgz#961c8dd3f195ea96c747e636b262b800a6b1af48"
- integrity sha512-ZuRs1miPT4HrjFa+9fRfOFXxGJfORgelKV9f9nNOWw2gl6gVsRaVDOQP0+MI0G0wGKns1Yacsu0GjOFbTK0JFQ==
-
-p-finally@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
- integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=
-
p-limit@^1.1.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8"
@@ -5135,7 +5738,14 @@ p-limit@^2.0.0, p-limit@^2.2.0:
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
dependencies:
- p-try "^2.0.0"
+ p-try "^2.0.0"
+
+p-limit@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
+ integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
+ dependencies:
+ yocto-queue "^0.1.0"
p-locate@^2.0.0:
version "2.0.0"
@@ -5213,31 +5823,21 @@ parse-json@^4.0.0:
error-ex "^1.3.1"
json-parse-better-errors "^1.0.1"
-parse-json@^5.0.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.1.0.tgz#f96088cdf24a8faa9aea9a009f2d9d942c999646"
- integrity sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==
+parse-json@^5.2.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
+ integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==
dependencies:
"@babel/code-frame" "^7.0.0"
error-ex "^1.3.1"
json-parse-even-better-errors "^2.3.0"
lines-and-columns "^1.1.6"
-parse5@5.1.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178"
- integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==
-
parseurl@~1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
-pascalcase@^0.1.1:
- version "0.1.1"
- resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"
- integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=
-
path-exists@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
@@ -5253,11 +5853,6 @@ path-is-absolute@^1.0.0:
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
-path-key@^2.0.0, path-key@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
- integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
-
path-key@^3.0.0, path-key@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
@@ -5268,10 +5863,15 @@ path-parse@^1.0.6:
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
-path-to-regexp@0.1.7:
- version "0.1.7"
- resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
- integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
+path-parse@^1.0.7:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
+ integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
+
+path-to-regexp@0.1.10:
+ version "0.1.10"
+ resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.10.tgz#67e9108c5c0551b9e5326064387de4763c4d5f8b"
+ integrity sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==
path-type@^2.0.0:
version "2.0.0"
@@ -5285,16 +5885,21 @@ pend@~1.2.0:
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA=
-performance-now@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
- integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
+picocolors@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
+ integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
-picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1:
+picomatch@^2.0.4, picomatch@^2.2.1:
version "2.2.2"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==
+picomatch@^2.2.3, picomatch@^2.3.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
+ integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
+
pify@^2.0.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
@@ -5305,12 +5910,10 @@ pify@^3.0.0:
resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=
-pirates@^4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87"
- integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==
- dependencies:
- node-modules-regexp "^1.0.0"
+pirates@^4.0.4:
+ version "4.0.7"
+ resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.7.tgz#643b4a18c4257c8a65104b73f3049ce9a0a15e22"
+ integrity sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==
pkg-dir@^2.0.0:
version "2.0.0"
@@ -5333,21 +5936,11 @@ please-upgrade-node@^3.2.0:
dependencies:
semver-compare "^1.0.0"
-posix-character-classes@^0.1.0:
- version "0.1.1"
- resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
- integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
-
prelude-ls@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
-prelude-ls@~1.1.2:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
- integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=
-
prepend-http@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
@@ -5358,15 +5951,14 @@ prettier@^2.1.1:
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.1.2.tgz#3050700dae2e4c8b67c4c3f666cdb8af405e1ce5"
integrity sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg==
-pretty-format@^26.5.2:
- version "26.5.2"
- resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.5.2.tgz#5d896acfdaa09210683d34b6dc0e6e21423cd3e1"
- integrity sha512-VizyV669eqESlkOikKJI8Ryxl/kPpbdLwNdPs2GrbQs18MpySB5S0Yo0N7zkg2xTRiFq4CFw8ct5Vg4a0xP0og==
+pretty-format@^29.7.0:
+ version "29.7.0"
+ resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812"
+ integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==
dependencies:
- "@jest/types" "^26.5.2"
- ansi-regex "^5.0.0"
- ansi-styles "^4.0.0"
- react-is "^16.12.0"
+ "@jest/schemas" "^29.6.3"
+ ansi-styles "^5.0.0"
+ react-is "^18.0.0"
pretty-quick@^3.0.2:
version "3.0.2"
@@ -5418,19 +6010,14 @@ prop-types@^15.7.2:
object-assign "^4.1.1"
react-is "^16.8.1"
-proxy-addr@~2.0.5:
- version "2.0.6"
- resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf"
- integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==
+proxy-addr@~2.0.7:
+ version "2.0.7"
+ resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
+ integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==
dependencies:
- forwarded "~0.1.2"
+ forwarded "0.2.0"
ipaddr.js "1.9.1"
-psl@^1.1.28:
- version "1.8.0"
- resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24"
- integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==
-
pstree.remy@^1.1.7:
version "1.1.8"
resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a"
@@ -5449,6 +6036,11 @@ punycode@^2.1.0, punycode@^2.1.1:
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
+punycode@^2.3.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
+ integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
+
pupa@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.0.1.tgz#dbdc9ff48ffbea4a26a069b6f9f7abb051008726"
@@ -5456,33 +6048,37 @@ pupa@^2.0.1:
dependencies:
escape-goat "^2.0.0"
-qs@6.7.0:
- version "6.7.0"
- resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
- integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
+pure-rand@^6.0.0:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2"
+ integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==
-qs@^6.5.1, qs@^6.7.0:
- version "6.9.4"
- resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.4.tgz#9090b290d1f91728d3c22e54843ca44aea5ab687"
- integrity sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==
+qs@6.11.0:
+ version "6.11.0"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a"
+ integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==
+ dependencies:
+ side-channel "^1.0.4"
-qs@~6.5.2:
- version "6.5.2"
- resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
- integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
+qs@6.13.0, qs@^6.5.1, qs@^6.7.0:
+ version "6.13.0"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906"
+ integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==
+ dependencies:
+ side-channel "^1.0.6"
range-parser@~1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
-raw-body@2.4.0:
- version "2.4.0"
- resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332"
- integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==
+raw-body@2.5.2:
+ version "2.5.2"
+ resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a"
+ integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==
dependencies:
- bytes "3.1.0"
- http-errors "1.7.2"
+ bytes "3.1.2"
+ http-errors "2.0.0"
iconv-lite "0.4.24"
unpipe "1.0.0"
@@ -5506,11 +6102,16 @@ rc@^1.2.8:
minimist "^1.2.0"
strip-json-comments "~2.0.1"
-react-is@^16.12.0, react-is@^16.8.1:
+react-is@^16.8.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
+react-is@^18.0.0:
+ version "18.3.1"
+ resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e"
+ integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==
+
read-pkg-up@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be"
@@ -5519,15 +6120,6 @@ read-pkg-up@^2.0.0:
find-up "^2.0.0"
read-pkg "^2.0.0"
-read-pkg-up@^7.0.1:
- version "7.0.1"
- resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507"
- integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==
- dependencies:
- find-up "^4.1.0"
- read-pkg "^5.2.0"
- type-fest "^0.8.1"
-
read-pkg@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8"
@@ -5546,16 +6138,6 @@ read-pkg@^4.0.1:
parse-json "^4.0.0"
pify "^3.0.0"
-read-pkg@^5.2.0:
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc"
- integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==
- dependencies:
- "@types/normalize-package-data" "^2.4.0"
- normalize-package-data "^2.5.0"
- parse-json "^5.0.0"
- type-fest "^0.6.0"
-
readable-stream@^2.3.5:
version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
@@ -5595,19 +6177,6 @@ regenerator-runtime@^0.13.4:
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==
-regex-not@^1.0.0, regex-not@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"
- integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==
- dependencies:
- extend-shallow "^3.0.2"
- safe-regex "^1.1.0"
-
-regexp-clone@1.0.0, regexp-clone@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/regexp-clone/-/regexp-clone-1.0.0.tgz#222db967623277056260b992626354a04ce9bf63"
- integrity sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw==
-
regexp.prototype.flags@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75"
@@ -5635,63 +6204,6 @@ registry-url@^5.0.0:
dependencies:
rc "^1.2.8"
-remove-trailing-separator@^1.0.1:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef"
- integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8=
-
-repeat-element@^1.1.2:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce"
- integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==
-
-repeat-string@^1.6.1:
- version "1.6.1"
- resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
- integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=
-
-request-promise-core@1.1.4:
- version "1.1.4"
- resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f"
- integrity sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==
- dependencies:
- lodash "^4.17.19"
-
-request-promise-native@^1.0.8:
- version "1.0.9"
- resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz#e407120526a5efdc9a39b28a5679bf47b9d9dc28"
- integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==
- dependencies:
- request-promise-core "1.1.4"
- stealthy-require "^1.1.1"
- tough-cookie "^2.3.3"
-
-request@^2.88.2:
- version "2.88.2"
- resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
- integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
- dependencies:
- aws-sign2 "~0.7.0"
- aws4 "^1.8.0"
- caseless "~0.12.0"
- combined-stream "~1.0.6"
- extend "~3.0.2"
- forever-agent "~0.6.1"
- form-data "~2.3.2"
- har-validator "~5.1.3"
- http-signature "~1.2.0"
- is-typedarray "~1.0.0"
- isstream "~0.1.2"
- json-stringify-safe "~5.0.1"
- mime-types "~2.1.19"
- oauth-sign "~0.9.0"
- performance-now "^2.1.0"
- qs "~6.5.2"
- safe-buffer "^5.1.2"
- tough-cookie "~2.5.0"
- tunnel-agent "^0.6.0"
- uuid "^3.3.2"
-
require-directory@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
@@ -5702,14 +6214,6 @@ require-main-filename@^2.0.0:
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
-require_optional@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/require_optional/-/require_optional-1.0.1.tgz#4cf35a4247f64ca3df8c2ef208cc494b1ca8fc2e"
- integrity sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==
- dependencies:
- resolve-from "^2.0.0"
- semver "^5.1.0"
-
resolve-cwd@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
@@ -5717,11 +6221,6 @@ resolve-cwd@^3.0.0:
dependencies:
resolve-from "^5.0.0"
-resolve-from@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-2.0.0.tgz#9480ab20e94ffa1d9e80a804c7ea147611966b57"
- integrity sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=
-
resolve-from@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
@@ -5732,18 +6231,27 @@ resolve-from@^5.0.0:
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
-resolve-url@^0.2.1:
- version "0.2.1"
- resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
- integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
+resolve.exports@^2.0.0:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.3.tgz#41955e6f1b4013b7586f873749a635dea07ebe3f"
+ integrity sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==
-resolve@^1.10.0, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.3.2:
+resolve@^1.10.0, resolve@^1.13.1, resolve@^1.17.0:
version "1.17.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444"
integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==
dependencies:
path-parse "^1.0.6"
+resolve@^1.20.0:
+ version "1.22.11"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.11.tgz#aad857ce1ffb8bfa9b0b1ac29f1156383f68c262"
+ integrity sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==
+ dependencies:
+ is-core-module "^2.16.1"
+ path-parse "^1.0.7"
+ supports-preserve-symlinks-flag "^1.0.0"
+
responselike@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7"
@@ -5751,11 +6259,6 @@ responselike@^1.0.2:
dependencies:
lowercase-keys "^1.0.0"
-ret@~0.1.10:
- version "0.1.15"
- resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
- integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
-
retry@^0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
@@ -5768,18 +6271,6 @@ rimraf@2.6.3:
dependencies:
glob "^7.1.3"
-rimraf@^3.0.0:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
- integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
- dependencies:
- glob "^7.1.3"
-
-rsvp@^4.8.4:
- version "4.8.5"
- resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734"
- integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==
-
rxjs@^6.5.2:
version "6.6.3"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552"
@@ -5792,52 +6283,16 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
-safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
+safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@~5.2.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
-safe-regex@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e"
- integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4=
- dependencies:
- ret "~0.1.10"
-
-"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
+"safer-buffer@>= 2.1.2 < 3":
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
-sane@^4.0.3:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded"
- integrity sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==
- dependencies:
- "@cnakazawa/watch" "^1.0.3"
- anymatch "^2.0.0"
- capture-exit "^2.0.0"
- exec-sh "^0.3.2"
- execa "^1.0.0"
- fb-watchman "^2.0.0"
- micromatch "^3.1.4"
- minimist "^1.1.1"
- walker "~1.0.5"
-
-saslprep@^1.0.0:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/saslprep/-/saslprep-1.0.3.tgz#4c02f946b56cf54297e347ba1093e7acac4cf226"
- integrity sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==
- dependencies:
- sparse-bitfield "^3.0.3"
-
-saxes@^5.0.0:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d"
- integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==
- dependencies:
- xmlchars "^2.2.0"
-
semver-compare@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
@@ -5850,78 +6305,102 @@ semver-diff@^3.1.1:
dependencies:
semver "^6.3.0"
-"semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0, semver@^5.7.1:
- version "5.7.1"
- resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
- integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
+"semver@2 || 3 || 4 || 5", semver@^5.6.0, semver@^5.7.1:
+ version "5.7.2"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8"
+ integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==
-semver@^6.0.0, semver@^6.2.0, semver@^6.3.0:
- version "6.3.0"
- resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
- integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
+semver@^6.0.0, semver@^6.2.0, semver@^6.3.0, semver@^6.3.1:
+ version "6.3.1"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
+ integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
-semver@^7.2.1, semver@^7.3.2:
- version "7.3.4"
- resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97"
- integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==
+semver@^7.2.1:
+ version "7.5.4"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
+ integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
dependencies:
lru-cache "^6.0.0"
-send@0.17.1:
- version "0.17.1"
- resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8"
- integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==
+semver@^7.5.3, semver@^7.5.4:
+ version "7.7.3"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946"
+ integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==
+
+send@0.18.0:
+ version "0.18.0"
+ resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be"
+ integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==
dependencies:
debug "2.6.9"
- depd "~1.1.2"
- destroy "~1.0.4"
+ depd "2.0.0"
+ destroy "1.2.0"
encodeurl "~1.0.2"
escape-html "~1.0.3"
etag "~1.8.1"
fresh "0.5.2"
- http-errors "~1.7.2"
+ http-errors "2.0.0"
mime "1.6.0"
- ms "2.1.1"
- on-finished "~2.3.0"
+ ms "2.1.3"
+ on-finished "2.4.1"
range-parser "~1.2.1"
- statuses "~1.5.0"
+ statuses "2.0.1"
-serve-static@1.14.1:
- version "1.14.1"
- resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9"
- integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==
+send@0.19.0:
+ version "0.19.0"
+ resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8"
+ integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==
+ dependencies:
+ debug "2.6.9"
+ depd "2.0.0"
+ destroy "1.2.0"
+ encodeurl "~1.0.2"
+ escape-html "~1.0.3"
+ etag "~1.8.1"
+ fresh "0.5.2"
+ http-errors "2.0.0"
+ mime "1.6.0"
+ ms "2.1.3"
+ on-finished "2.4.1"
+ range-parser "~1.2.1"
+ statuses "2.0.1"
+
+serve-static@1.16.0:
+ version "1.16.0"
+ resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.0.tgz#2bf4ed49f8af311b519c46f272bf6ac3baf38a92"
+ integrity sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA==
dependencies:
encodeurl "~1.0.2"
escape-html "~1.0.3"
parseurl "~1.3.3"
- send "0.17.1"
+ send "0.18.0"
set-blocking@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
-set-value@^2.0.0, set-value@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b"
- integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==
+set-function-length@^1.2.1:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449"
+ integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==
dependencies:
- extend-shallow "^2.0.1"
- is-extendable "^0.1.1"
- is-plain-object "^2.0.3"
- split-string "^3.0.1"
+ define-data-property "^1.1.4"
+ es-errors "^1.3.0"
+ function-bind "^1.1.2"
+ get-intrinsic "^1.2.4"
+ gopd "^1.0.1"
+ has-property-descriptors "^1.0.2"
setprototypeof@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683"
integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==
-shebang-command@^1.2.0:
+setprototypeof@1.2.0:
version "1.2.0"
- resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
- integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=
- dependencies:
- shebang-regex "^1.0.0"
+ resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
+ integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
shebang-command@^2.0.0:
version "2.0.0"
@@ -5930,21 +6409,11 @@ shebang-command@^2.0.0:
dependencies:
shebang-regex "^3.0.0"
-shebang-regex@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
- integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=
-
shebang-regex@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
-shellwords@^0.1.1:
- version "0.1.1"
- resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"
- integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==
-
side-channel@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.3.tgz#cdc46b057550bbab63706210838df5d4c19519c3"
@@ -5953,21 +6422,36 @@ side-channel@^1.0.2:
es-abstract "^1.18.0-next.0"
object-inspect "^1.8.0"
-sift@7.0.1:
- version "7.0.1"
- resolved "https://registry.yarnpkg.com/sift/-/sift-7.0.1.tgz#47d62c50b159d316f1372f8b53f9c10cd21a4b08"
- integrity sha512-oqD7PMJ+uO6jV9EQCl0LrRw1OwsiPsiFQR5AR30heR+4Dl7jBBbDLnNvWiak20tzZlSE1H7RB30SX/1j/YYT7g==
+side-channel@^1.0.4, side-channel@^1.0.6:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2"
+ integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==
+ dependencies:
+ call-bind "^1.0.7"
+ es-errors "^1.3.0"
+ get-intrinsic "^1.2.4"
+ object-inspect "^1.13.1"
+
+sift@17.1.3:
+ version "17.1.3"
+ resolved "https://registry.yarnpkg.com/sift/-/sift-17.1.3.tgz#9d2000d4d41586880b0079b5183d839c7a142bf7"
+ integrity sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==
siginfo@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30"
integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==
-signal-exit@^3.0.0, signal-exit@^3.0.2:
+signal-exit@^3.0.2:
version "3.0.3"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==
+signal-exit@^3.0.3, signal-exit@^3.0.7:
+ version "3.0.7"
+ resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
+ integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
+
sisteransi@^1.0.4:
version "1.0.5"
resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"
@@ -5987,80 +6471,37 @@ slice-ansi@^2.1.0:
astral-regex "^1.0.0"
is-fullwidth-code-point "^2.0.0"
-sliced@1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/sliced/-/sliced-1.0.1.tgz#0b3a662b5d04c3177b1926bea82b03f837a2ef41"
- integrity sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=
-
-snapdragon-node@^2.0.1:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"
- integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==
- dependencies:
- define-property "^1.0.0"
- isobject "^3.0.0"
- snapdragon-util "^3.0.1"
+smart-buffer@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae"
+ integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==
-snapdragon-util@^3.0.1:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2"
- integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==
+socks@^2.7.1:
+ version "2.8.7"
+ resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.7.tgz#e2fb1d9a603add75050a2067db8c381a0b5669ea"
+ integrity sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==
dependencies:
- kind-of "^3.2.0"
+ ip-address "^10.0.1"
+ smart-buffer "^4.2.0"
-snapdragon@^0.8.1:
- version "0.8.2"
- resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d"
- integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==
- dependencies:
- base "^0.11.1"
- debug "^2.2.0"
- define-property "^0.2.5"
- extend-shallow "^2.0.1"
- map-cache "^0.2.2"
- source-map "^0.5.6"
- source-map-resolve "^0.5.0"
- use "^3.1.0"
-
-source-map-resolve@^0.5.0:
- version "0.5.3"
- resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a"
- integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==
- dependencies:
- atob "^2.1.2"
- decode-uri-component "^0.2.0"
- resolve-url "^0.2.1"
- source-map-url "^0.4.0"
- urix "^0.1.0"
-
-source-map-support@^0.5.6:
- version "0.5.19"
- resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
- integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==
+source-map-support@0.5.13:
+ version "0.5.13"
+ resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932"
+ integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==
dependencies:
buffer-from "^1.0.0"
source-map "^0.6.0"
-source-map-url@^0.4.0:
- version "0.4.0"
- resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
- integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=
-
-source-map@^0.5.0, source-map@^0.5.6:
+source-map@^0.5.0:
version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
-source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1:
+source-map@^0.6.0, source-map@^0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
-source-map@^0.7.3:
- version "0.7.3"
- resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
- integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
-
sparse-bitfield@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz#ff4ae6e68656056ba4b3e792ab3334d38273ca11"
@@ -6099,37 +6540,15 @@ spdx-license-ids@^3.0.0:
resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz#c80757383c28abf7296744998cbc106ae8b854ce"
integrity sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==
-split-string@^3.0.1, split-string@^3.0.2:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
- integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==
- dependencies:
- extend-shallow "^3.0.0"
-
sprintf-js@~1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
-sshpk@^1.7.0:
- version "1.16.1"
- resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
- integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==
- dependencies:
- asn1 "~0.2.3"
- assert-plus "^1.0.0"
- bcrypt-pbkdf "^1.0.0"
- dashdash "^1.12.0"
- ecc-jsbn "~0.1.1"
- getpass "^0.1.1"
- jsbn "~0.1.0"
- safer-buffer "^2.0.2"
- tweetnacl "~0.14.0"
-
-stack-utils@^2.0.2:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.2.tgz#5cf48b4557becb4638d0bc4f21d23f5d19586593"
- integrity sha512-0H7QK2ECz3fyZMzQ8rH0j2ykpfbnd20BFtfg/SqVC2+sCTtcw0aDTGB7dk+de4U4uUeuz6nOtJcrkFFLG1B0Rg==
+stack-utils@^2.0.3:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f"
+ integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==
dependencies:
escape-string-regexp "^2.0.0"
@@ -6138,24 +6557,16 @@ stackback@0.0.2:
resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b"
integrity sha1-Gsig2Ug4SNFpXkGLbQMaPDzmjjs=
-static-extend@^0.1.1:
- version "0.1.2"
- resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6"
- integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=
- dependencies:
- define-property "^0.2.5"
- object-copy "^0.1.0"
+statuses@2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
+ integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
-"statuses@>= 1.5.0 < 2", statuses@~1.5.0:
+"statuses@>= 1.5.0 < 2":
version "1.5.0"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
-stealthy-require@^1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b"
- integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=
-
string-length@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.1.tgz#4a973bf31ef77c4edbceadd6af2611996985f8a1"
@@ -6182,6 +6593,15 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0:
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.0"
+string-width@^4.2.3:
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+ integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+ dependencies:
+ emoji-regex "^8.0.0"
+ is-fullwidth-code-point "^3.0.0"
+ strip-ansi "^6.0.1"
+
string.prototype.matchall@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.2.tgz#48bb510326fb9fdeb6a33ceaa81a6ea04ef7648e"
@@ -6238,6 +6658,13 @@ strip-ansi@^6.0.0:
dependencies:
ansi-regex "^5.0.0"
+strip-ansi@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+ integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+ dependencies:
+ ansi-regex "^5.0.1"
+
strip-bom@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
@@ -6248,11 +6675,6 @@ strip-bom@^4.0.0:
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878"
integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==
-strip-eof@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
- integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=
-
strip-final-newline@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
@@ -6268,6 +6690,11 @@ strip-json-comments@~2.0.1:
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
+strnum@^2.1.0:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/strnum/-/strnum-2.1.2.tgz#a5e00ba66ab25f9cafa3726b567ce7a49170937a"
+ integrity sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==
+
superagent@^3.8.3:
version "3.8.3"
resolved "https://registry.yarnpkg.com/superagent/-/superagent-3.8.3.tgz#460ea0dbdb7d5b11bc4f78deba565f86a178e128"
@@ -6306,25 +6733,24 @@ supports-color@^6.1.0:
dependencies:
has-flag "^3.0.0"
-supports-color@^7.0.0, supports-color@^7.1.0:
+supports-color@^7.1.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
dependencies:
has-flag "^4.0.0"
-supports-hyperlinks@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz#f663df252af5f37c5d49bbd7eeefa9e0b9e59e47"
- integrity sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA==
+supports-color@^8.0.0:
+ version "8.1.1"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
+ integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
dependencies:
has-flag "^4.0.0"
- supports-color "^7.0.0"
-symbol-tree@^3.2.4:
- version "3.2.4"
- resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
- integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==
+supports-preserve-symlinks-flag@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
+ integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
table@^5.2.3:
version "5.4.6"
@@ -6336,7 +6762,7 @@ table@^5.2.3:
slice-ansi "^2.1.0"
string-width "^3.0.0"
-tar-stream@^2.1.3, tar-stream@^2.1.4:
+tar-stream@^2.1.4:
version "2.1.4"
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.4.tgz#c4fb1a11eb0da29b893a5b25476397ba2d053bfa"
integrity sha512-o3pS2zlG4gxr67GmFYBLlq+dM8gyRGUOvsrHclSkvtVtQbjV0s/+ZE8OpICbaj8clrX3tjeHngYGP7rweaBnuw==
@@ -6352,14 +6778,6 @@ term-size@^2.1.0:
resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.0.tgz#1f16adedfe9bdc18800e1776821734086fcc6753"
integrity sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw==
-terminal-link@^2.0.0:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994"
- integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==
- dependencies:
- ansi-escapes "^4.2.1"
- supports-hyperlinks "^2.0.0"
-
test-exclude@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e"
@@ -6374,19 +6792,7 @@ text-table@^0.2.0:
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
-throat@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b"
- integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==
-
-tmp@^0.2.1:
- version "0.2.1"
- resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14"
- integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==
- dependencies:
- rimraf "^3.0.0"
-
-tmpl@1.0.x:
+tmpl@1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc"
integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==
@@ -6396,26 +6802,11 @@ to-fast-properties@^2.0.0:
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
-to-object-path@^0.3.0:
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af"
- integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=
- dependencies:
- kind-of "^3.0.2"
-
to-readable-stream@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771"
integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==
-to-regex-range@^2.1.0:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38"
- integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=
- dependencies:
- is-number "^3.0.0"
- repeat-string "^1.6.1"
-
to-regex-range@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
@@ -6423,21 +6814,16 @@ to-regex-range@^5.0.1:
dependencies:
is-number "^7.0.0"
-to-regex@^3.0.1, to-regex@^3.0.2:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce"
- integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==
- dependencies:
- define-property "^2.0.2"
- extend-shallow "^3.0.2"
- regex-not "^1.0.2"
- safe-regex "^1.1.0"
-
toidentifier@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
+toidentifier@1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
+ integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
+
touch@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b"
@@ -6445,29 +6831,19 @@ touch@^3.1.0:
dependencies:
nopt "~1.0.10"
-tough-cookie@^2.3.3, tough-cookie@~2.5.0:
- version "2.5.0"
- resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
- integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==
- dependencies:
- psl "^1.1.28"
- punycode "^2.1.1"
-
-tough-cookie@^3.0.1:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-3.0.1.tgz#9df4f57e739c26930a018184887f4adb7dca73b2"
- integrity sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==
+tr46@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9"
+ integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==
dependencies:
- ip-regex "^2.1.0"
- psl "^1.1.28"
punycode "^2.1.1"
-tr46@^2.0.2:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.0.2.tgz#03273586def1595ae08fedb38d7733cee91d2479"
- integrity sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg==
+tr46@^5.1.0:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/tr46/-/tr46-5.1.1.tgz#96ae867cddb8fdb64a49cc3059a8d428bcf238ca"
+ integrity sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==
dependencies:
- punycode "^2.1.1"
+ punycode "^2.3.1"
tr46@~0.0.3:
version "0.0.3"
@@ -6494,23 +6870,16 @@ tslib@^1.9.0:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
+tslib@^2.3.1, tslib@^2.6.1, tslib@^2.6.2:
+ version "2.8.1"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
+ integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
+
tsscmp@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.6.tgz#85b99583ac3589ec4bfef825b5000aa911d605eb"
integrity sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==
-tunnel-agent@^0.6.0:
- version "0.6.0"
- resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
- integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=
- dependencies:
- safe-buffer "^5.0.1"
-
-tweetnacl@^0.14.3, tweetnacl@~0.14.0:
- version "0.14.5"
- resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
- integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
-
type-check@^0.4.0, type-check@~0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
@@ -6518,13 +6887,6 @@ type-check@^0.4.0, type-check@~0.4.0:
dependencies:
prelude-ls "^1.2.1"
-type-check@~0.3.2:
- version "0.3.2"
- resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
- integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=
- dependencies:
- prelude-ls "~1.1.2"
-
type-detect@4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
@@ -6535,17 +6897,12 @@ type-fest@^0.11.0:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1"
integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==
-type-fest@^0.6.0:
- version "0.6.0"
- resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b"
- integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==
-
type-fest@^0.8.1:
version "0.8.1"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
-type-is@~1.6.17, type-is@~1.6.18:
+type-is@~1.6.18:
version "1.6.18"
resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
@@ -6572,16 +6929,6 @@ undefsafe@^2.0.2:
dependencies:
debug "^2.2.0"
-union-value@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847"
- integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==
- dependencies:
- arr-union "^3.1.0"
- get-value "^2.0.6"
- is-extendable "^0.1.1"
- set-value "^2.0.1"
-
unique-string@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d"
@@ -6594,13 +6941,13 @@ unpipe@1.0.0, unpipe@~1.0.0:
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=
-unset-value@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559"
- integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=
+update-browserslist-db@^1.2.0:
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz#64d76db58713136acbeb4c49114366cc6cc2e80d"
+ integrity sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==
dependencies:
- has-value "^0.3.1"
- isobject "^3.0.0"
+ escalade "^3.2.0"
+ picocolors "^1.1.1"
update-notifier@^4.0.0:
version "4.1.3"
@@ -6628,11 +6975,6 @@ uri-js@^4.2.2:
dependencies:
punycode "^2.1.0"
-urix@^0.1.0:
- version "0.1.0"
- resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
- integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=
-
url-parse-lax@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c"
@@ -6645,11 +6987,6 @@ url-template@^2.0.8:
resolved "https://registry.yarnpkg.com/url-template/-/url-template-2.0.8.tgz#fc565a3cccbff7730c775f5641f9555791439f21"
integrity sha1-/FZaPMy/93MMd19WQflVV5FDnyE=
-use@^3.1.0:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
- integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==
-
util-deprecate@^1.0.1, util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
@@ -6660,34 +6997,29 @@ utils-merge@1.0.1:
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
-uuid@8.3.0:
- version "8.3.0"
- resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.0.tgz#ab738085ca22dc9a8c92725e459b1d507df5d6ea"
- integrity sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ==
-
-uuid@^3.3.2:
- version "3.4.0"
- resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
- integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
-
-uuid@^8.0.0, uuid@^8.2.0, uuid@^8.3.0:
+uuid@^8.0.0:
version "8.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
+uuid@^9.0.0:
+ version "9.0.1"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"
+ integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==
+
v8-compile-cache@^2.0.3:
version "2.1.1"
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz#54bc3cdd43317bca91e35dcaf305b1a7237de745"
integrity sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==
-v8-to-istanbul@^6.0.1:
- version "6.0.1"
- resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-6.0.1.tgz#7ef0e32faa10f841fe4c1b0f8de96ed067c0be1e"
- integrity sha512-PzM1WlqquhBvsV+Gco6WSFeg1AGdD53ccMRkFeyHRE/KRZaVacPOmQYP3EeVgDBtKD2BJ8kgynBQ5OtKiHCH+w==
+v8-to-istanbul@^9.0.1:
+ version "9.3.0"
+ resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz#b9572abfa62bd556c16d75fdebc1a411d5ff3175"
+ integrity sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==
dependencies:
+ "@jridgewell/trace-mapping" "^0.3.12"
"@types/istanbul-lib-coverage" "^2.0.1"
- convert-source-map "^1.6.0"
- source-map "^0.7.3"
+ convert-source-map "^2.0.0"
validate-npm-package-license@^3.0.1:
version "3.0.4"
@@ -6698,71 +7030,47 @@ validate-npm-package-license@^3.0.1:
spdx-expression-parse "^3.0.0"
validator@^13.1.1:
- version "13.7.0"
- resolved "https://registry.yarnpkg.com/validator/-/validator-13.7.0.tgz#4f9658ba13ba8f3d82ee881d3516489ea85c0857"
- integrity sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==
+ version "13.15.23"
+ resolved "https://registry.yarnpkg.com/validator/-/validator-13.15.23.tgz#59a874f84e4594588e3409ab1edbe64e96d0c62d"
+ integrity sha512-4yoz1kEWqUjzi5zsPbAS/903QXSYp0UOtHsPpp7p9rHAw/W+dkInskAE386Fat3oKRROwO98d9ZB0G4cObgUyw==
vary@^1, vary@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
-verror@1.10.0:
- version "1.10.0"
- resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
- integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=
- dependencies:
- assert-plus "^1.0.0"
- core-util-is "1.0.2"
- extsprintf "^1.2.0"
-
-w3c-hr-time@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd"
- integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==
- dependencies:
- browser-process-hrtime "^1.0.0"
-
-w3c-xmlserializer@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a"
- integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==
- dependencies:
- xml-name-validator "^3.0.0"
-
-walker@^1.0.7, walker@~1.0.5:
- version "1.0.7"
- resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb"
- integrity sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=
+walker@^1.0.8:
+ version "1.0.8"
+ resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f"
+ integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==
dependencies:
- makeerror "1.0.x"
+ makeerror "1.0.12"
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=
-webidl-conversions@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff"
- integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==
-
-webidl-conversions@^6.1.0:
- version "6.1.0"
- resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514"
- integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==
+webidl-conversions@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a"
+ integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==
-whatwg-encoding@^1.0.5:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0"
- integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==
+whatwg-url@^11.0.0:
+ version "11.0.0"
+ resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018"
+ integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==
dependencies:
- iconv-lite "0.4.24"
+ tr46 "^3.0.0"
+ webidl-conversions "^7.0.0"
-whatwg-mimetype@^2.3.0:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
- integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==
+"whatwg-url@^14.1.0 || ^13.0.0":
+ version "14.2.0"
+ resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-14.2.0.tgz#4ee02d5d725155dae004f6ae95c73e7ef5d95663"
+ integrity sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==
+ dependencies:
+ tr46 "^5.1.0"
+ webidl-conversions "^7.0.0"
whatwg-url@^5.0.0:
version "5.0.0"
@@ -6772,28 +7080,12 @@ whatwg-url@^5.0.0:
tr46 "~0.0.3"
webidl-conversions "^3.0.0"
-whatwg-url@^8.0.0:
- version "8.4.0"
- resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.4.0.tgz#50fb9615b05469591d2b2bd6dfaed2942ed72837"
- integrity sha512-vwTUFf6V4zhcPkWp/4CQPr1TW9Ml6SF4lVyaIMBdJw5i6qUUJ1QWM4Z6YYVkfka0OUIzVo/0aNtGVGk256IKWw==
- dependencies:
- lodash.sortby "^4.7.0"
- tr46 "^2.0.2"
- webidl-conversions "^6.1.0"
-
which-module@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
-which@^1.2.9:
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
- integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
- dependencies:
- isexe "^2.0.0"
-
-which@^2.0.1, which@^2.0.2:
+which@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
@@ -6815,10 +7107,10 @@ widest-line@^3.1.0:
dependencies:
string-width "^4.0.0"
-word-wrap@^1.2.3, word-wrap@~1.2.3:
- version "1.2.3"
- resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
- integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
+word-wrap@^1.2.3:
+ version "1.2.5"
+ resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34"
+ integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==
wrap-ansi@^5.1.0:
version "5.1.0"
@@ -6829,10 +7121,10 @@ wrap-ansi@^5.1.0:
string-width "^3.0.0"
strip-ansi "^5.0.0"
-wrap-ansi@^6.2.0:
- version "6.2.0"
- resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
- integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==
+wrap-ansi@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+ integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
@@ -6853,6 +7145,14 @@ write-file-atomic@^3.0.0:
signal-exit "^3.0.2"
typedarray-to-buffer "^3.1.5"
+write-file-atomic@^4.0.2:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd"
+ integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==
+ dependencies:
+ imurmurhash "^0.1.4"
+ signal-exit "^3.0.7"
+
write@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3"
@@ -6860,11 +7160,6 @@ write@1.0.3:
dependencies:
mkdirp "^0.5.1"
-ws@^7.2.3:
- version "7.4.6"
- resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c"
- integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==
-
x-xss-protection@1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/x-xss-protection/-/x-xss-protection-1.3.0.tgz#3e3a8dd638da80421b0e9fff11a2dbe168f6d52c"
@@ -6875,21 +7170,21 @@ xdg-basedir@^4.0.0:
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13"
integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==
-xml-name-validator@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
- integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==
-
-xmlchars@^2.2.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
- integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
-
y18n@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4"
integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==
+y18n@^5.0.5:
+ version "5.0.8"
+ resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
+ integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
+
+yallist@^3.0.2:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
+ integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
+
yallist@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
@@ -6903,13 +7198,10 @@ yargs-parser@^13.1.2:
camelcase "^5.0.0"
decamelize "^1.2.0"
-yargs-parser@^18.1.2:
- version "18.1.3"
- resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"
- integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==
- dependencies:
- camelcase "^5.0.0"
- decamelize "^1.2.0"
+yargs-parser@^21.1.1:
+ version "21.1.1"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
+ integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==
yargs@^13.3.0:
version "13.3.2"
@@ -6927,22 +7219,18 @@ yargs@^13.3.0:
y18n "^4.0.0"
yargs-parser "^13.1.2"
-yargs@^15.4.1:
- version "15.4.1"
- resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8"
- integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==
+yargs@^17.3.1:
+ version "17.7.2"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269"
+ integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==
dependencies:
- cliui "^6.0.0"
- decamelize "^1.2.0"
- find-up "^4.1.0"
- get-caller-file "^2.0.1"
+ cliui "^8.0.1"
+ escalade "^3.1.1"
+ get-caller-file "^2.0.5"
require-directory "^2.1.1"
- require-main-filename "^2.0.0"
- set-blocking "^2.0.0"
- string-width "^4.2.0"
- which-module "^2.0.0"
- y18n "^4.0.0"
- yargs-parser "^18.1.2"
+ string-width "^4.2.3"
+ y18n "^5.0.5"
+ yargs-parser "^21.1.1"
yauzl@^2.10.0:
version "2.10.0"
@@ -6951,3 +7239,8 @@ yauzl@^2.10.0:
dependencies:
buffer-crc32 "~0.2.3"
fd-slicer "~1.1.0"
+
+yocto-queue@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
+ integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
diff --git a/biome.json b/biome.json
new file mode 100644
index 000000000..3cd3f4eb3
--- /dev/null
+++ b/biome.json
@@ -0,0 +1,44 @@
+{
+ "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
+ "vcs": {
+ "enabled": true,
+ "clientKind": "git",
+ "useIgnoreFile": true
+ },
+ "files": {
+ "ignoreUnknown": true,
+ "ignore": ["node_modules", "dist", "build", "coverage"]
+ },
+ "formatter": {
+ "enabled": true,
+ "indentStyle": "space",
+ "indentWidth": 2,
+ "lineWidth": 100
+ },
+ "organizeImports": {
+ "enabled": true
+ },
+ "linter": {
+ "enabled": true,
+ "rules": {
+ "recommended": true,
+ "correctness": {
+ "noUnusedVariables": "warn",
+ "useExhaustiveDependencies": "warn"
+ },
+ "suspicious": {
+ "noExplicitAny": "off"
+ },
+ "style": {
+ "noUnusedTemplateLiteral": "off"
+ }
+ }
+ },
+ "javascript": {
+ "formatter": {
+ "quoteStyle": "single",
+ "trailingCommas": "all",
+ "semicolons": "always"
+ }
+ }
+}
diff --git a/client/.prettierrc.json b/client/.prettierrc.json
deleted file mode 100644
index 544138be4..000000000
--- a/client/.prettierrc.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "singleQuote": true
-}
diff --git a/client/Dockerfile.client b/client/Dockerfile.client
index 971f93ddb..ce27bb157 100644
--- a/client/Dockerfile.client
+++ b/client/Dockerfile.client
@@ -1,4 +1,4 @@
-FROM node:14.11.0 AS client-development
+FROM node:20-alpine AS client-development
RUN mkdir /srv/client && chown node:node /srv/client
WORKDIR /srv/client
USER node
@@ -6,7 +6,7 @@ RUN mkdir -p node_modules
COPY --chown=node:node package.json package.json ./
RUN npm install --silent
-FROM node:14.11.0-slim AS client-builder
+FROM node:20-alpine AS client-builder
USER node
WORKDIR /srv/client
COPY --from=client-development /srv/client/node_modules node_modules
diff --git a/client/Dockerfile.dev b/client/Dockerfile.dev
index 0ae20b4cb..879ba998a 100644
--- a/client/Dockerfile.dev
+++ b/client/Dockerfile.dev
@@ -1,4 +1,4 @@
-FROM node:14.11.0 AS client-development
+FROM node:18-alpine AS client-development
RUN mkdir /srv/client && chown node:node /srv/client
WORKDIR /srv/client
USER node
diff --git a/client/Dockerfile.prod b/client/Dockerfile.prod
index cac246b85..bcd153121 100644
--- a/client/Dockerfile.prod
+++ b/client/Dockerfile.prod
@@ -1,19 +1,19 @@
-FROM node:14.11.0 AS node-modules-install
+FROM node:20-alpine AS node-modules-install
RUN mkdir /srv/client && chown node:node /srv/client
WORKDIR /srv/client
USER node
RUN mkdir -p node_modules
-COPY --chown=node:node package.json package.json ./
-RUN npm install --silent
+COPY --chown=node:node package*.json ./
+RUN npm install --no-update-notifier
-FROM node:14.11.0-slim AS client-builder
+FROM node:20-alpine AS client-builder
USER node
WORKDIR /srv/client
COPY --from=node-modules-install /srv/client/node_modules node_modules
COPY . .
USER root
-ARG CUSTOM_REQUEST_HEADER nAb3kY-S%qE#4!d
-ENV REACT_APP_CUSTOM_REQUEST_HEADER $CUSTOM_REQUEST_HEADER
+ARG CUSTOM_REQUEST_HEADER=nAb3kY-S%qE#4!d
+ENV REACT_APP_CUSTOM_REQUEST_HEADER=$CUSTOM_REQUEST_HEADER
RUN npm run build
FROM nginx as client-production
diff --git a/client/public/index.html b/client/index.html
similarity index 86%
rename from client/public/index.html
rename to client/index.html
index b0f3060f0..da21cf20c 100644
--- a/client/public/index.html
+++ b/client/index.html
@@ -2,7 +2,7 @@
-
+
-
+
@@ -33,6 +33,7 @@
You need to enable JavaScript to run this app.
+