diff --git a/.dockerignore b/.dockerignore index 6d2597700..669020cef 100644 --- a/.dockerignore +++ b/.dockerignore @@ -5,6 +5,7 @@ dist/ swagger.json .husky/ .github/ +assets/ # Logs logs/ @@ -24,4 +25,5 @@ dockerfile docker-compose.yml cspell.json README.md -nodemon.json \ No newline at end of file +nodemon.json +templates \ No newline at end of file diff --git a/.env.example b/.env.example index dc8cd0452..018ae6ab0 100644 --- a/.env.example +++ b/.env.example @@ -2,16 +2,19 @@ APP_NAME=NestJS_ACK APP_ENV=development APP_LANGUAGE=en APP_TIMEZONE=Asia/Jakarta -APP_DEBUG=false HTTP_ENABLE=true HTTP_HOST=localhost HTTP_PORT=3000 +DEBUG_ENABLE=false + +MIDDLEWARE_CORS_ORIGIN=* + URL_VERSIONING_ENABLE=true URL_VERSION=1 -DATABASE_URI=mongodb://host.docker.internal:27017,host.docker.internal:27018,host.docker.internal:27019/ack?retryWrites=true&w=majority&replicaSet=rs0 +DATABASE_URI=mongodb://localhost:27017,localhost:27018,localhost:27019/ack?retryWrites=true&w=majority&replicaSet=rs0 DATABASE_DEBUG=false AUTH_JWT_ISSUER=https://example.com @@ -19,7 +22,7 @@ AUTH_JWT_AUDIENCE=ack AUTH_JWT_ACCESS_TOKEN_EXPIRED=15m AUTH_JWT_ACCESS_TOKEN_SECRET_KEY=1234567890 -AUTH_JWT_REFRESH_TOKEN_EXPIRED=182d +AUTH_JWT_REFRESH_TOKEN_EXPIRED=7d AUTH_JWT_REFRESH_TOKEN_SECRET_KEY=0987654321 AUTH_SOCIAL_GOOGLE_CLIENT_ID= @@ -28,18 +31,24 @@ AUTH_SOCIAL_GOOGLE_CLIENT_SECRET= AUTH_SOCIAL_APPLE_CLIENT_ID= AUTH_SOCIAL_APPLE_SIGN_IN_CLIENT_ID= -AWS_S3_CREDENTIAL_KEY= -AWS_S3_CREDENTIAL_SECRET= -AWS_S3_REGION=ap-southeast-3 -AWS_S3_BUCKET= +AWS_S3_PUBLIC_CREDENTIAL_KEY= +AWS_S3_PUBLIC_CREDENTIAL_SECRET= +AWS_S3_PUBLIC_REGION=ap-southeast-3 +AWS_S3_PUBLIC_BUCKET= +AWS_S3_PUBLIC_CDN= +AWS_S3_PRIVATE_CREDENTIAL_KEY= +AWS_S3_PRIVATE_CREDENTIAL_SECRET= +AWS_S3_PRIVATE_REGION=ap-southeast-3 +AWS_S3_PRIVATE_BUCKET= + AWS_SES_CREDENTIAL_KEY= AWS_SES_CREDENTIAL_SECRET= AWS_SES_REGION=ap-southeast-3 REDIS_HOST=localhost REDIS_PORT=6379 +REDIS_USERNAME= REDIS_PASSWORD= -REDIS_TLS=false SENTRY_DSN= diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml deleted file mode 100644 index 1529a462a..000000000 --- a/.github/workflows/ci-cd.yml +++ /dev/null @@ -1,298 +0,0 @@ -name: CI_CD -on: - push: - branches: - - main - - staging - workflow_dispatch: - -jobs: - build_image: - runs-on: ubuntu-latest - - env: - DOCKERFILE: ci/dockerfile - - DOCKERHUB_USERNAME: ${{ vars.DOCKERHUB_USERNAME }} - DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} - DOCKERHUB_REPO_NAME: ${{ vars.DOCKERHUB_REPO_NAME }} - - steps: - - name: Git checkout - uses: actions/checkout@v3 - - - name: Get short sha commit - id: git - run: | - echo "short_sha=$(git rev-parse --short $GITHUB_SHA)" >> "$GITHUB_OUTPUT" - - - name: Get latest version - id: version - uses: ActionsTools/read-json-action@main - with: - file_path: "package.json" - - - name: Git - run: | - echo Short sha: ${{ steps.git.outputs.short_sha }} - echo Version is: ${{ steps.version.outputs.version }} - - - name: Environment - run: | - echo DOCKERFILE is: ${{ env.DOCKERFILE }} - echo DOCKERHUB_USERNAME is: ${{ env.DOCKERHUB_USERNAME }} - echo DOCKERHUB_TOKEN is: ${{ env.DOCKERHUB_TOKEN }} - echo DOCKERHUB_REPO_NAME is: ${{ env.DOCKERHUB_REPO_NAME }} - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - name: Set up Docker Buildx for Builder - uses: docker/setup-buildx-action@v3 - id: builder - - - name: Set up Docker Buildx for Main - uses: docker/setup-buildx-action@v3 - id: main - - - name: Builder name - run: echo ${{ steps.builder.outputs.name }} - - - name: Main name - run: echo ${{ steps.main.outputs.name }} - - - name: Login to DockerHub - uses: docker/login-action@v3 - with: - username: ${{ env.DOCKERHUB_USERNAME }} - password: ${{ env.DOCKERHUB_TOKEN }} - - - name: Build builder - uses: docker/build-push-action@v4 - with: - builder: ${{ steps.builder.outputs.name }} - file: ${{ env.DOCKERFILE }} - target: builder - - - name: Build main and push - uses: docker/build-push-action@v4 - if: ${{ github.ref_name == 'main' }} - with: - builder: ${{ steps.main.outputs.name }} - file: ${{ env.DOCKERFILE }} - build-args: | - NODE_ENV=production - target: main - tags: | - ${{ env.DOCKERHUB_USERNAME }}/${{ env.DOCKERHUB_REPO_NAME }}:latest - ${{ env.DOCKERHUB_USERNAME }}/${{ env.DOCKERHUB_REPO_NAME }}:main_v${{ steps.version.outputs.version }} - ${{ env.DOCKERHUB_USERNAME }}/${{ env.DOCKERHUB_REPO_NAME }}:main_v${{ steps.version.outputs.version }}_sha-${{ steps.git.outputs.short_sha }} - push: true - - - name: Build staging and push - uses: docker/build-push-action@v4 - if: ${{ github.ref_name == 'staging' }} - with: - builder: ${{ steps.main.outputs.name }} - file: ${{ env.DOCKERFILE }} - build-args: | - NODE_ENV=staging - target: main - tags: | - ${{ env.DOCKERHUB_USERNAME }}/${{ env.DOCKERHUB_REPO_NAME }}:staging_v${{ steps.version.outputs.version }} - ${{ env.DOCKERHUB_USERNAME }}/${{ env.DOCKERHUB_REPO_NAME }}:staging_v${{ steps.version.outputs.version }}_sha-${{ steps.git.outputs.short_sha }} - push: true - - deploy_production: - needs: [ build_image ] - runs-on: ubuntu-latest - if: ${{ github.ref_name == 'main' }} - environment: 'production' - - env: - DOCKERHUB_USERNAME: ${{ vars.DOCKERHUB_USERNAME }} - DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} - DOCKERHUB_REPO_NAME: ${{ vars.DOCKERHUB_REPO_NAME }} - - DOCKER_CONTAINER_NAME: ${{ vars.DOCKER_CONTAINER_NAME }} - DOCKER_CONTAINER_PORT: 3000 - DOCKER_CONTAINER_PORT_EXPOSE: ${{ vars.DOCKER_CONTAINER_PORT_EXPOSE }} - DOCKER_CONTAINER_NETWORK: app-network - - SSH_HOST: ${{ vars.SSH_HOST }} - SSH_PORT: ${{ vars.SSH_PORT }} - SSH_USER: ${{ vars.SSH_USER }} - SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} - - steps: - - name: Git checkout - uses: actions/checkout@v3 - - - name: Get short sha commit - id: git - run: | - echo "short_sha=$(git rev-parse --short $GITHUB_SHA)" >> "$GITHUB_OUTPUT" - - - name: Get latest version - id: version - uses: ActionsTools/read-json-action@main - with: - file_path: "package.json" - - - name: Git - run: | - echo Short sha: ${{ steps.git.outputs.short_sha }} - echo Version is: ${{ steps.version.outputs.version }} - - - name: Environment - run: | - echo DOCKERHUB_USERNAME is: ${{ env.DOCKERHUB_USERNAME }} - echo DOCKERHUB_TOKEN is: ${{ env.DOCKERHUB_TOKEN }} - echo DOCKERHUB_REPO_NAME is: ${{ env.DOCKERHUB_REPO_NAME }} - echo DOCKER_CONTAINER_NAME is: ${{ env.DOCKER_CONTAINER_NAME }} - echo DOCKER_CONTAINER_PORT is: ${{ env.DOCKER_CONTAINER_PORT }} - echo DOCKER_CONTAINER_PORT_EXPOSE is: ${{ env.DOCKER_CONTAINER_PORT_EXPOSE }} - echo DOCKER_CONTAINER_NETWORK is: ${{ env.DOCKER_CONTAINER_NETWORK }} - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Login to DockerHub - uses: docker/login-action@v3 - with: - username: ${{ env.DOCKERHUB_USERNAME }} - password: ${{ env.DOCKERHUB_TOKEN }} - - - name: Deploy - uses: fifsky/ssh-action@master - with: - command: | - docker pull ${{ env.DOCKERHUB_USERNAME }}/${{ env.DOCKERHUB_REPO_NAME }}:main_v${{ steps.version.outputs.version }}_sha-${{ steps.git.outputs.short_sha }} - docker stop ${{ env.DOCKER_CONTAINER_NAME }} && docker rm ${{ env.DOCKER_CONTAINER_NAME }} - docker network create ${{ env.DOCKER_CONTAINER_NETWORK }} --driver=bridge - docker run -itd \ - --env NODE_ENV=production \ - --hostname ${{ env.DOCKER_CONTAINER_NAME }} \ - --publish ${{ env.DOCKER_CONTAINER_PORT_EXPOSE }}:${{ env.DOCKER_CONTAINER_PORT }} \ - --network ${{ env.DOCKER_CONTAINER_NETWORK }} \ - --volume /app/${{ env.DOCKER_CONTAINER_NAME }}/logs/:/app/logs/ \ - --volume /app/${{ env.DOCKER_CONTAINER_NAME }}/.env:/app/.env \ - --restart unless-stopped \ - --name ${{ env.DOCKER_CONTAINER_NAME }} ${{ env.DOCKERHUB_USERNAME }}/${{ env.DOCKERHUB_REPO_NAME }}:main_v${{ steps.version.outputs.version }}_sha-${{ steps.git.outputs.short_sha }} - host: ${{ env.SSH_HOST }} - port: ${{ env.SSH_PORT }} - user: ${{ env.SSH_USER }} - key: ${{ env.SSH_PRIVATE_KEY }} - - - name: Clean - uses: fifsky/ssh-action@master - continue-on-error: true - with: - command: | - docker container prune --force - docker image prune --force - docker rmi $(docker images ${{ env.DOCKERHUB_USERNAME }}/** -q) --force - host: ${{ env.SSH_HOST }} - port: ${{ env.SSH_PORT }} - user: ${{ env.SSH_USER }} - key: ${{ env.SSH_PRIVATE_KEY }} - - deploy_staging: - needs: [ build_image ] - runs-on: ubuntu-latest - if: ${{ github.ref_name == 'staging' }} - environment: 'staging' - - env: - DOCKERHUB_USERNAME: ${{ vars.DOCKERHUB_USERNAME }} - DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} - DOCKERHUB_REPO_NAME: ${{ vars.DOCKERHUB_REPO_NAME }} - - DOCKER_CONTAINER_NAME: ${{ vars.DOCKER_CONTAINER_NAME }} - DOCKER_CONTAINER_PORT: 3000 - DOCKER_CONTAINER_PORT_EXPOSE: ${{ vars.DOCKER_CONTAINER_PORT_EXPOSE }} - DOCKER_CONTAINER_NETWORK: app-network - - SSH_HOST: ${{ vars.SSH_HOST }} - SSH_PORT: ${{ vars.SSH_PORT }} - SSH_USER: ${{ vars.SSH_USER }} - SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} - - steps: - - name: Git checkout - uses: actions/checkout@v3 - - - name: Get short sha commit - id: git - run: | - echo "short_sha=$(git rev-parse --short $GITHUB_SHA)" >> "$GITHUB_OUTPUT" - - - name: Get latest version - id: version - uses: ActionsTools/read-json-action@main - with: - file_path: "package.json" - - - name: Git - run: | - echo Short sha: ${{ steps.git.outputs.short_sha }} - echo Version is: ${{ steps.version.outputs.version }} - - - name: Environment - run: | - echo DOCKERHUB_USERNAME is: ${{ env.DOCKERHUB_USERNAME }} - echo DOCKERHUB_TOKEN is: ${{ env.DOCKERHUB_TOKEN }} - echo DOCKERHUB_REPO_NAME is: ${{ env.DOCKERHUB_REPO_NAME }} - echo DOCKER_CONTAINER_NAME is: ${{ env.DOCKER_CONTAINER_NAME }} - echo DOCKER_CONTAINER_PORT is: ${{ env.DOCKER_CONTAINER_PORT }} - echo DOCKER_CONTAINER_PORT_EXPOSE is: ${{ env.DOCKER_CONTAINER_PORT_EXPOSE }} - echo DOCKER_CONTAINER_NETWORK is: ${{ env.DOCKER_CONTAINER_NETWORK }} - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Login to DockerHub - uses: docker/login-action@v3 - with: - username: ${{ env.DOCKERHUB_USERNAME }} - password: ${{ env.DOCKERHUB_TOKEN }} - - - name: Deploy - uses: fifsky/ssh-action@master - with: - command: | - docker pull ${{ env.DOCKERHUB_USERNAME }}/${{ env.DOCKERHUB_REPO_NAME }}:staging_v${{ steps.version.outputs.version }}_sha-${{ steps.git.outputs.short_sha }} - docker stop ${{ env.DOCKER_CONTAINER_NAME }} && docker rm ${{ env.DOCKER_CONTAINER_NAME }} - docker network create ${{ env.DOCKER_CONTAINER_NETWORK }} --driver=bridge - docker run -itd \ - --env NODE_ENV=development \ - --hostname ${{ env.DOCKER_CONTAINER_NAME }} \ - --publish ${{ env.DOCKER_CONTAINER_PORT_EXPOSE }}:${{ env.DOCKER_CONTAINER_PORT }} \ - --network ${{ env.DOCKER_CONTAINER_NETWORK }} \ - --volume /app/${{ env.DOCKER_CONTAINER_NAME }}/logs/:/app/logs/ \ - --volume /app/${{ env.DOCKER_CONTAINER_NAME }}/.env:/app/.env \ - --restart unless-stopped \ - --name ${{ env.DOCKER_CONTAINER_NAME }} ${{ env.DOCKERHUB_USERNAME }}/${{ env.DOCKERHUB_REPO_NAME }}:staging_v${{ steps.version.outputs.version }}_sha-${{ steps.git.outputs.short_sha }} - host: ${{ env.SSH_HOST }} - port: ${{ env.SSH_PORT }} - user: ${{ env.SSH_USER }} - key: ${{ env.SSH_PRIVATE_KEY }} - - - name: Clean - uses: fifsky/ssh-action@master - continue-on-error: true - with: - command: | - docker container prune --force - docker image prune --force - docker rmi $(docker images ${{ env.DOCKERHUB_USERNAME }}/** -q) --force - host: ${{ env.SSH_HOST }} - port: ${{ env.SSH_PORT }} - user: ${{ env.SSH_USER }} - key: ${{ env.SSH_PRIVATE_KEY }} diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 7e02f794f..6e3036815 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -1,32 +1,31 @@ name: Linter on: - - workflow_dispatch: - pull_request: - branches: - - main - - staging - - development + workflow_dispatch: + pull_request: + branches: + - main + # - staging + # - development jobs: - linter: - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: ['lts/*'] + linter: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: ['lts/*'] + + steps: + - name: Git checkout + uses: actions/checkout@v3 - steps: - - name: Git checkout - uses: actions/checkout@v3 + - name: Setup node version ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} - - name: Setup node version ${{ matrix.node-version }} - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} + - name: Install dependencies + run: yarn --frozen-lockfile --non-interactive - - name: Install dependencies - run: yarn --frozen-lockfile - - - name: Linter - run: yarn lint + - name: Linter + run: yarn lint diff --git a/.github/workflows/release-version.yml b/.github/workflows/release-version.yml new file mode 100644 index 000000000..a58d2062a --- /dev/null +++ b/.github/workflows/release-version.yml @@ -0,0 +1,41 @@ +name: ReleaseVersion + +permissions: + contents: write + +on: + workflow_dispatch: + +jobs: + release: + runs-on: ubuntu-latest + + steps: + - name: Git checkout + uses: actions/checkout@v3 + + - name: Get short sha commit + id: git + run: | + echo "short_sha=$(git rev-parse --short $GITHUB_SHA)" >> "$GITHUB_OUTPUT" + + - name: Get latest version + id: version + uses: martinbeentjes/npm-get-version-action@main + + - name: Git + run: | + echo Branch name is: ${{ github.ref_name }} + echo Short sha: ${{ steps.git.outputs.short_sha }} + echo Version is: ${{ steps.version.outputs.current-version }} + + - name: Release + uses: softprops/action-gh-release@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: v${{ steps.version.outputs.current-version }} + name: v${{ steps.version.outputs.current-version }} + generate_release_notes: true + draft: false + prerelease: false diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1376fa5ea..daf1cf7b6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,42 +1,401 @@ name: Release - -permissions: - contents: write - on: - - workflow_dispatch: + # push: + # branches: + # - main + # - staging + # - development + workflow_dispatch: jobs: - release: - runs-on: ubuntu-latest - - steps: - - name: Git checkout - uses: actions/checkout@v3 - - - name: Get short sha commit - id: git - run: | - echo "short_sha=$(git rev-parse --short $GITHUB_SHA)" >> "$GITHUB_OUTPUT" - - - name: Get latest version - id: version - uses: martinbeentjes/npm-get-version-action@main - - - name: Git - run: | - echo Branch name is: ${{ github.ref_name }} - echo Short sha: ${{ steps.git.outputs.short_sha }} - echo Version is: ${{ steps.version.outputs.current-version }} - - - name: Release - uses: softprops/action-gh-release@master + build_image_production: + runs-on: ubuntu-latest + if: ${{ github.ref_name == 'main' }} + + env: + DOCKERFILE: ci/dockerfile + + DOCKERHUB_USERNAME: ${{ vars.DOCKERHUB_USERNAME }} + DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} + DOCKERHUB_REPO_NAME: ${{ vars.DOCKERHUB_REPO_NAME }} + + steps: + - name: Git checkout + uses: actions/checkout@v3 + + - name: Get short sha commit + id: git + run: | + echo "short_sha=$(git rev-parse --short $GITHUB_SHA)" >> "$GITHUB_OUTPUT" + + - name: Get latest version + id: version + uses: ActionsTools/read-json-action@main + with: + file_path: 'package.json' + + - name: Git + run: | + echo Short sha: ${{ steps.git.outputs.short_sha }} + echo Version is: ${{ steps.version.outputs.version }} + + - name: Environment + run: | + echo DOCKERFILE is: ${{ env.DOCKERFILE }} + echo DOCKERHUB_USERNAME is: ${{ env.DOCKERHUB_USERNAME }} + echo DOCKERHUB_TOKEN is: ${{ env.DOCKERHUB_TOKEN }} + echo DOCKERHUB_REPO_NAME is: ${{ env.DOCKERHUB_REPO_NAME }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx for Builder + uses: docker/setup-buildx-action@v3 + id: builder + + - name: Set up Docker Buildx for Main + uses: docker/setup-buildx-action@v3 + id: main + + - name: Builder name + run: echo ${{ steps.builder.outputs.name }} + + - name: Main name + run: echo ${{ steps.main.outputs.name }} + + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ env.DOCKERHUB_USERNAME }} + password: ${{ env.DOCKERHUB_TOKEN }} + + - name: Build builder + uses: docker/build-push-action@v4 + with: + builder: ${{ steps.builder.outputs.name }} + file: ${{ env.DOCKERFILE }} + build-args: | + NODE_ENV=test + target: builder + + - name: Build main and push + uses: docker/build-push-action@v4 + with: + builder: ${{ steps.main.outputs.name }} + file: ${{ env.DOCKERFILE }} + build-args: | + NODE_ENV=production + target: main + tags: | + ${{ env.DOCKERHUB_USERNAME }}/${{ env.DOCKERHUB_REPO_NAME }}:latest + ${{ env.DOCKERHUB_USERNAME }}/${{ env.DOCKERHUB_REPO_NAME }}:main_v${{ steps.version.outputs.version }} + ${{ env.DOCKERHUB_USERNAME }}/${{ env.DOCKERHUB_REPO_NAME }}:main_v${{ steps.version.outputs.version }}_sha-${{ steps.git.outputs.short_sha }} + push: true + + deploy_production: + needs: [build_image_production] + runs-on: ubuntu-latest + if: ${{ github.ref_name == 'main' }} + environment: 'production' + + env: + DOCKERHUB_USERNAME: ${{ vars.DOCKERHUB_USERNAME }} + DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} + DOCKERHUB_REPO_NAME: ${{ vars.DOCKERHUB_REPO_NAME }} + + DOCKER_CONTAINER_NAME: ${{ vars.DOCKER_CONTAINER_NAME }} + DOCKER_CONTAINER_PORT: 3000 + DOCKER_CONTAINER_PORT_EXPOSE: ${{ vars.DOCKER_CONTAINER_PORT_EXPOSE }} + DOCKER_CONTAINER_NETWORK: app-network + + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ vars.AWS_REGION }} + AWS_SECURITY_GROUP_ID: ${{ vars.AWS_SECURITY_GROUP_ID }} + + SSH_HOST: ${{ vars.SSH_HOST }} + SSH_PORT: ${{ vars.SSH_PORT }} + SSH_USER: ${{ vars.SSH_USER }} + SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} + + steps: + - name: Git checkout + uses: actions/checkout@v3 + + - name: Get short sha commit + id: git + run: | + echo "short_sha=$(git rev-parse --short $GITHUB_SHA)" >> "$GITHUB_OUTPUT" + + - name: Get latest version + id: version + uses: ActionsTools/read-json-action@main + with: + file_path: 'package.json' + + - name: Git + run: | + echo Short sha: ${{ steps.git.outputs.short_sha }} + echo Version is: ${{ steps.version.outputs.version }} + + - name: Environment + run: | + echo DOCKERHUB_USERNAME is: ${{ env.DOCKERHUB_USERNAME }} + echo DOCKERHUB_TOKEN is: ${{ env.DOCKERHUB_TOKEN }} + echo DOCKERHUB_REPO_NAME is: ${{ env.DOCKERHUB_REPO_NAME }} + echo DOCKER_CONTAINER_NAME is: ${{ env.DOCKER_CONTAINER_NAME }} + echo DOCKER_CONTAINER_PORT is: ${{ env.DOCKER_CONTAINER_PORT }} + echo DOCKER_CONTAINER_PORT_EXPOSE is: ${{ env.DOCKER_CONTAINER_PORT_EXPOSE }} + echo DOCKER_CONTAINER_NETWORK is: ${{ env.DOCKER_CONTAINER_NETWORK }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ env.DOCKERHUB_USERNAME }} + password: ${{ env.DOCKERHUB_TOKEN }} + + - name: Add public IP to AWS security group + uses: sohelamin/aws-security-group-add-ip-action@master + with: + aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + aws-security-group-id: ${{ env.AWS_SECURITY_GROUP_ID }} + port: '22' + protocol: 'tcp' + description: 'GitHub Action' + + - name: Deploy + uses: fifsky/ssh-action@master + with: + command: | + docker pull ${{ env.DOCKERHUB_USERNAME }}/${{ env.DOCKERHUB_REPO_NAME }}:main_v${{ steps.version.outputs.version }}_sha-${{ steps.git.outputs.short_sha }} + docker stop ${{ env.DOCKER_CONTAINER_NAME }} && docker rm ${{ env.DOCKER_CONTAINER_NAME }} + docker network create ${{ env.DOCKER_CONTAINER_NETWORK }} --driver=bridge + docker run -itd \ + --env NODE_ENV=production \ + --hostname ${{ env.DOCKER_CONTAINER_NAME }} \ + --publish ${{ env.DOCKER_CONTAINER_PORT_EXPOSE }}:${{ env.DOCKER_CONTAINER_PORT }} \ + --network ${{ env.DOCKER_CONTAINER_NETWORK }} \ + --volume /app/${{ env.DOCKER_CONTAINER_NAME }}/.env:/app/.env \ + --restart unless-stopped \ + --name ${{ env.DOCKER_CONTAINER_NAME }} ${{ env.DOCKERHUB_USERNAME }}/${{ env.DOCKERHUB_REPO_NAME }}:main_v${{ steps.version.outputs.version }}_sha-${{ steps.git.outputs.short_sha }} + host: ${{ env.SSH_HOST }} + port: ${{ env.SSH_PORT }} + user: ${{ env.SSH_USER }} + key: ${{ env.SSH_PRIVATE_KEY }} + + - name: Clean + uses: fifsky/ssh-action@master + continue-on-error: true + with: + command: | + docker container prune --force + docker image prune --force + docker rmi $(docker images ${{ env.DOCKERHUB_USERNAME }}/** -q) --force + host: ${{ env.SSH_HOST }} + port: ${{ env.SSH_PORT }} + user: ${{ env.SSH_USER }} + key: ${{ env.SSH_PRIVATE_KEY }} + + build_image_staging: + runs-on: ubuntu-latest + if: ${{ github.ref_name == 'staging' }} + + env: + DOCKERFILE: ci/dockerfile + + DOCKERHUB_USERNAME: ${{ vars.DOCKERHUB_USERNAME }} + DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} + DOCKERHUB_REPO_NAME: ${{ vars.DOCKERHUB_REPO_NAME }} + + steps: + - name: Git checkout + uses: actions/checkout@v3 + + - name: Get short sha commit + id: git + run: | + echo "short_sha=$(git rev-parse --short $GITHUB_SHA)" >> "$GITHUB_OUTPUT" + + - name: Get latest version + id: version + uses: ActionsTools/read-json-action@main + with: + file_path: 'package.json' + + - name: Git + run: | + echo Short sha: ${{ steps.git.outputs.short_sha }} + echo Version is: ${{ steps.version.outputs.version }} + + - name: Environment + run: | + echo DOCKERFILE is: ${{ env.DOCKERFILE }} + echo DOCKERHUB_USERNAME is: ${{ env.DOCKERHUB_USERNAME }} + echo DOCKERHUB_TOKEN is: ${{ env.DOCKERHUB_TOKEN }} + echo DOCKERHUB_REPO_NAME is: ${{ env.DOCKERHUB_REPO_NAME }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx for Builder + uses: docker/setup-buildx-action@v3 + id: builder + + - name: Set up Docker Buildx for Main + uses: docker/setup-buildx-action@v3 + id: main + + - name: Builder name + run: echo ${{ steps.builder.outputs.name }} + + - name: Main name + run: echo ${{ steps.main.outputs.name }} + + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ env.DOCKERHUB_USERNAME }} + password: ${{ env.DOCKERHUB_TOKEN }} + + - name: Build builder + uses: docker/build-push-action@v4 + with: + builder: ${{ steps.builder.outputs.name }} + file: ${{ env.DOCKERFILE }} + build-args: | + NODE_ENV=test + target: builder + + - name: Build staging and push + uses: docker/build-push-action@v4 + if: ${{ github.ref_name == 'staging' }} + with: + builder: ${{ steps.main.outputs.name }} + file: ${{ env.DOCKERFILE }} + build-args: | + NODE_ENV=development + target: main + tags: | + ${{ env.DOCKERHUB_USERNAME }}/${{ env.DOCKERHUB_REPO_NAME }}:staging_v${{ steps.version.outputs.version }} + ${{ env.DOCKERHUB_USERNAME }}/${{ env.DOCKERHUB_REPO_NAME }}:staging_v${{ steps.version.outputs.version }}_sha-${{ steps.git.outputs.short_sha }} + push: true + + deploy_staging: + needs: [build_image_staging] + runs-on: ubuntu-latest + if: ${{ github.ref_name == 'staging' }} + environment: 'staging' + env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: v${{ steps.version.outputs.current-version }} - name: v${{ steps.version.outputs.current-version }} - generate_release_notes: true - draft: false - prerelease: false + DOCKERHUB_USERNAME: ${{ vars.DOCKERHUB_USERNAME }} + DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} + DOCKERHUB_REPO_NAME: ${{ vars.DOCKERHUB_REPO_NAME }} + + DOCKER_CONTAINER_NAME: ${{ vars.DOCKER_CONTAINER_NAME }} + DOCKER_CONTAINER_PORT: 3000 + DOCKER_CONTAINER_PORT_EXPOSE: ${{ vars.DOCKER_CONTAINER_PORT_EXPOSE }} + DOCKER_CONTAINER_NETWORK: app-network + + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ vars.AWS_REGION }} + AWS_SECURITY_GROUP_ID: ${{ vars.AWS_SECURITY_GROUP_ID }} + + SSH_HOST: ${{ vars.SSH_HOST }} + SSH_PORT: ${{ vars.SSH_PORT }} + SSH_USER: ${{ vars.SSH_USER }} + SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} + + steps: + - name: Git checkout + uses: actions/checkout@v3 + + - name: Get short sha commit + id: git + run: | + echo "short_sha=$(git rev-parse --short $GITHUB_SHA)" >> "$GITHUB_OUTPUT" + + - name: Get latest version + id: version + uses: ActionsTools/read-json-action@main + with: + file_path: 'package.json' + + - name: Git + run: | + echo Short sha: ${{ steps.git.outputs.short_sha }} + echo Version is: ${{ steps.version.outputs.version }} + + - name: Environment + run: | + echo DOCKERHUB_USERNAME is: ${{ env.DOCKERHUB_USERNAME }} + echo DOCKERHUB_TOKEN is: ${{ env.DOCKERHUB_TOKEN }} + echo DOCKERHUB_REPO_NAME is: ${{ env.DOCKERHUB_REPO_NAME }} + echo DOCKER_CONTAINER_NAME is: ${{ env.DOCKER_CONTAINER_NAME }} + echo DOCKER_CONTAINER_PORT is: ${{ env.DOCKER_CONTAINER_PORT }} + echo DOCKER_CONTAINER_PORT_EXPOSE is: ${{ env.DOCKER_CONTAINER_PORT_EXPOSE }} + echo DOCKER_CONTAINER_NETWORK is: ${{ env.DOCKER_CONTAINER_NETWORK }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ env.DOCKERHUB_USERNAME }} + password: ${{ env.DOCKERHUB_TOKEN }} + + - name: Add public IP to AWS security group + uses: sohelamin/aws-security-group-add-ip-action@master + with: + aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + aws-security-group-id: ${{ env.AWS_SECURITY_GROUP_ID }} + port: '22' + protocol: 'tcp' + description: 'GitHub Action' + + - name: Deploy + uses: fifsky/ssh-action@master + with: + command: | + docker pull ${{ env.DOCKERHUB_USERNAME }}/${{ env.DOCKERHUB_REPO_NAME }}:staging_v${{ steps.version.outputs.version }}_sha-${{ steps.git.outputs.short_sha }} + docker stop ${{ env.DOCKER_CONTAINER_NAME }} && docker rm ${{ env.DOCKER_CONTAINER_NAME }} + docker network create ${{ env.DOCKER_CONTAINER_NETWORK }} --driver=bridge + docker run -itd \ + --env NODE_ENV=development \ + --hostname ${{ env.DOCKER_CONTAINER_NAME }} \ + --publish ${{ env.DOCKER_CONTAINER_PORT_EXPOSE }}:${{ env.DOCKER_CONTAINER_PORT }} \ + --network ${{ env.DOCKER_CONTAINER_NETWORK }} \ + --volume /app/${{ env.DOCKER_CONTAINER_NAME }}/.env:/app/.env \ + --restart unless-stopped \ + --name ${{ env.DOCKER_CONTAINER_NAME }} ${{ env.DOCKERHUB_USERNAME }}/${{ env.DOCKERHUB_REPO_NAME }}:staging_v${{ steps.version.outputs.version }}_sha-${{ steps.git.outputs.short_sha }} + host: ${{ env.SSH_HOST }} + port: ${{ env.SSH_PORT }} + user: ${{ env.SSH_USER }} + key: ${{ env.SSH_PRIVATE_KEY }} + + - name: Clean + uses: fifsky/ssh-action@master + continue-on-error: true + with: + command: | + docker container prune --force + docker image prune --force + docker rmi $(docker images ${{ env.DOCKERHUB_USERNAME }}/** -q) --force + host: ${{ env.SSH_HOST }} + port: ${{ env.SSH_PORT }} + user: ${{ env.SSH_USER }} + key: ${{ env.SSH_PRIVATE_KEY }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 252305667..260c94ed0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,35 +1,33 @@ name: Test on: + workflow_dispatch: + # pull_request: + # branches: + # - main + # - staging + # - development - workflow_dispatch: - pull_request: - branches: - - main - - staging - - development - jobs: - test: - runs-on: ubuntu-latest + test: + runs-on: ubuntu-latest - strategy: - matrix: - node-version: ['lts/*'] + strategy: + matrix: + node-version: ['lts/*'] - steps: - - name: Git checkout - uses: actions/checkout@v3 - - - name: Setup node version ${{ matrix.node-version }} - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} + steps: + - name: Git checkout + uses: actions/checkout@v3 - - name: Install dependencies - run: yarn --frozen-lockfile + - name: Setup node version ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} - - name: Unit Test - run: NODE_ENV=test yarn test - env: - CI: true + - name: Install dependencies + run: yarn --frozen-lockfile --non-interactive + - name: Unit Test + run: NODE_ENV=test yarn test + env: + CI: true diff --git a/.prettierignore b/.prettierignore index 2ff8622f1..321a80a21 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1 +1,2 @@ -package.json \ No newline at end of file +templates/* +swagger.json \ No newline at end of file diff --git a/.prettierrc b/.prettierrc index 55cd4b1d7..967a9dbd4 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,11 +1,11 @@ { - "semi": true, - "trailingComma": "es5", - "singleQuote": true, - "printWidth": 80, - "tabWidth": 4, - "useTabs": false, - "endOfLine": "lf", - "bracketSpacing": true, - "arrowParens": "avoid" + "semi": true, + "trailingComma": "es5", + "singleQuote": true, + "printWidth": 80, + "tabWidth": 4, + "useTabs": false, + "endOfLine": "lf", + "bracketSpacing": true, + "arrowParens": "avoid" } diff --git a/.swcrc b/.swcrc new file mode 100644 index 000000000..91629ed9d --- /dev/null +++ b/.swcrc @@ -0,0 +1,18 @@ +{ + "$schema": "https://json.schemastore.org/swcrc", + "sourceMaps": true, + "jsc": { + "parser": { + "syntax": "typescript", + "decorators": true, + "dynamicImport": true + }, + "transform": { + "legacyDecorator": true, + "decoratorMetadata": true + }, + "baseUrl": "./" + }, + "minify": false, + "exclude": ["node_modules", "dist", "coverage", "templates"] +} diff --git a/LICENSE.md b/LICENSE.md index 5c4d505b3..812c120f4 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. diff --git a/README.md b/README.md index f13e0579e..2a291f824 100644 --- a/README.md +++ b/README.md @@ -13,17 +13,17 @@ [![Yarn][yarn-shield]][ref-yarn] [![Docker][docker-shield]][ref-docker] -# ACK NestJs Boilerplate 🔥 🚀 +# ACK NestJs Boilerplate 🔥 🚀 > This repo will representative of authentication service and authorization service [ACK NestJs][ack] is a [Http NestJs v10.x][ref-nestjs] boilerplate. Best uses for backend service. -*You can [request feature][ack-issues] or [report bug][ack-issues] with following this link* +_You can [request feature][ack-issues] or [report bug][ack-issues] with following this link_ ## Table of contents -- [ACK NestJs Boilerplate 🔥 🚀](#ack-nestjs-boilerplate---) +- [ACK NestJs Boilerplate 🔥 🚀](#ack-nestjs-boilerplate--) - [Table of contents](#table-of-contents) - [Important](#important) - [TODO](#todo) @@ -37,18 +37,15 @@ - [Install Dependencies](#install-dependencies) - [Create environment](#create-environment) - [Database Migration and Seed](#database-migration-and-seed) - - [Email Migration](#email-migration) + - [Template Migration](#template-migration) - [Run Project](#run-project) - [Installation with Docker](#installation-with-docker) - [Test](#test) - [Swagger](#swagger) - - [API Key](#api-key) - - [User](#user) + - [API Key](#api-key) + - [User](#user) - [BullMQ Board](#bullmq-board) - [User](#user-1) - - [Redis Client Web Base](#redis-client-web-base) - - [MongoDB Client Web Base](#mongodb-client-web-base) - - [User](#user-2) - [License](#license) - [Contribute](#contribute) - [Contact](#contact) @@ -57,27 +54,17 @@ > Very limited documentation -* There have been some breaking changes between v5 and v6. -* The features will be relate with AWS / Amazon web service -* Stateless Authorization -* Must run MongoDB as a `replication set` for `database transactions`. -* If you want to implement `Google SSO`. You must have google cloud console account, then create your own Credential to get the `clientId` and `clientSecret`. -* If you want to implement `Apple SSO`. You must have `clientId` and `signInClientId`. -* If you change the environment value of `APP_ENV` to `production`, that will trigger. - 1. CorsMiddleware will implement config from `src/configs/middleware.config.ts`. - 2. Documentation will `disable`. - 3. Global prefix will remove. Before is `/api`. -* For monitoring, this project will use `sentry.io`, and only send `500` or `internal server error`. +- Stateful Authorization, using `redis-session` and `JWT`. +- Must run MongoDB as a `replication set` for `database transactions`. +- If you want to implement `Google SSO`. You must have google cloud console account, then create your own Credential to get the `clientId` and `clientSecret`. +- If you want to implement `Apple SSO`. You must have `clientId` and `signInClientId`. +- If you change the environment value of `APP_ENV` to `production`, that will disable Documentation. +- For monitoring, this project will use `sentry.io`, and sent uncatched error and/or `internal server error`. ## TODO -- [ ] Export Module -- [ ] Move to Stateful Authorization - 1. Session Module - 2. Device Module - 3. Password Period Module - 5. Reset Password Module - 6. Verification Module +- [ ] Export Module +- [ ] Move to Stateful Authorization 1. Reset Password Module 2. Verification Module ## Prerequisites @@ -97,56 +84,54 @@ We assume that everyone who comes here is **`programmer with intermediate knowle Describes which version. -| Name | Version | -| ---------- | -------- | -| NestJs | v10.x | -| NestJs Swagger | v7.x | -| NodeJs | v20.x | -| Typescript | v5.x | -| Mongoose | v10.x | -| MongoDB | v7.x | -| Yarn | v1.x | -| NPM | v10.x | -| Docker | v24.x | -| Docker Compose | v2.x | - +| Name | Version | +| -------------- | -------- | +| NestJs | v10.x | +| NestJs Swagger | v8.0.x | +| NodeJs | v22.11.x | +| Typescript | v5.6.x | +| Mongoose | v10.0.x | +| MongoDB | v8.x | +| Yarn | v1.22.x | +| Docker | v27.2.x | +| Docker Compose | v2.29.x | ## Objective -* Easy to maintenance -* NestJs Habit -* Component based / modular folder structure -* Stateless authentication and authorization -* Repository Design Pattern or Data Access Layer Design Pattern -* Follow Community Guide Line -* Follow The Twelve-Factor App -* Adopt SOLID and KISS principle -* Support for Microservice Architecture, Serverless Architecture, Clean Architecture, and/or Hexagonal Architecture +- Easy to maintenance +- NestJs Habit +- Component based / modular folder structure +- Stateless authentication and authorization +- Repository Design Pattern or Data Access Layer Design Pattern +- Follow Community Guide Line +- Follow The Twelve-Factor App ## Features ### Main Features -* NestJs 10.x 🥳 -* Typescript 🚀 -* Production ready 🔥 -* MongoDB integrate by using [mongoose][ref-mongoose] 🎉 -* Cached response with redis -* Queue bullmq with redis -* Authorization, Role Management. -* Repository Design Pattern (Multi Repository, can mix with other orm) -* Authentication (`Access Token`, `Refresh Token`, `API Key`, `Google SSO`, `Apple SSO`) -* Import and export data with CSV or Excel by using `decorator` -* Support multi-language `i18n` 🗣, can controllable with request header `x-custom-lang` -* Request validation for all request params, query, dan body with `class-validation` -* Swagger / OpenAPI 3 included -* Url Versioning, default version is `1` -* Server Side Pagination -* Sentry.io for Monitoring Tools -* Support Docker installation -* Support CI/CD (Eg: Github Action) -* Husky GitHook for run linter before commit 🐶 -* Linter with EsLint for Typescript +- NestJs 10.x 🥳 +- Typescript 🚀 +- Production ready 🔥 +- MongoDB integrate by using [mongoose][ref-mongoose] 🎉 +- Cached response with redis. +- Queue bullmq with redis. +- Logger with pino 🌲 +- SWC (Speedy Web Compiler) Compiler, fast compiler. +- Authorization, Role, and session Management (can revoke). +- Repository Design Pattern. +- Authentication (`Access Token`, `Refresh Token`, `API Key`, `Google SSO`, `Apple SSO`) +- Export data with CSV or Excel by using `decorator`. +- Support multi-language `i18n` 🗣, can controllable with request header `x-custom-lang` +- Request validation for all request params, query, dan body with `class-validation` +- Swagger / OpenAPI 3 included. +- Url Versioning, default version is `1`. +- Server Side Pagination. +- Sentry.io for Monitoring Tools. +- Support Docker installation. +- Support CI/CD (Eg: Github Action). +- Husky GitHook for run linter before commit 🐶. +- Linter with EsLint for Typescript. ## Installation @@ -157,10 +142,9 @@ The recommended version is the LTS version for every tool and package. 1. [NodeJs][ref-nodejs] 2. [MongoDB][ref-mongodb] -2. [Redis][ref-redis] -3. [Yarn][ref-yarn] -4. [Git][ref-git] - +3. [Redis][ref-redis] +4. [Yarn][ref-yarn] +5. [Git][ref-git] ### Clone Repo @@ -188,11 +172,12 @@ cp .env.example .env ### Database Migration and Seed -By default the options of `AutoCreate` and `AutoIndex` will be `false`. Thats means the schema in MongoDb will not change with the latest. -So to update the schema we need to run +By default the options of `AutoCreate` and `AutoIndex` will be `false`. +Thats means the schema in MongoDb will not change with the latest. +So in the first place, u need to update the schema ```bash -yarn migrate +yarn migrate:schema ``` After migrate the schema, also we need to run data seed @@ -201,15 +186,16 @@ After migrate the schema, also we need to run data seed yarn seed ``` -### Email Migration +### Template Migration > Optional -The email will automatically create email template through AWS SES if we set the value at `.env` file +The template migration will automatically upload `/templates` through AWS SES. For migrate + ```bash -yarn migrate:email +yarn migrate:template ``` ### Run Project @@ -251,54 +237,39 @@ yarn test ## Swagger -You can check The Swagger after running this project. Url `localhost:3000/docs` and don't for get to put `x-api-key` on header. +You can check The Swagger after running this project. Url `localhost:3000/docs` - -### API Key +## API Key api key: `v8VB0yY887lMpTA2VJMV` api key secret: `zeZbtGTugBTn3Qd5UXtSZBwt7gn3bg` -### User +## User 1. Super Admin - - email: `superadmin@mail.com` - - password: `aaAA@123` + - email: `superadmin@mail.com` + - password: `aaAA@123` 2. Admin - - email: `admin@mail.com` - - password: `aaAA@123` + - email: `admin@mail.com` + - password: `aaAA@123` 3. Member - - email: `member@mail.com` - - password: `aaAA@123` + - email: `member@mail.com` + - password: `aaAA@123` 4. User - - email: `user@mail.com` - - password: `aaAA@123` - + - email: `user@mail.com` + - password: `aaAA@123` + ## BullMQ Board > This available with docker installation -You can check and monitor your queue. Url `localhost:3010` +You can check and monitor your queue. +Url `localhost:3010` ### User - - email: `admin` - - password: `admin123` - -## Redis Client Web Base -> This available with docker installation - -You can check redis data using `redis-commander`. Url `localhost:3011` - -## MongoDB Client Web Base - -> This available with docker installation - -You can check mongodb data using `mongo-express`. Url `localhost:3012` - -### User - - email: `admin` - - password: `admin123` +- email: `admin` +- password: `admin123` ## License @@ -314,7 +285,7 @@ How to contribute in this repo 4. Push your changes to your remote branch 5. Open a pull request -If your code behind commit with the `original/main branch`, please update your code and resolve the conflict. +If your code behind commit with the `origin/main` branch, please update your code and resolve the conflict. ## Contact @@ -324,12 +295,12 @@ If your code behind commit with the `original/main branch`, please update your c [![LinkedIn][linkedin-shield]][author-linkedin] + [ack-contributors-shield]: https://img.shields.io/github/contributors/andrechristikan/ack-nestjs-boilerplate?style=for-the-badge [ack-forks-shield]: https://img.shields.io/github/forks/andrechristikan/ack-nestjs-boilerplate?style=for-the-badge [ack-stars-shield]: https://img.shields.io/github/stars/andrechristikan/ack-nestjs-boilerplate?style=for-the-badge [ack-issues-shield]: https://img.shields.io/github/issues/andrechristikan/ack-nestjs-boilerplate?style=for-the-badge [ack-license-shield]: https://img.shields.io/github/license/andrechristikan/ack-nestjs-boilerplate?style=for-the-badge - [nestjs-shield]: https://img.shields.io/badge/nestjs-%23E0234E.svg?style=for-the-badge&logo=nestjs&logoColor=white [nodejs-shield]: https://img.shields.io/badge/Node.js-339933?style=for-the-badge&logo=nodedotjs&logoColor=white [typescript-shield]: https://img.shields.io/badge/TypeScript-007ACC?style=for-the-badge&logo=typescript&logoColor=white @@ -338,25 +309,28 @@ If your code behind commit with the `original/main branch`, please update your c [jest-shield]: https://img.shields.io/badge/-jest-%23C21325?style=for-the-badge&logo=jest&logoColor=white [yarn-shield]: https://img.shields.io/badge/yarn-%232C8EBB.svg?style=for-the-badge&logo=yarn&logoColor=white [docker-shield]: https://img.shields.io/badge/docker-%230db7ed.svg?style=for-the-badge&logo=docker&logoColor=white - [github-shield]: https://img.shields.io/badge/GitHub-100000?style=for-the-badge&logo=github&logoColor=white [linkedin-shield]: https://img.shields.io/badge/LinkedIn-0077B5?style=for-the-badge&logo=linkedin&logoColor=white + [author-linkedin]: https://linkedin.com/in/andrechristikan [author-email]: mailto:andrechristikan@gmail.com [author-github]: https://github.com/andrechristikan + [ack-issues]: https://github.com/andrechristikan/ack-nestjs-boilerplate/issues [ack-stars]: https://github.com/andrechristikan/ack-nestjs-boilerplate/stargazers [ack-forks]: https://github.com/andrechristikan/ack-nestjs-boilerplate/network/members [ack-contributors]: https://github.com/andrechristikan/ack-nestjs-boilerplate/graphs/contributors + [license]: LICENSE.md + [ref-nestjs]: http://nestjs.com [ref-mongoose]: https://mongoosejs.com [ref-mongodb]: https://docs.mongodb.com/ diff --git a/ci/dockerfile b/ci/dockerfile index 6ce85b7f1..bad434c05 100644 --- a/ci/dockerfile +++ b/ci/dockerfile @@ -1,8 +1,8 @@ # Test Image FROM node:lts-alpine as builder -LABEL maintainer "andrechristikan@mail.com" +LABEL maintainer "andrechristikan@gmail.com" -ENV NODE_ENV=test +ENV NODE_ENV=testing WORKDIR /app COPY package.json yarn.lock ./ @@ -15,14 +15,16 @@ RUN yarn build # Production Image FROM node:lts-alpine as main -LABEL maintainer "andrechristikan@mail.com" +LABEL maintainer "andrechristikan@gmail.com" +ARG NODE_ENV ENV NODE_ENV=${NODE_ENV} WORKDIR /app EXPOSE 3000 COPY --from=builder /app/package.json ./package.json +COPY --from=builder /app/yarn.lock ./yarn.lock COPY --from=builder /app/dist ./dist RUN touch .env diff --git a/docker-compose.yml b/docker-compose.yml index a6a155d52..5feec873a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,158 +1,110 @@ - version: '3.8' name: ack services: - service: - build: - context: . - args: - NODE_ENV: 'development' - container_name: service - hostname: service - ports: - - 3000:3000 - volumes: - - ./src/:/app/src/ - - .env/:/app/.env - restart: always - networks: - - app-network - - redis: - image: redis:latest - container_name: redis - hostname: redis - restart: always - ports: - - '6379:6379' - volumes: - - redis_data:/data - networks: - - app-network - healthcheck: - test: ['CMD', 'redis-cli', 'ping'] - interval: 10s - timeout: 30s - retries: 5 - - redis-bullboard: - image: deadly0/bull-board:latest - container_name: redis-bullboard - hostname: redis-bullboard - restart: always - ports: - - 3010:3000 - networks: - - app-network - environment: - - REDIS_HOST=redis - - REDIS_PORT=6379 - - REDIS_DB=0 - - USER_LOGIN=admin - - USER_PASSWORD=admin123 - depends_on: - - redis - - redis-commander: - image: ghcr.io/joeferner/redis-commander:latest - container_name: redis-commander - hostname: redis-commander - restart: always - ports: - - 3011:8081 - networks: - - app-network - environment: - - REDIS_HOSTS=local:redis:6379 - depends_on: - - redis + redis: + image: redis:latest + container_name: redis + hostname: redis + restart: always + ports: + - '6379:6379' + volumes: + - redis_data:/data + networks: + - app-network + healthcheck: + test: ['CMD', 'redis-cli', 'ping'] + interval: 10s + timeout: 30s + retries: 5 - mongo-express: - image: mongo-express:latest - container_name: mongo-express - hostname: mongo-express - environment: - ME_CONFIG_BASICAUTH_USERNAME: admin - ME_CONFIG_BASICAUTH_PASSWORD: admin123 - ME_CONFIG_MONGODB_URL: mongodb://host.docker.internal:27017,host.docker.internal:27018,host.docker.internal:27019/ack?retryWrites=true&w=majority&replicaSet=rs0 - ports: - - 3012:8081 - networks: - - app-network - depends_on: - - mongodb1 - - mongodb2 - - mongodb3 + redis-bullboard: + image: deadly0/bull-board:latest + container_name: redis-bullboard + hostname: redis-bullboard + restart: always + ports: + - 3010:3000 + networks: + - app-network + environment: + - REDIS_HOST=redis + - REDIS_PORT=6379 + - BULL_PREFIX=bull + - USER_LOGIN=admin + - USER_PASSWORD=admin123 + depends_on: + - redis - mongodb1: - container_name: mongodb1 - hostname: mongodb1 - image: mongo:latest - restart: always - ports: - - "27017:27017" - links: - - mongodb2 - - mongodb3 - depends_on: - mongodb2: - condition: service_started - mongodb3: - condition: service_started - networks: - - app-network - volumes: - - mongodb1_data:/data/db - - mongodb1_config:/data/configdb - command: mongod --bind_ip_all --replSet rs0 --port 27017 - extra_hosts: - - "host.docker.internal:host-gateway" - healthcheck: - test: echo "try { rs.status() } catch (err) { rs.initiate({_id:'rs0',members:[{_id:0,host:'host.docker.internal:27017',priority:1},{_id:1,host:'host.docker.internal:27018',priority:0.5},{_id:2,host:'host.docker.internal:27019',priority:0.5}]}) }" | mongosh --port 27017 --quiet - interval: 5s - timeout: 30s - start_period: 0s - start_interval: 1s - retries: 5 + mongodb1: + container_name: mongodb1 + hostname: mongodb1 + image: mongo:latest + restart: always + ports: + - '27017:27017' + links: + - mongodb2 + - mongodb3 + depends_on: + mongodb2: + condition: service_started + mongodb3: + condition: service_started + networks: + - app-network + volumes: + - mongodb1_data:/data/db + - mongodb1_config:/data/configdb + command: mongod --bind_ip_all --replSet rs0 --port 27017 + extra_hosts: + - 'host.docker.internal:host-gateway' + healthcheck: + test: echo "try { rs.status() } catch (err) { rs.initiate({_id:'rs0',members:[{_id:0,host:'host.docker.internal:27017',priority:1},{_id:1,host:'host.docker.internal:27018',priority:0.5},{_id:2,host:'host.docker.internal:27019',priority:0.5}]}) }" | mongosh --bind_ip_all --port 27017 --replSet rs0doc --quiet + interval: 5s + timeout: 30s + start_period: 0s + start_interval: 1s + retries: 5 - mongodb2: - container_name: mongodb2 - hostname: mongodb2 - image: mongo:latest - networks: - - app-network - restart: always - ports: - - "27018:27018" - volumes: - - mongodb2_data:/data/db - - mongodb2_config:/data/configdb - command: mongod --bind_ip_all --replSet rs0 --port 27018 + mongodb2: + container_name: mongodb2 + hostname: mongodb2 + image: mongo:latest + networks: + - app-network + restart: always + ports: + - '27018:27018' + volumes: + - mongodb2_data:/data/db + - mongodb2_config:/data/configdb + command: mongod --bind_ip_all --replSet rs0 --port 27018 - mongodb3: - container_name: mongodb3 - hostname: mongodb3 - image: mongo:latest - networks: - - app-network - restart: always - ports: - - "27019:27019" - volumes: - - mongodb3_data:/data/db - - mongodb3_config:/data/configdb - command: mongod --bind_ip_all --replSet rs0 --port 27019 + mongodb3: + container_name: mongodb3 + hostname: mongodb3 + image: mongo:latest + networks: + - app-network + restart: always + ports: + - '27019:27019' + volumes: + - mongodb3_data:/data/db + - mongodb3_config:/data/configdb + command: mongod --bind_ip_all --replSet rs0 --port 27019 volumes: - mongodb1_data: - mongodb2_data: - mongodb3_data: - mongodb1_config: - mongodb2_config: - mongodb3_config: - redis_data: + mongodb1_data: + mongodb2_data: + mongodb3_data: + mongodb1_config: + mongodb2_config: + mongodb3_config: + redis_data: networks: - app-network: - driver: bridge \ No newline at end of file + app-network: + driver: bridge diff --git a/eslint.config.mjs b/eslint.config.mjs index daf660973..79e758c50 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -42,6 +42,19 @@ export default [ rules: { ...rules, '@typescript-eslint/no-explicit-any': 'off', + 'no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': [ + 'warn', + { + args: 'all', + argsIgnorePattern: '^_', + caughtErrors: 'all', + caughtErrorsIgnorePattern: '^_', + destructuredArrayIgnorePattern: '^_', + varsIgnorePattern: '^_', + ignoreRestSiblings: true, + }, + ], }, }, { @@ -66,6 +79,19 @@ export default [ rules: { ...rules, '@typescript-eslint/no-explicit-any': 'off', + 'no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': [ + 'warn', + { + args: 'all', + argsIgnorePattern: '^_', + caughtErrors: 'all', + caughtErrorsIgnorePattern: '^_', + destructuredArrayIgnorePattern: '^_', + varsIgnorePattern: '^_', + ignoreRestSiblings: true, + }, + ], }, }, ]; diff --git a/nest-cli.json b/nest-cli.json index d40c3532b..db9bda508 100644 --- a/nest-cli.json +++ b/nest-cli.json @@ -2,14 +2,20 @@ "collection": "@nestjs/schematics", "sourceRoot": "src", "compilerOptions": { + "builder": { + "type": "swc", + "typecCheck": true, + "options": { + "swcrcPath": ".swcrc" + } + }, "plugins": ["@nestjs/swagger"], "assets": [ { "include": "languages/**/*", - "outDir": "dist/src" + "outDir": "dist" } ], - "webpack": false, "deleteOutDir": true, "watchAssets": true } diff --git a/package.json b/package.json index 853d907ab..d98cce6da 100644 --- a/package.json +++ b/package.json @@ -1,141 +1,150 @@ { - "name": "ack-nestjs-boilerplate", - "version": "7.0.0", - "description": "Ack NestJs Boilerplate", - "repository": { - "type": "git", - "url": "https://github.com/andrechristikan/ack-nestjs-boilerplate.git" - }, - "author": { - "name": "andrechristikan", - "email": "andrechristikan@gmail.com" - }, - "private": true, - "license": "MIT", - "scripts": { - "prepare": "husky", - "clean": "rimraf dist", - "build": "nest build", - "format": "prettier --write '{src,test}/**/*.ts'", - "start": "nest start", - "start:dev": "nest start --watch", - "start:prod": "node dist/src/main", - "lint": "eslint", - "lint:staged": "lint-staged", - "lint:fix": "eslint --fix", - "test": "TZ=UTC jest --config test/jest.json --runInBand --passWithNoTests --forceExit", - "deadcode": "ts-prune --project tsconfig.json --unusedInModule --skip *.json", - "spell": "cspell lint --config cspell.json '{src,test}/**/*.ts' --color --gitignore --no-must-find-files --no-summary --no-progress || true", - "upgrade:package": "ncu -u", - "migrate": "APP_ENV=migration nest start", - "migrate:email": "nestjs-command migrate:email", - "rollback:email": "nestjs-command rollback:email", - "seed": "nestjs-command seed:country && nestjs-command seed:apikey && nestjs-command seed:role && nestjs-command seed:user", - "rollback": "nestjs-command remove:country && nestjs-command remove:apikey && nestjs-command remove:user && nestjs-command remove:role" - }, - "dependencies": { - "@aws-sdk/client-s3": "^3.629.0", - "@aws-sdk/client-ses": "^3.629.0", - "@aws-sdk/s3-request-presigner": "^3.629.0", - "@casl/ability": "^6.7.1", - "@faker-js/faker": "^8.4.1", - "@nestjs/axios": "^3.0.2", - "@nestjs/bullmq": "^10.2.0", - "@nestjs/cache-manager": "^2.2.2", - "@nestjs/common": "^10.4.0", - "@nestjs/config": "^3.2.3", - "@nestjs/core": "^10.4.0", - "@nestjs/jwt": "^10.2.0", - "@nestjs/mongoose": "^10.0.10", - "@nestjs/passport": "^10.0.3", - "@nestjs/platform-express": "^10.4.0", - "@nestjs/swagger": "^7.4.0", - "@nestjs/terminus": "^10.2.3", - "@nestjs/throttler": "^6.1.0", - "@ntegral/nestjs-sentry": "^4.0.1", - "@sentry/node": "^8.25.0", - "axios": "^1.7.3", - "bcryptjs": "^2.4.3", - "bullmq": "^5.12.5", - "cache-manager": "^5.7.6", - "cache-manager-redis-store": "^3.0.1", - "case": "^1.6.3", - "class-transformer": "^0.5.1", - "class-validator": "^0.14.1", - "crypto-js": "^4.2.0", - "google-auth-library": "^9.13.0", - "helmet": "^7.1.0", - "moment": "^2.30.1", - "moment-timezone": "^0.5.45", - "mongoose": "^8.5.2", - "nestjs-command": "^3.1.4", - "nestjs-i18n": "^10.4.5", - "passport": "^0.7.0", - "passport-headerapikey": "^1.2.2", - "passport-jwt": "^4.0.1", - "reflect-metadata": "^0.2.2", - "response-time": "^2.3.2", - "rxjs": "^7.8.1", - "verify-apple-id-token": "^3.1.2", - "xlsx": "^0.18.5", - "yargs": "^17.7.2", - "yarn": "^1.22.22" - }, - "devDependencies": { - "@commitlint/cli": "^19.4.0", - "@commitlint/config-conventional": "^19.2.2", - "@eslint/js": "^9.9.0", - "@golevelup/ts-jest": "^0.5.2", - "@nestjs/cli": "^10.4.4", - "@nestjs/schematics": "^10.1.3", - "@nestjs/testing": "^10.4.0", - "@types/bcryptjs": "^2.4.6", - "@types/bytes": "^3.1.4", - "@types/cors": "^2.8.17", - "@types/crypto-js": "^4.2.2", - "@types/eslint__js": "^8.42.3", - "@types/express": "^4.17.21", - "@types/jest": "^29.5.12", - "@types/lodash": "^4.17.7", - "@types/ms": "^0.7.34", - "@types/multer": "^1.4.11", - "@types/node": "^22.2.0", - "@types/passport-jwt": "^4.0.1", - "@types/response-time": "^2.3.8", - "@types/supertest": "^6.0.2", - "@types/uuid": "^10.0.0", - "cspell": "^8.13.3", - "eslint": "^9.9.0", - "eslint-config-prettier": "^9.1.0", - "husky": "^9.1.4", - "jest": "^29.7.0", - "lint-staged": "^15.2.9", - "prettier": "^3.3.3", - "rxjs-marbles": "^7.0.1", - "stop-only": "^3.3.3", - "supertest": "^7.0.0", - "ts-jest": "^29.2.4", - "ts-loader": "^9.5.1", - "ts-node": "^10.9.2", - "ts-prune": "^0.10.3", - "tsconfig-paths": "^4.2.0", - "typescript": "^5.5.4", - "typescript-eslint": "^8.1.0" - }, - "lint-staged": { - "**/*.ts": [ - "prettier --write '{src,test}/**/*.ts'", - "eslint", - "stop-only --file" - ], - "*.{json,yaml}": [ - "prettier --write" - ] - }, - "packageManager": "yarn@1.22.21", - "engines": { - "npm": "please-use-yarn", - "yarn": ">= 1.22.21", - "node": ">= 20.11.0" - } + "name": "ack-nestjs-boilerplate", + "version": "7.1.0", + "description": "Ack NestJs Boilerplate", + "repository": { + "type": "git", + "url": "https://github.com/andrechristikan/ack-nestjs-boilerplate.git" + }, + "author": { + "name": "andrechristikan", + "email": "andrechristikan@gmail.com" + }, + "private": true, + "license": "MIT", + "scripts": { + "prepare": "husky", + "clean": "rimraf dist", + "build": "nest build", + "format": "prettier --write '{src,test}/**/*.{ts,json,yml,yaml}' && prettier --write '*.{ts,json,yml,yaml,mjs}'", + "start": "nest start", + "start:dev": "nest start --watch", + "start:prod": "node dist/src/main", + "lint": "eslint", + "lint:staged": "lint-staged", + "lint:fix": "eslint --fix", + "test": "TZ=UTC jest --config test/jest.json --runInBand --passWithNoTests --forceExit", + "deadcode": "ts-prune --project tsconfig.json --unusedInModule --skip *.json", + "spell": "cspell lint --config cspell.json '{src,test}/**/*.ts' --color --gitignore --no-must-find-files --no-summary --no-progress || true", + "package:upgrade": "ncu -u", + "package:check": "ncu -i", + "migrate:schema": "APP_ENV=migration nest start", + "migrate:template": "nestjs-command migrate:template", + "rollback:template": "nestjs-command rollback:template", + "seed": "nestjs-command seed:country && nestjs-command seed:apikey && nestjs-command seed:role && nestjs-command seed:user", + "remove": "nestjs-command remove:country && nestjs-command remove:apikey && nestjs-command remove:user && nestjs-command remove:role" + }, + "dependencies": { + "@aws-sdk/client-s3": "^3.685.0", + "@aws-sdk/client-ses": "^3.682.0", + "@aws-sdk/s3-request-presigner": "^3.685.0", + "@casl/ability": "^6.7.2", + "@faker-js/faker": "^9.1.0", + "@nestjs/axios": "^3.1.1", + "@nestjs/bullmq": "^10.2.2", + "@nestjs/cache-manager": "^2.3.0", + "@nestjs/common": "^10.4.6", + "@nestjs/config": "^3.3.0", + "@nestjs/core": "^10.4.6", + "@nestjs/jwt": "^10.2.0", + "@nestjs/mongoose": "^10.1.0", + "@nestjs/passport": "^10.0.3", + "@nestjs/platform-express": "^10.4.6", + "@nestjs/swagger": "^8.0.1", + "@nestjs/terminus": "^10.2.3", + "@nestjs/throttler": "^6.2.1", + "@sentry/nestjs": "^8.36.0", + "@sentry/profiling-node": "^8.36.0", + "axios": "^1.7.7", + "bcryptjs": "^2.4.3", + "bullmq": "^5.23.0", + "cache-manager": "^4.1.0", + "cache-manager-redis-store": "^3.0.1", + "case": "^1.6.3", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.1", + "compression": "^1.7.5", + "crypto-js": "^4.2.0", + "google-auth-library": "^9.14.2", + "helmet": "^8.0.0", + "luxon": "^3.5.0", + "mongoose": "^8.8.0", + "nestjs-command": "^3.1.4", + "nestjs-i18n": "^10.4.9", + "nestjs-pino": "^4.1.0", + "passport": "^0.7.0", + "passport-headerapikey": "^1.2.2", + "passport-jwt": "^4.0.1", + "pino-http": "^10.3.0", + "pino-pretty": "^11.3.0", + "reflect-metadata": "^0.2.2", + "response-time": "^2.3.3", + "rxjs": "^7.8.1", + "verify-apple-id-token": "^3.1.2", + "xlsx": "^0.18.5", + "yargs": "^17.7.2", + "yarn": "^1.22.22" + }, + "devDependencies": { + "@commitlint/cli": "^19.5.0", + "@commitlint/config-conventional": "^19.5.0", + "@eslint/js": "^9.14.0", + "@golevelup/ts-jest": "^0.5.6", + "@nestjs/cli": "^10.4.5", + "@nestjs/schematics": "^10.2.3", + "@nestjs/testing": "^10.4.6", + "@swc/cli": "^0.5.0", + "@swc/core": "^1.7.42", + "@swc/jest": "^0.2.37", + "@types/bcryptjs": "^2.4.6", + "@types/bytes": "^3.1.4", + "@types/compression": "^1.7.5", + "@types/cors": "^2.8.17", + "@types/crypto-js": "^4.2.2", + "@types/eslint__js": "^8.42.3", + "@types/express": "^5.0.0", + "@types/jest": "^29.5.14", + "@types/lodash": "^4.17.13", + "@types/luxon": "^3.4.2", + "@types/ms": "^0.7.34", + "@types/multer": "^1.4.12", + "@types/node": "^22.8.7", + "@types/passport-jwt": "^4.0.1", + "@types/response-time": "^2.3.8", + "@types/supertest": "^6.0.2", + "@types/uuid": "^10.0.0", + "cspell": "^8.15.6", + "eslint": "^9.14.0", + "eslint-config-prettier": "^9.1.0", + "husky": "^9.1.6", + "jest": "^29.7.0", + "lint-staged": "^15.2.10", + "prettier": "^3.3.3", + "rxjs-marbles": "^7.0.1", + "stop-only": "^3.4.1", + "supertest": "^7.0.0", + "ts-jest": "^29.2.5", + "ts-loader": "^9.5.1", + "ts-node": "^10.9.2", + "ts-prune": "^0.10.3", + "tsconfig-paths": "^4.2.0", + "typescript": "^5.6.3", + "typescript-eslint": "^8.12.2" + }, + "lint-staged": { + "**/*.ts": [ + "prettier --write '{src,test}/**/*.{ts,json,yml,yaml}'", + "eslint", + "stop-only --file" + ], + "*.{json,yaml}": [ + "prettier --write" + ] + }, + "packageManager": "yarn@1.22.22", + "engines": { + "npm": "please-use-yarn", + "yarn": ">= 1.22.22", + "node": ">= 20.11.0" + } } diff --git a/src/app/app.middleware.module.ts b/src/app/app.middleware.module.ts index febb5726d..9713e6a33 100644 --- a/src/app/app.middleware.module.ts +++ b/src/app/app.middleware.module.ts @@ -1,9 +1,4 @@ -import { - LogLevel, - MiddlewareConsumer, - Module, - NestModule, -} from '@nestjs/common'; +import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common'; import { ConfigModule, ConfigService } from '@nestjs/config'; import { APP_FILTER, APP_GUARD } from '@nestjs/core'; import { @@ -11,8 +6,6 @@ import { ThrottlerModule, ThrottlerModuleOptions, } from '@nestjs/throttler'; -import { SentryModule } from '@ntegral/nestjs-sentry'; -import { ENUM_APP_ENVIRONMENT } from 'src/app/enums/app.enum'; import { AppGeneralFilter } from 'src/app/filters/app.general.filter'; import { AppHttpFilter } from 'src/app/filters/app.http.filter'; import { AppValidationImportFilter } from 'src/app/filters/app.validation-import.filter'; @@ -28,6 +21,8 @@ import { AppCustomLanguageMiddleware } from 'src/app/middlewares/app.custom-lang import { AppHelmetMiddleware } from 'src/app/middlewares/app.helmet.middleware'; import { AppResponseTimeMiddleware } from 'src/app/middlewares/app.response-time.middleware'; import { AppUrlVersionMiddleware } from 'src/app/middlewares/app.url-version.middleware'; +import { SentryModule } from '@sentry/nestjs/setup'; +import { AppRequestIdMiddleware } from 'src/app/middlewares/app.request-id.middleware'; @Module({ controllers: [], @@ -55,41 +50,26 @@ import { AppUrlVersionMiddleware } from 'src/app/middlewares/app.url-version.mid }, ], imports: [ + SentryModule.forRoot(), ThrottlerModule.forRootAsync({ imports: [ConfigModule], inject: [ConfigService], useFactory: (config: ConfigService): ThrottlerModuleOptions => ({ throttlers: [ { - ttl: config.get('middleware.throttle.ttl'), - limit: config.get('middleware.throttle.limit'), + ttl: config.get('middleware.throttle.ttl'), + limit: config.get('middleware.throttle.limit'), }, ], }), }), - SentryModule.forRootAsync({ - inject: [ConfigService], - imports: [ConfigModule], - useFactory: async (configService: ConfigService) => ({ - dsn: configService.get('debug.sentry.dsn'), - debug: false, - environment: configService.get('app.env'), - release: configService.get('app.repoVersion'), - logLevels: configService.get( - 'debug.sentry.logLevels.exception' - ), - close: { - enabled: true, - timeout: configService.get('debug.sentry.timeout'), - }, - }), - }), ], }) export class AppMiddlewareModule implements NestModule { configure(consumer: MiddlewareConsumer): void { consumer .apply( + AppRequestIdMiddleware, AppHelmetMiddleware, AppJsonBodyParserMiddleware, AppTextBodyParserMiddleware, diff --git a/src/app/dtos/app.env.dto.ts b/src/app/dtos/app.env.dto.ts index 5ce8b2e81..cbc43fbaf 100644 --- a/src/app/dtos/app.env.dto.ts +++ b/src/app/dtos/app.env.dto.ts @@ -34,16 +34,6 @@ export class AppEnvDto { @IsEnum(ENUM_APP_TIMEZONE) APP_TIMEZONE: ENUM_APP_TIMEZONE; - @IsBoolean() - @IsNotEmpty() - @Type(() => Boolean) - APP_DEBUG: boolean; - - @IsBoolean() - @IsNotEmpty() - @Type(() => Boolean) - HTTP_ENABLE: boolean; - @IsNotEmpty() @IsString() HTTP_HOST: string; @@ -53,6 +43,15 @@ export class AppEnvDto { @Type(() => Number) HTTP_PORT: number; + @IsBoolean() + @IsNotEmpty() + @Type(() => Boolean) + DEBUG_ENABLE: boolean; + + @IsString() + @IsNotEmpty() + MIDDLEWARE_CORS_ORIGIN: string; + @IsBoolean() @IsNotEmpty() @Type(() => Boolean) @@ -98,19 +97,39 @@ export class AppEnvDto { @IsOptional() @IsString() - AWS_S3_CREDENTIAL_KEY?: string; + AWS_S3_PUBLIC_CREDENTIAL_KEY?: string; @IsOptional() @IsString() - AWS_S3_CREDENTIAL_SECRET?: string; + AWS_S3_PUBLIC_CREDENTIAL_SECRET?: string; @IsOptional() @IsString() - AWS_S3_REGION?: string; + AWS_S3_PUBLIC_REGION?: string; @IsOptional() @IsString() - AWS_S3_BUCKET?: string; + AWS_S3_PUBLIC_BUCKET?: string; + + @IsOptional() + @IsString() + AWS_S3_PUBLIC_CDN?: string; + + @IsOptional() + @IsString() + AWS_S3_PRIVATE_CREDENTIAL_KEY?: string; + + @IsOptional() + @IsString() + AWS_S3_PRIVATE_CREDENTIAL_SECRET?: string; + + @IsOptional() + @IsString() + AWS_S3_PRIVATE_REGION?: string; + + @IsOptional() + @IsString() + AWS_S3_PRIVATE_BUCKET?: string; @IsOptional() @IsString() @@ -151,12 +170,11 @@ export class AppEnvDto { @IsOptional() @IsString() - REDIS_PASSWORD?: string; + REDIS_USERNAME?: string; - @IsNotEmpty() - @IsBoolean() - @Type(() => Boolean) - REDIS_TLS: boolean; + @IsOptional() + @IsString() + REDIS_PASSWORD?: string; @IsOptional() @IsString() diff --git a/src/app/enums/app.enum.ts b/src/app/enums/app.enum.ts index 9f8844a33..e494e76c5 100644 --- a/src/app/enums/app.enum.ts +++ b/src/app/enums/app.enum.ts @@ -3,6 +3,7 @@ export enum ENUM_APP_ENVIRONMENT { MIGRATION = 'migration', STAGING = 'staging', DEVELOPMENT = 'development', + LOCAL = 'local', } export enum ENUM_APP_TIMEZONE { diff --git a/src/app/filters/app.general.filter.ts b/src/app/filters/app.general.filter.ts index 059a75455..3f5245601 100644 --- a/src/app/filters/app.general.filter.ts +++ b/src/app/filters/app.general.filter.ts @@ -4,39 +4,33 @@ import { ArgumentsHost, HttpException, HttpStatus, - Optional, InternalServerErrorException, Logger, } from '@nestjs/common'; import { HttpArgumentsHost } from '@nestjs/common/interfaces'; import { ConfigService } from '@nestjs/config'; import { HttpAdapterHost } from '@nestjs/core'; -import { InjectSentry, SentryService } from '@ntegral/nestjs-sentry'; import { Response } from 'express'; import { IAppException } from 'src/app/interfaces/app.interface'; import { FileImportException } from 'src/common/file/exceptions/file.import.exception'; import { HelperDateService } from 'src/common/helper/services/helper.date.service'; +import { ENUM_MESSAGE_LANGUAGE } from 'src/common/message/enums/message.enum'; import { MessageService } from 'src/common/message/services/message.service'; import { RequestValidationException } from 'src/common/request/exceptions/request.validation.exception'; import { IRequestApp } from 'src/common/request/interfaces/request.interface'; import { ResponseMetadataDto } from 'src/common/response/dtos/response.dto'; +import * as Sentry from '@sentry/nestjs'; @Catch() export class AppGeneralFilter implements ExceptionFilter { - private readonly debug: boolean; private readonly logger = new Logger(AppGeneralFilter.name); constructor( - @Optional() - @InjectSentry() - private readonly sentryService: SentryService, private readonly httpAdapterHost: HttpAdapterHost, private readonly messageService: MessageService, private readonly configService: ConfigService, private readonly helperDateService: HelperDateService - ) { - this.debug = this.configService.get('app.debug'); - } + ) {} async catch(exception: unknown, host: ArgumentsHost): Promise { const { httpAdapter } = this.httpAdapterHost; @@ -45,9 +39,7 @@ export class AppGeneralFilter implements ExceptionFilter { const response: Response = ctx.getResponse(); const request: IRequestApp = ctx.getRequest(); - if (this.debug) { - this.logger.error(exception); - } + this.logger.error(exception); // sentry this.sendToSentry(exception); @@ -66,10 +58,12 @@ export class AppGeneralFilter implements ExceptionFilter { const statusCode = HttpStatus.INTERNAL_SERVER_ERROR; // metadata + const today = this.helperDateService.create(); const xLanguage: string = - request.__language ?? this.messageService.getLanguage(); - const xTimestamp = this.helperDateService.createTimestamp(); - const xTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone; + request.__language ?? + this.configService.get('message.language'); + const xTimestamp = this.helperDateService.getTimestamp(today); + const xTimezone = this.helperDateService.getZone(today); const xVersion = request.__version ?? this.configService.get('app.urlVersion.version'); @@ -116,11 +110,9 @@ export class AppGeneralFilter implements ExceptionFilter { } try { - this.sentryService.instance().captureException(exception); + Sentry.captureException(exception); } catch (err: unknown) { - if (this.debug) { - this.logger.error(err); - } + this.logger.error(err); } return; diff --git a/src/app/filters/app.http.filter.ts b/src/app/filters/app.http.filter.ts index 12914c800..842e04d24 100644 --- a/src/app/filters/app.http.filter.ts +++ b/src/app/filters/app.http.filter.ts @@ -9,8 +9,10 @@ import { import { HttpArgumentsHost } from '@nestjs/common/interfaces'; import { ConfigService } from '@nestjs/config'; import { Response } from 'express'; +import { ENUM_APP_ENVIRONMENT } from 'src/app/enums/app.enum'; import { IAppException } from 'src/app/interfaces/app.interface'; import { HelperDateService } from 'src/common/helper/services/helper.date.service'; +import { ENUM_MESSAGE_LANGUAGE } from 'src/common/message/enums/message.enum'; import { IMessageOptionsProperties } from 'src/common/message/interfaces/message.interface'; import { MessageService } from 'src/common/message/services/message.service'; import { IRequestApp } from 'src/common/request/interfaces/request.interface'; @@ -18,19 +20,20 @@ import { ResponseMetadataDto } from 'src/common/response/dtos/response.dto'; @Catch(HttpException) export class AppHttpFilter implements ExceptionFilter { - private readonly debug: boolean; + private readonly logger = new Logger(AppHttpFilter.name); + private readonly globalPrefix: string; private readonly docPrefix: string; - private readonly logger = new Logger(AppHttpFilter.name); + private readonly env: ENUM_APP_ENVIRONMENT; constructor( private readonly messageService: MessageService, private readonly configService: ConfigService, private readonly helperDateService: HelperDateService ) { - this.debug = this.configService.get('app.debug'); this.globalPrefix = this.configService.get('app.globalPrefix'); this.docPrefix = this.configService.get('doc.prefix'); + this.env = this.configService.get('app.env'); } async catch(exception: HttpException, host: ArgumentsHost): Promise { @@ -38,15 +41,23 @@ export class AppHttpFilter implements ExceptionFilter { const response: Response = ctx.getResponse(); const request: IRequestApp = ctx.getRequest(); - if (this.debug) { - this.logger.error(exception); - } + this.logger.error(exception); if ( !request.path.startsWith(this.globalPrefix) && !request.path.startsWith(this.docPrefix) ) { - response.redirect(HttpStatus.PERMANENT_REDIRECT, this.docPrefix); + if (this.env === ENUM_APP_ENVIRONMENT.PRODUCTION) { + response.redirect( + HttpStatus.PERMANENT_REDIRECT, + '/api/public/hello' + ); + } else { + response.redirect( + HttpStatus.PERMANENT_REDIRECT, + this.docPrefix + ); + } return; } @@ -59,10 +70,12 @@ export class AppHttpFilter implements ExceptionFilter { let data: Record; // metadata + const today = this.helperDateService.create(); const xLanguage: string = - request.__language ?? this.messageService.getLanguage(); - const xTimestamp = this.helperDateService.createTimestamp(); - const xTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone; + request.__language ?? + this.configService.get('message.language'); + const xTimestamp = this.helperDateService.getTimestamp(today); + const xTimezone = this.helperDateService.getZone(today); const xVersion = request.__version ?? this.configService.get('app.urlVersion.version'); diff --git a/src/app/filters/app.validation-import.filter.ts b/src/app/filters/app.validation-import.filter.ts index f098f83f9..a67a925b6 100644 --- a/src/app/filters/app.validation-import.filter.ts +++ b/src/app/filters/app.validation-import.filter.ts @@ -5,6 +5,7 @@ import { Response } from 'express'; import { IAppImportException } from 'src/app/interfaces/app.interface'; import { FileImportException } from 'src/common/file/exceptions/file.import.exception'; import { HelperDateService } from 'src/common/helper/services/helper.date.service'; +import { ENUM_MESSAGE_LANGUAGE } from 'src/common/message/enums/message.enum'; import { IMessageValidationImportError, IMessageValidationImportErrorParam, @@ -15,16 +16,13 @@ import { ResponseMetadataDto } from 'src/common/response/dtos/response.dto'; @Catch(FileImportException) export class AppValidationImportFilter implements ExceptionFilter { - private readonly debug: boolean; private readonly logger = new Logger(AppValidationImportFilter.name); constructor( private readonly messageService: MessageService, private readonly configService: ConfigService, private readonly helperDateService: HelperDateService - ) { - this.debug = this.configService.get('app.debug'); - } + ) {} async catch( exception: FileImportException, @@ -34,15 +32,15 @@ export class AppValidationImportFilter implements ExceptionFilter { const response: Response = ctx.getResponse(); const request: IRequestApp = ctx.getRequest(); - if (this.debug) { - this.logger.error(exception); - } + this.logger.error(exception); // metadata + const today = this.helperDateService.create(); const xLanguage: string = - request.__language ?? this.messageService.getLanguage(); - const xTimestamp = this.helperDateService.createTimestamp(); - const xTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone; + request.__language ?? + this.configService.get('message.language'); + const xTimestamp = this.helperDateService.getTimestamp(today); + const xTimezone = this.helperDateService.getZone(today); const xVersion = request.__version ?? this.configService.get('app.urlVersion.version'); diff --git a/src/app/filters/app.validation.filter.ts b/src/app/filters/app.validation.filter.ts index 9275f462d..3d5d857b4 100644 --- a/src/app/filters/app.validation.filter.ts +++ b/src/app/filters/app.validation.filter.ts @@ -4,6 +4,7 @@ import { ConfigService } from '@nestjs/config'; import { Response } from 'express'; import { IAppException } from 'src/app/interfaces/app.interface'; import { HelperDateService } from 'src/common/helper/services/helper.date.service'; +import { ENUM_MESSAGE_LANGUAGE } from 'src/common/message/enums/message.enum'; import { IMessageValidationError } from 'src/common/message/interfaces/message.interface'; import { MessageService } from 'src/common/message/services/message.service'; import { RequestValidationException } from 'src/common/request/exceptions/request.validation.exception'; @@ -12,16 +13,13 @@ import { ResponseMetadataDto } from 'src/common/response/dtos/response.dto'; @Catch(RequestValidationException) export class AppValidationFilter implements ExceptionFilter { - private readonly debug: boolean; private readonly logger = new Logger(AppValidationFilter.name); constructor( private readonly messageService: MessageService, private readonly configService: ConfigService, private readonly helperDateService: HelperDateService - ) { - this.debug = this.configService.get('app.debug'); - } + ) {} async catch( exception: RequestValidationException, @@ -31,15 +29,15 @@ export class AppValidationFilter implements ExceptionFilter { const response: Response = ctx.getResponse(); const request: IRequestApp = ctx.getRequest(); - if (this.debug) { - this.logger.error(exception); - } + this.logger.error(exception); // metadata + const today = this.helperDateService.create(); const xLanguage: string = - request.__language ?? this.messageService.getLanguage(); - const xTimestamp = this.helperDateService.createTimestamp(); - const xTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone; + request.__language ?? + this.configService.get('message.language'); + const xTimestamp = this.helperDateService.getTimestamp(today); + const xTimezone = this.helperDateService.getZone(today); const xVersion = request.__version ?? this.configService.get('app.urlVersion.version'); diff --git a/src/app/middlewares/app.cors.middleware.ts b/src/app/middlewares/app.cors.middleware.ts index 60cee28a4..f6d7b7255 100644 --- a/src/app/middlewares/app.cors.middleware.ts +++ b/src/app/middlewares/app.cors.middleware.ts @@ -25,13 +25,8 @@ export class AppCorsMiddleware implements NestMiddleware { } use(req: Request, res: Response, next: NextFunction): void { - const allowOrigin = - this.appEnv === ENUM_APP_ENVIRONMENT.PRODUCTION - ? this.allowOrigin - : '*'; - const corsOptions: CorsOptions = { - origin: allowOrigin, + origin: this.allowOrigin, methods: this.allowMethod, allowedHeaders: this.allowHeader, preflightContinue: false, diff --git a/src/app/middlewares/app.custom-language.middleware.ts b/src/app/middlewares/app.custom-language.middleware.ts index a38794839..29168059a 100644 --- a/src/app/middlewares/app.custom-language.middleware.ts +++ b/src/app/middlewares/app.custom-language.middleware.ts @@ -19,7 +19,7 @@ export class AppCustomLanguageMiddleware implements NestMiddleware { async use( req: IRequestApp, - res: Response, + _res: Response, next: NextFunction ): Promise { let customLang: string = diff --git a/src/app/middlewares/app.request-id.middleware.ts b/src/app/middlewares/app.request-id.middleware.ts new file mode 100644 index 000000000..791af12dd --- /dev/null +++ b/src/app/middlewares/app.request-id.middleware.ts @@ -0,0 +1,12 @@ +import { Injectable, NestMiddleware } from '@nestjs/common'; +import { Request, Response, NextFunction } from 'express'; +import { v4 as uuid } from 'uuid'; + +@Injectable() +export class AppRequestIdMiddleware implements NestMiddleware { + use(req: Request, _res: Response, next: NextFunction): void { + req.id = uuid(); + + next(); + } +} diff --git a/src/app/middlewares/app.url-version.middleware.ts b/src/app/middlewares/app.url-version.middleware.ts index 2a90e1f33..a02770e77 100644 --- a/src/app/middlewares/app.url-version.middleware.ts +++ b/src/app/middlewares/app.url-version.middleware.ts @@ -31,7 +31,7 @@ export class AppUrlVersionMiddleware implements NestMiddleware { async use( req: IRequestApp, - res: Response, + _res: Response, next: NextFunction ): Promise { const originalUrl: string = req.originalUrl; @@ -43,11 +43,7 @@ export class AppUrlVersionMiddleware implements NestMiddleware { ) ) { const url: string[] = originalUrl.split('/'); - if (this.env === ENUM_APP_ENVIRONMENT.PRODUCTION) { - version = url[1].replace(this.urlVersionPrefix, ''); - } else { - version = url[2].replace(this.urlVersionPrefix, ''); - } + version = url[2].replace(this.urlVersionPrefix, ''); } req.__version = version; diff --git a/src/cli.ts b/src/cli.ts index ccb49c0b1..b233cd93a 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -5,7 +5,7 @@ import { MigrationModule } from 'src/migration/migration.module'; async function bootstrap() { const app = await NestFactory.createApplicationContext(MigrationModule, { - logger: ['error'], + logger: ['error', 'fatal'], }); const logger = new Logger('NestJs-Seed'); diff --git a/src/common/common.module.ts b/src/common/common.module.ts index d82c7a932..722b6b07a 100644 --- a/src/common/common.module.ts +++ b/src/common/common.module.ts @@ -1,8 +1,10 @@ import { Module } from '@nestjs/common'; import { MongooseModule } from '@nestjs/mongoose'; import { DATABASE_CONNECTION_NAME } from 'src/common/database/constants/database.constant'; -import { DatabaseModule } from 'src/common/database/database.module'; -import { DatabaseService } from 'src/common/database/services/database.service'; +import { + DatabaseModule, + DatabaseOptionModule, +} from 'src/common/database/database.module'; import { MessageModule } from 'src/common/message/message.module'; import { HelperModule } from 'src/common/helper/helper.module'; import { RequestModule } from 'src/common/request/request.module'; @@ -13,15 +15,18 @@ import configs from 'src/configs'; import { ApiKeyModule } from 'src/modules/api-key/api-key.module'; import { PaginationModule } from 'src/common/pagination/pagination.module'; import { FileModule } from 'src/common/file/file.module'; +import { BullModule } from '@nestjs/bullmq'; +import { CacheModule, CacheOptions, CacheStore } from '@nestjs/cache-manager'; import { redisStore } from 'cache-manager-redis-store'; -import { CacheModule, CacheStore } from '@nestjs/cache-manager'; -import type { RedisClientOptions } from 'redis'; +import { DatabaseOptionService } from 'src/common/database/services/database.options.service'; +import { LoggerModule as PinoLoggerModule } from 'nestjs-pino'; +import { LoggerOptionModule } from 'src/common/logger/logger.option.module'; +import { LoggerOptionService } from 'src/common/logger/services/logger.option.service'; @Module({ controllers: [], providers: [], imports: [ - // Config ConfigModule.forRoot({ load: configs, isGlobal: true, @@ -31,27 +36,65 @@ import type { RedisClientOptions } from 'redis'; }), MongooseModule.forRootAsync({ connectionName: DATABASE_CONNECTION_NAME, - imports: [DatabaseModule], - inject: [DatabaseService], - useFactory: (databaseService: DatabaseService) => + imports: [DatabaseOptionModule], + inject: [DatabaseOptionService], + useFactory: (databaseService: DatabaseOptionService) => databaseService.createOptions(), }), - CacheModule.registerAsync({ + BullModule.forRootAsync({ imports: [ConfigModule], inject: [ConfigService], + useFactory: (configService: ConfigService) => ({ + connection: { + host: configService.get('redis.queue.host'), + port: configService.get('redis.queue.port'), + username: configService.get('redis.queue.username'), + password: configService.get('redis.queue.password'), + tls: configService.get('redis.queue.tls'), + }, + defaultJobOptions: { + backoff: { + type: 'exponential', + delay: 3000, + }, + attempts: 3, + }, + }), + }), + CacheModule.registerAsync({ isGlobal: true, - useFactory: async (configService: ConfigService) => ({ - store: (await redisStore({ + imports: [ConfigModule], + useFactory: async ( + configService: ConfigService + ): Promise => { + const store = await redisStore({ socket: { - host: configService.get('redis.host'), - port: configService.get('redis.port'), - tls: configService.get('redis.tls'), + host: configService.get('redis.cached.host'), + port: configService.get('redis.cached.port'), + tls: configService.get('redis.cached.tls'), }, - username: configService.get('redis.username'), - password: configService.get('redis.password'), + username: configService.get( + 'redis.cached.username' + ), + password: configService.get( + 'redis.cached.password' + ), + }); + + return { + store: store as any as CacheStore, + max: configService.get('redis.cached.max'), ttl: configService.get('redis.cached.ttl'), - })) as unknown as CacheStore, - }), + }; + }, + inject: [ConfigService], + }), + PinoLoggerModule.forRootAsync({ + imports: [LoggerOptionModule], + inject: [LoggerOptionService], + useFactory: async (loggerOptionService: LoggerOptionService) => { + return loggerOptionService.createOptions(); + }, }), MessageModule.forRoot(), HelperModule.forRoot(), @@ -59,8 +102,9 @@ import type { RedisClientOptions } from 'redis'; PolicyModule.forRoot(), AuthModule.forRoot(), ApiKeyModule.forRoot(), - PaginationModule.forRoot(), FileModule.forRoot(), + DatabaseModule.forRoot(), + PaginationModule.forRoot(), ], }) export class CommonModule {} diff --git a/src/common/database/abstracts/database.entity.abstract.ts b/src/common/database/bases/database.entity.ts similarity index 95% rename from src/common/database/abstracts/database.entity.abstract.ts rename to src/common/database/bases/database.entity.ts index 313ce0249..b758f5ced 100644 --- a/src/common/database/abstracts/database.entity.abstract.ts +++ b/src/common/database/bases/database.entity.ts @@ -1,7 +1,7 @@ import { DatabaseProp } from 'src/common/database/decorators/database.decorator'; import { v4 as uuidV4 } from 'uuid'; -export abstract class DatabaseEntityAbstract { +export class DatabaseEntityBase { @DatabaseProp({ type: String, default: uuidV4, diff --git a/src/common/database/abstracts/database.repository.abstract.ts b/src/common/database/bases/database.repository.ts similarity index 95% rename from src/common/database/abstracts/database.repository.abstract.ts rename to src/common/database/bases/database.repository.ts index 10d6579ab..9fe7cb166 100644 --- a/src/common/database/abstracts/database.repository.abstract.ts +++ b/src/common/database/bases/database.repository.ts @@ -5,7 +5,6 @@ import { UpdateQuery, UpdateWithAggregationPipeline, } from 'mongoose'; -import { DatabaseEntityAbstract } from 'src/common/database/abstracts/database.entity.abstract'; import { IDatabaseAggregateOptions, IDatabaseCreateManyOptions, @@ -13,9 +12,9 @@ import { IDatabaseDeleteManyOptions, IDatabaseDeleteOptions, IDatabaseDocument, - IDatabaseExistOptions, IDatabaseFindAllAggregateOptions, IDatabaseFindAllOptions, + IDatabaseFindOneOptions, IDatabaseGetTotalOptions, IDatabaseOptions, IDatabaseSaveOptions, @@ -25,9 +24,10 @@ import { import MongoDB from 'mongodb'; import { ENUM_PAGINATION_ORDER_DIRECTION_TYPE } from 'src/common/pagination/enums/pagination.enum'; import { DatabaseSoftDeleteDto } from 'src/common/database/dtos/database.soft-delete.dto'; +import { DatabaseEntityBase } from 'src/common/database/bases/database.entity'; -export abstract class DatabaseRepositoryAbstract< - Entity extends DatabaseEntityAbstract, +export class DatabaseRepositoryBase< + Entity extends DatabaseEntityBase, EntityDocument extends IDatabaseDocument, > { protected readonly _repository: Model; @@ -82,7 +82,7 @@ export abstract class DatabaseRepositoryAbstract< async findOne( find: Record, - options?: IDatabaseOptions + options?: IDatabaseFindOneOptions ): Promise { const repository = this._repository.findOne({ ...find, @@ -103,6 +103,10 @@ export abstract class DatabaseRepositoryAbstract< ); } + if (options?.order) { + repository.sort(options.order); + } + if (options?.session) { repository.session(options.session); } @@ -112,7 +116,7 @@ export abstract class DatabaseRepositoryAbstract< async findOneById( _id: string, - options?: IDatabaseOptions + options?: IDatabaseFindOneOptions ): Promise { const repository = this._repository.findOne({ _id, @@ -133,6 +137,10 @@ export abstract class DatabaseRepositoryAbstract< ); } + if (options?.order) { + repository.sort(options.order); + } + if (options?.session) { repository.session(options.session); } @@ -142,7 +150,7 @@ export abstract class DatabaseRepositoryAbstract< async findOneAndLock( find: Record, - options?: IDatabaseOptions + options?: IDatabaseFindOneOptions ): Promise { const repository = this._repository.findOneAndUpdate( { @@ -169,6 +177,10 @@ export abstract class DatabaseRepositoryAbstract< ); } + if (options?.order) { + repository.sort(options.order); + } + if (options?.session) { repository.session(options.session); } @@ -178,7 +190,7 @@ export abstract class DatabaseRepositoryAbstract< async findOneByIdAndLock( _id: string, - options?: IDatabaseOptions + options?: IDatabaseFindOneOptions ): Promise { const repository = this._repository.findOneAndUpdate( { @@ -205,6 +217,10 @@ export abstract class DatabaseRepositoryAbstract< ); } + if (options?.order) { + repository.sort(options.order); + } + if (options?.session) { repository.session(options.session); } @@ -240,17 +256,8 @@ export abstract class DatabaseRepositoryAbstract< async exists( find: Record, - options?: IDatabaseExistOptions + options?: IDatabaseOptions ): Promise { - if (options?.excludeId) { - find = { - ...find, - _id: { - $nin: options?.excludeId ?? [], - }, - }; - } - const repository = this._repository.exists({ ...find, deleted: options?.withDeleted ?? false, @@ -274,7 +281,7 @@ export abstract class DatabaseRepositoryAbstract< return result ? true : false; } - async create( + async create( data: T, options?: IDatabaseCreateOptions ): Promise { diff --git a/src/common/database/database.module.ts b/src/common/database/database.module.ts index dbcf67bf2..f46cbe102 100644 --- a/src/common/database/database.module.ts +++ b/src/common/database/database.module.ts @@ -1,10 +1,25 @@ -import { Module } from '@nestjs/common'; +import { DynamicModule, Global, Module } from '@nestjs/common'; +import { DatabaseOptionService } from 'src/common/database/services/database.options.service'; import { DatabaseService } from 'src/common/database/services/database.service'; @Module({ - providers: [DatabaseService], - exports: [DatabaseService], + providers: [DatabaseOptionService], + exports: [DatabaseOptionService], imports: [], controllers: [], }) -export class DatabaseModule {} +export class DatabaseOptionModule {} + +@Global() +@Module({}) +export class DatabaseModule { + static forRoot(): DynamicModule { + return { + module: DatabaseModule, + providers: [DatabaseService], + exports: [DatabaseService], + imports: [], + controllers: [], + }; + } +} diff --git a/src/common/database/decorators/database.decorator.ts b/src/common/database/decorators/database.decorator.ts index 3e5865a3e..4e8e2774c 100644 --- a/src/common/database/decorators/database.decorator.ts +++ b/src/common/database/decorators/database.decorator.ts @@ -12,13 +12,13 @@ import { Schema as MongooseSchema } from 'mongoose'; import { DATABASE_CONNECTION_NAME } from 'src/common/database/constants/database.constant'; import { IDatabaseQueryContainOptions } from 'src/common/database/interfaces/database.interface'; -export function DatabaseConnection( +export function InjectDatabaseConnection( connectionName?: string ): ParameterDecorator { return InjectConnection(connectionName ?? DATABASE_CONNECTION_NAME); } -export function DatabaseModel( +export function InjectDatabaseModel( entity: any, connectionName?: string ): ParameterDecorator { @@ -45,48 +45,7 @@ export function DatabaseSchema>( return SchemaFactory.createForClass(entity) as N; } -export function DatabaseQueryIn( - field: string, - values: T[] -): Record { - return { - [field]: { - $in: values, - }, - }; -} -export function DatabaseQueryNin( - field: string, - values: T[] -): Record { - return { - [field]: { - $nin: values, - }, - }; -} - -export function DatabaseQueryEqual( - field: string, - value: T -): Record { - return { - [field]: value, - }; -} - -export function DatabaseQueryNotEqual( - field: string, - value: T -): Record { - return { - [field]: { - $ne: value, - }, - }; -} - -export function DatabaseQueryContain( +export function DatabaseHelperQueryContain( field: string, value: string, options?: IDatabaseQueryContainOptions @@ -107,15 +66,3 @@ export function DatabaseQueryContain( }, }; } - -export function DatabaseQueryOr(queries: Record[]) { - return { - $or: queries, - }; -} - -export function DatabaseQueryAnd(queries: Record[]) { - return { - $and: queries, - }; -} diff --git a/src/common/database/dtos/database.dto.ts b/src/common/database/dtos/database.dto.ts index 3db52163c..a0665dcb7 100644 --- a/src/common/database/dtos/database.dto.ts +++ b/src/common/database/dtos/database.dto.ts @@ -1,5 +1,6 @@ import { faker } from '@faker-js/faker'; -import { ApiProperty } from '@nestjs/swagger'; +import { ApiHideProperty, ApiProperty } from '@nestjs/swagger'; +import { Exclude } from 'class-transformer'; export class DatabaseDto { @ApiProperty({ @@ -46,4 +47,8 @@ export class DatabaseDto { nullable: true, }) deletedBy?: string; + + @ApiHideProperty() + @Exclude() + __v?: string; } diff --git a/src/common/database/interfaces/database.interface.ts b/src/common/database/interfaces/database.interface.ts index e60278c1c..6002e8f5e 100644 --- a/src/common/database/interfaces/database.interface.ts +++ b/src/common/database/interfaces/database.interface.ts @@ -15,6 +15,10 @@ export interface IDatabaseOptions { withDeleted?: boolean; } +export interface IDatabaseFindOneOptions extends IDatabaseOptions { + order?: IPaginationOrder; +} + export type IDatabaseGetTotalOptions = Omit; export interface IDatabaseFindAllPagingOptions { @@ -22,13 +26,8 @@ export interface IDatabaseFindAllPagingOptions { offset: number; } -export interface IDatabaseFindAllOptions extends IDatabaseOptions { - paging: IDatabaseFindAllPagingOptions; - order: IPaginationOrder; -} - -export interface IDatabaseExistOptions extends IDatabaseOptions { - excludeId?: string[]; +export interface IDatabaseFindAllOptions extends IDatabaseFindOneOptions { + paging?: IDatabaseFindAllPagingOptions; } // Action diff --git a/src/common/database/interfaces/database.option-service.interface.ts b/src/common/database/interfaces/database.option-service.interface.ts new file mode 100644 index 000000000..4b8c61927 --- /dev/null +++ b/src/common/database/interfaces/database.option-service.interface.ts @@ -0,0 +1,5 @@ +import { MongooseModuleOptions } from '@nestjs/mongoose'; + +export interface IDatabaseOptionService { + createOptions(): MongooseModuleOptions; +} diff --git a/src/common/database/interfaces/database.service.interface.ts b/src/common/database/interfaces/database.service.interface.ts index 242b337ca..699000290 100644 --- a/src/common/database/interfaces/database.service.interface.ts +++ b/src/common/database/interfaces/database.service.interface.ts @@ -1,5 +1,34 @@ -import { MongooseModuleOptions } from '@nestjs/mongoose'; - export interface IDatabaseService { - createOptions(): MongooseModuleOptions; + filterEqual( + field: string, + filterValue: T + ): Record; + filterNotEqual( + field: string, + filterValue: T + ): Record; + filterContain(field: string, filterValue: string): Record; + filterContainFullMatch( + field: string, + filterValue: string + ): Record; + filterIn( + field: string, + filterValue: T[] + ): Record; + filterNin( + field: string, + filterValue: T[] + ): Record< + string, + { + $nin: T[]; + } + >; + filterDateBetween( + fieldStart: string, + fieldEnd: string, + filterStartValue: Date, + filterEndValue: Date + ): Record; } diff --git a/src/common/database/services/database.options.service.ts b/src/common/database/services/database.options.service.ts new file mode 100644 index 000000000..e4dc765ed --- /dev/null +++ b/src/common/database/services/database.options.service.ts @@ -0,0 +1,35 @@ +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { MongooseModuleOptions } from '@nestjs/mongoose'; +import mongoose from 'mongoose'; +import { ENUM_APP_ENVIRONMENT } from 'src/app/enums/app.enum'; +import { IDatabaseOptionService } from 'src/common/database/interfaces/database.option-service.interface'; + +@Injectable() +export class DatabaseOptionService implements IDatabaseOptionService { + constructor(private readonly configService: ConfigService) {} + + createOptions(): MongooseModuleOptions { + const env = this.configService.get('app.env'); + + const uri = this.configService.get('database.uri'); + const debug = this.configService.get('database.debug'); + + const timeoutOptions = this.configService.get>( + 'database.timeoutOptions' + ); + + if (env !== ENUM_APP_ENVIRONMENT.PRODUCTION) { + mongoose.set('debug', debug); + } + + const mongooseOptions: MongooseModuleOptions = { + uri, + autoCreate: env === ENUM_APP_ENVIRONMENT.MIGRATION, + autoIndex: env === ENUM_APP_ENVIRONMENT.MIGRATION, + ...timeoutOptions, + }; + + return mongooseOptions; + } +} diff --git a/src/common/database/services/database.service.ts b/src/common/database/services/database.service.ts index 59d56f676..5a746bac5 100644 --- a/src/common/database/services/database.service.ts +++ b/src/common/database/services/database.service.ts @@ -1,35 +1,93 @@ import { Injectable } from '@nestjs/common'; -import { MongooseModuleOptions } from '@nestjs/mongoose'; -import mongoose from 'mongoose'; -import { ConfigService } from '@nestjs/config'; import { IDatabaseService } from 'src/common/database/interfaces/database.service.interface'; -import { ENUM_APP_ENVIRONMENT } from 'src/app/enums/app.enum'; +import { DatabaseHelperQueryContain } from 'src/common/database/decorators/database.decorator'; @Injectable() export class DatabaseService implements IDatabaseService { - constructor(private readonly configService: ConfigService) {} + filterEqual( + field: string, + filterValue: T + ): Record { + return { + [field]: { + $eq: filterValue, + }, + }; + } - createOptions(): MongooseModuleOptions { - const env = this.configService.get('app.env'); + filterNotEqual( + field: string, + filterValue: T + ): Record { + return { + [field]: { + $ne: filterValue, + }, + }; + } - const uri = this.configService.get('database.uri'); - const debug = this.configService.get('database.debug'); + filterContain(field: string, filterValue: string): Record { + return DatabaseHelperQueryContain(field, filterValue); + } - const timeoutOptions = this.configService.get>( - 'database.timeoutOptions' - ); + filterContainFullMatch( + field: string, + filterValue: string + ): Record { + return DatabaseHelperQueryContain(field, filterValue, { + fullWord: true, + }); + } - if (env !== ENUM_APP_ENVIRONMENT.PRODUCTION) { - mongoose.set('debug', debug); - } + filterIn( + field: string, + filterValue: T[] + ): Record { + return { + [field]: { + $in: filterValue, + }, + }; + } - const mongooseOptions: MongooseModuleOptions = { - uri, - autoCreate: env === ENUM_APP_ENVIRONMENT.MIGRATION, - autoIndex: env === ENUM_APP_ENVIRONMENT.MIGRATION, - ...timeoutOptions, + filterNin( + field: string, + filterValue: T[] + ): Record< + string, + { + $nin: T[]; + } + > { + return { + [field]: { + $nin: filterValue, + }, }; + } - return mongooseOptions; + filterDateBetween( + fieldStart: string, + fieldEnd: string, + filterStartValue: Date, + filterEndValue: Date + ): Record { + if (fieldStart === fieldEnd) { + return { + [fieldStart]: { + $gte: filterStartValue, + $lte: filterEndValue, + }, + }; + } + + return { + [fieldStart]: { + $gte: filterStartValue, + }, + [fieldEnd]: { + $lte: filterEndValue, + }, + }; } } diff --git a/src/common/doc/decorators/doc.decorator.ts b/src/common/doc/decorators/doc.decorator.ts index ba99a3de6..85ae5e11f 100644 --- a/src/common/doc/decorators/doc.decorator.ts +++ b/src/common/doc/decorators/doc.decorator.ts @@ -481,7 +481,7 @@ export function DocResponsePaging( 'Order direction base on _metadata.pagination.availableOrderDirection', }), ApiExtraModels(ResponsePagingDto), - ApiExtraModels(options.dto as any), + ApiExtraModels(docs.dto as any), ApiResponse({ description: docs.httpStatus.toString(), status: docs.httpStatus, diff --git a/src/common/helper/enums/helper.enum.ts b/src/common/helper/enums/helper.enum.ts index 6b9ee0269..8cc9f6a9b 100644 --- a/src/common/helper/enums/helper.enum.ts +++ b/src/common/helper/enums/helper.enum.ts @@ -1,36 +1,9 @@ -export enum ENUM_HELPER_DATE_FORMAT { - DATE = 'YYYY-MM-DD', - FRIENDLY_DATE = 'DD MMM YYYY', - FRIENDLY_DATE_TIME = 'DD MMM YYYY HH:MM:SS', - FRIENDLY_DATE_WITH_DAY = 'dddd, DD MMM YYYY', - FRIENDLY_DATE_TIME_WITH_DAY = 'dddd, DD MMM YYYY HH:MM:SS', - YEAR_MONTH = 'YYYY-MM', - MONTH_DATE = 'MM-DD', - ONLY_YEAR = 'YYYY', - ONLY_MONTH = 'MM', - ONLY_DATE = 'DD', - ONLY_TIME = 'HH:mm', - ISO_DATE = 'YYYY-MM-DDTHH:MM:SSZ', - DAY_LONG = 'dddd', - DAY_SHORT = 'ddd', - HOUR_LONG = 'HH', - HOUR_SHORT = 'H', - MINUTE_LONG = 'mm', - MINUTE_SHORT = 'm', - SECOND_LONG = 'ss', - SECOND_SHORT = 's', - TIMEZONE = 'Z', -} - -export enum ENUM_HELPER_DATE_DIFF { - MILIS = 'milis', - SECONDS = 'seconds', - MINUTES = 'minutes', - HOURS = 'hours', - DAYS = 'days', -} - export enum ENUM_HELPER_FILE_EXCEL_TYPE { XLSX = 'xlsx', CSV = 'csv', } + +export enum ENUM_HELPER_DATE_DAY_OF { + START = 'start', + END = 'end', +} diff --git a/src/common/helper/interfaces/helper.array-service.interface.ts b/src/common/helper/interfaces/helper.array-service.interface.ts index f31c632f9..c4170edd1 100644 --- a/src/common/helper/interfaces/helper.array-service.interface.ts +++ b/src/common/helper/interfaces/helper.array-service.interface.ts @@ -1,6 +1,6 @@ export interface IHelperArrayService { - getFromLeft(array: T[], length: number): T[]; - getFromRight(array: T[], length: number): T[]; + getByIndexFromLeft(array: T[], index: number): T[]; + getByIndexFromRight(array: T[], index: number): T[]; getDifference(a: T[], b: T[]): T[]; getIntersection(a: T[], b: T[]): T[]; concat(a: T[], b: T[]): T[]; diff --git a/src/common/helper/interfaces/helper.date-service.interface.ts b/src/common/helper/interfaces/helper.date-service.interface.ts index 2f07e5f7d..80b6e1c2d 100644 --- a/src/common/helper/interfaces/helper.date-service.interface.ts +++ b/src/common/helper/interfaces/helper.date-service.interface.ts @@ -1,79 +1,20 @@ -import { - IHelperDateBackwardOptions, - IHelperDateCreateOptions, - IHelperDateDiffOptions, - IHelperDateFormatOptions, - IHelperDateForwardOptions, - IHelperDateRoundDownOptions, - IHelperDateSetTimeOptions, -} from 'src/common/helper/interfaces/helper.interface'; +import { DateObjectUnits, DateTime, Duration } from 'luxon'; +import { IHelperDateCreateOptions } from 'src/common/helper/interfaces/helper.interface'; export interface IHelperDateService { - calculateAge(dateOfBirth: Date, year?: number): number; - diff( - dateOne: Date, - dateTwoMoreThanDateOne: Date, - options?: IHelperDateDiffOptions - ): number; - check(date: string | Date | number): boolean; - checkIso(date: string | Date | number): boolean; + calculateAge(dateOfBirth: Date, fromYear?: number): Duration; + checkIso(date: string): boolean; checkTimestamp(timestamp: number): boolean; - create( - date?: string | number | Date, + getZone(date: Date): string; + getTimestamp(date: Date): number; + create(date?: Date, options?: IHelperDateCreateOptions): Date; + createInstance(date?: Date): DateTime; + createFromIso(iso: string, options?: IHelperDateCreateOptions): Date; + createFromTimestamp( + timestamp?: number, options?: IHelperDateCreateOptions ): Date; - createTimestamp( - date?: string | number | Date, - options?: IHelperDateCreateOptions - ): number; - format(date: Date, options?: IHelperDateFormatOptions): string; - formatIsoDurationFromMinutes(minutes: number): string; - formatIsoDurationFromHours(hours: number): string; - formatIsoDurationFromDays(days: number): string; - forwardInSeconds( - seconds: number, - options?: IHelperDateForwardOptions - ): Date; - backwardInSeconds( - seconds: number, - options?: IHelperDateBackwardOptions - ): Date; - forwardInMinutes( - minutes: number, - options?: IHelperDateForwardOptions - ): Date; - backwardInMinutes( - minutes: number, - options?: IHelperDateBackwardOptions - ): Date; - forwardInHours(hours: number, options?: IHelperDateForwardOptions): Date; - backwardInHours(hours: number, options?: IHelperDateBackwardOptions): Date; - forwardInDays(days: number, options?: IHelperDateForwardOptions): Date; - backwardInDays(days: number, options?: IHelperDateBackwardOptions): Date; - forwardInMonths(months: number, options?: IHelperDateForwardOptions): Date; - backwardInMonths( - months: number, - options?: IHelperDateBackwardOptions - ): Date; - forwardInYears(years: number, options?: IHelperDateForwardOptions): Date; - backwardInYears(years: number, options?: IHelperDateBackwardOptions): Date; - endOfMonth(date?: Date): Date; - startOfMonth(date?: Date): Date; - endOfYear(date?: Date): Date; - startOfYear(date?: Date): Date; - endOfDay(date?: Date): Date; - startOfDay(date?: Date): Date; - setTime( - date: Date, - { hour, minute, second, millisecond }: IHelperDateSetTimeOptions - ): Date; - addTime( - date: Date, - { hour, minute, second, millisecond }: IHelperDateSetTimeOptions - ): Date; - subtractTime( - date: Date, - { hour, minute, second }: IHelperDateSetTimeOptions - ): Date; - roundDown(date: Date, options?: IHelperDateRoundDownOptions): Date; + set(date: Date, units: DateObjectUnits): Date; + forward(date: Date, duration: Duration): Date; + backward(date: Date, duration: Duration): Date; } diff --git a/src/common/helper/interfaces/helper.interface.ts b/src/common/helper/interfaces/helper.interface.ts index 2fe536463..9ead45c58 100644 --- a/src/common/helper/interfaces/helper.interface.ts +++ b/src/common/helper/interfaces/helper.interface.ts @@ -1,7 +1,4 @@ -import { - ENUM_HELPER_DATE_DIFF, - ENUM_HELPER_DATE_FORMAT, -} from 'src/common/helper/enums/helper.enum'; +import { ENUM_HELPER_DATE_DAY_OF } from 'src/common/helper/enums/helper.enum'; // Helper Encryption export interface IHelperJwtVerifyOptions { @@ -20,41 +17,15 @@ export interface IHelperJwtOptions // Helper String -export interface IHelperStringCurrencyOptions { - locale?: string; -} - export interface IHelperStringPasswordOptions { length: number; } // Helper Date -export interface IHelperDateSetTimeOptions { - hour?: number; - minute?: number; - second?: number; - millisecond?: number; -} - -export interface IHelperDateDiffOptions { - format?: ENUM_HELPER_DATE_DIFF; -} - export interface IHelperDateCreateOptions { - startOfDay?: boolean; + dayOf?: ENUM_HELPER_DATE_DAY_OF; } -export interface IHelperDateFormatOptions { - format?: ENUM_HELPER_DATE_FORMAT | string; - locale?: string; -} - -export interface IHelperDateForwardOptions { - fromDate?: Date; -} - -export type IHelperDateBackwardOptions = IHelperDateForwardOptions; - export interface IHelperDateRoundDownOptions { hour: boolean; minute: boolean; diff --git a/src/common/helper/interfaces/helper.string-service.interface.ts b/src/common/helper/interfaces/helper.string-service.interface.ts index 3412033b1..34220ce15 100644 --- a/src/common/helper/interfaces/helper.string-service.interface.ts +++ b/src/common/helper/interfaces/helper.string-service.interface.ts @@ -1,17 +1,12 @@ -import { - IHelperStringCurrencyOptions, - IHelperStringPasswordOptions, -} from 'src/common/helper/interfaces/helper.interface'; +import { IHelperStringPasswordOptions } from 'src/common/helper/interfaces/helper.interface'; export interface IHelperStringService { randomReference(length: number): string; random(length: number): string; censor(text: string): string; - checkEmail(email: string): boolean; checkPasswordStrength( password: string, options?: IHelperStringPasswordOptions ): boolean; - checkSafeString(text: string): boolean; - formatCurrency(num: number, options?: IHelperStringCurrencyOptions): string; + formatCurrency(num: number, locale: string): string; } diff --git a/src/common/helper/services/helper.array.service.ts b/src/common/helper/services/helper.array.service.ts index be96729a5..51bd3a1e1 100644 --- a/src/common/helper/services/helper.array.service.ts +++ b/src/common/helper/services/helper.array.service.ts @@ -4,12 +4,12 @@ import { IHelperArrayService } from 'src/common/helper/interfaces/helper.array-s @Injectable() export class HelperArrayService implements IHelperArrayService { - getFromLeft(array: T[], length: number): T[] { - return _.take(array, length); + getByIndexFromLeft(array: T[], index: number): T[] { + return _.take(array, index + 1); } - getFromRight(array: T[], length: number): T[] { - return _.takeRight(array, length); + getByIndexFromRight(array: T[], index: number): T[] { + return _.takeRight(array, index + 1); } getDifference(a: T[], b: T[]): T[] { diff --git a/src/common/helper/services/helper.date.service.ts b/src/common/helper/services/helper.date.service.ts index b8481ced7..49740b6d7 100644 --- a/src/common/helper/services/helper.date.service.ts +++ b/src/common/helper/services/helper.date.service.ts @@ -1,20 +1,9 @@ import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; -import moment, { ISO_8601 } from 'moment-timezone'; -import { - ENUM_HELPER_DATE_DIFF, - ENUM_HELPER_DATE_FORMAT, -} from 'src/common/helper/enums/helper.enum'; +import { DateObjectUnits, DateTime, Duration } from 'luxon'; +import { ENUM_HELPER_DATE_DAY_OF } from 'src/common/helper/enums/helper.enum'; import { IHelperDateService } from 'src/common/helper/interfaces/helper.date-service.interface'; -import { - IHelperDateSetTimeOptions, - IHelperDateFormatOptions, - IHelperDateDiffOptions, - IHelperDateCreateOptions, - IHelperDateForwardOptions, - IHelperDateBackwardOptions, - IHelperDateRoundDownOptions, -} from 'src/common/helper/interfaces/helper.interface'; +import { IHelperDateCreateOptions } from 'src/common/helper/interfaces/helper.interface'; @Injectable() export class HelperDateService implements IHelperDateService { @@ -24,281 +13,155 @@ export class HelperDateService implements IHelperDateService { this.defTz = this.configService.get('app.timezone'); } - calculateAge(dateOfBirth: Date, year?: number): number { - const m = moment().tz(this.defTz); - - if (year) { - m.set('year', year); - } - - return m.diff(dateOfBirth, 'years'); - } - - diff( - dateOne: Date, - dateTwoMoreThanDateOne: Date, - options?: IHelperDateDiffOptions - ): number { - const mDateOne = moment(dateOne).tz(this.defTz); - const mDateTwo = moment(dateTwoMoreThanDateOne).tz(this.defTz); - const diff = moment.duration(mDateTwo.diff(mDateOne)); - - if (options?.format === ENUM_HELPER_DATE_DIFF.MILIS) { - return diff.asMilliseconds(); - } else if (options?.format === ENUM_HELPER_DATE_DIFF.SECONDS) { - return diff.asSeconds(); - } else if (options?.format === ENUM_HELPER_DATE_DIFF.HOURS) { - return diff.asHours(); - } else if (options?.format === ENUM_HELPER_DATE_DIFF.MINUTES) { - return diff.asMinutes(); - } else { - return diff.asDays(); + calculateAge(dateOfBirth: Date, fromYear?: number): Duration { + const dateTime = DateTime.now() + .setZone(this.defTz) + .plus({ + day: 1, + }) + .set({ + hour: 0, + minute: 0, + second: 0, + millisecond: 0, + }); + const dateTimeDob = DateTime.fromJSDate(dateOfBirth) + .setZone(this.defTz) + .set({ + hour: 0, + minute: 0, + second: 0, + millisecond: 0, + }); + + if (fromYear) { + dateTime.set({ + year: fromYear, + }); } - } - check(date: string | Date | number): boolean { - return moment(date, 'YYYY-MM-DD', true).tz(this.defTz).isValid(); + return dateTime.diff(dateTimeDob); } - checkIso(date: string | Date | number): boolean { - return moment(date, ISO_8601, true).tz(this.defTz).isValid(); + checkIso(date: string): boolean { + return DateTime.fromISO(date).setZone(this.defTz).isValid; } checkTimestamp(timestamp: number): boolean { - return moment(timestamp, true).tz(this.defTz).isValid(); - } - - create( - date?: string | number | Date, - options?: IHelperDateCreateOptions - ): Date { - const mDate = moment(date ?? undefined).tz(this.defTz); - - if (options?.startOfDay) { - mDate.startOf('day'); - } - - return mDate.toDate(); + return DateTime.fromMillis(timestamp).setZone(this.defTz).isValid; } - createTimestamp( - date?: string | number | Date, - options?: IHelperDateCreateOptions - ): number { - const mDate = moment(date ?? undefined).tz(this.defTz); - - if (options?.startOfDay) { - mDate.startOf('day'); - } - - return mDate.valueOf(); + getZone(date: Date): string { + return DateTime.fromJSDate(date).setZone(this.defTz).zone.name; } - format(date: Date, options?: IHelperDateFormatOptions): string { - if (options?.locale) { - moment.locale(options.locale); - } - - return moment(date) - .tz(this.defTz) - .format(options?.format ?? ENUM_HELPER_DATE_FORMAT.DATE); + getZoneOffset(date: Date): string { + return DateTime.fromJSDate(date).setZone(this.defTz).offsetNameShort; } - formatIsoDurationFromMinutes(minutes: number): string { - return moment.duration(minutes, 'minutes').toISOString(); + getTimestamp(date: Date): number { + return DateTime.fromJSDate(date).setZone(this.defTz).toMillis(); } - formatIsoDurationFromHours(hours: number): string { - return moment.duration(hours, 'hours').toISOString(); + formatToRFC2822(date: Date): string { + return DateTime.fromJSDate(date).setZone(this.defTz).toRFC2822(); } - formatIsoDurationFromDays(days: number): string { - return moment.duration(days, 'days').toISOString(); + formatToIso(date: Date): string { + return DateTime.fromJSDate(date).setZone(this.defTz).toISO(); } - forwardInSeconds( - seconds: number, - options?: IHelperDateForwardOptions - ): Date { - return moment(options?.fromDate) - .tz(this.defTz) - .add(seconds, 'seconds') - .toDate(); + formatToIsoDate(date: Date): string { + return DateTime.fromJSDate(date).setZone(this.defTz).toISODate(); } - backwardInSeconds( - seconds: number, - options?: IHelperDateBackwardOptions - ): Date { - return moment(options?.fromDate) - .tz(this.defTz) - .subtract(seconds, 'seconds') - .toDate(); + formatToIsoTime(date: Date): string { + return DateTime.fromJSDate(date).setZone(this.defTz).toISOTime(); } - forwardInMinutes( - minutes: number, - options?: IHelperDateForwardOptions - ): Date { - return moment(options?.fromDate) - .tz(this.defTz) - .add(minutes, 'minutes') - .toDate(); - } + create(date?: Date, options?: IHelperDateCreateOptions): Date { + const mDate = date + ? DateTime.fromJSDate(date).setZone(this.defTz) + : DateTime.now().setZone(this.defTz); - backwardInMinutes( - minutes: number, - options?: IHelperDateBackwardOptions - ): Date { - return moment(options?.fromDate) - .tz(this.defTz) - .subtract(minutes, 'minutes') - .toDate(); - } + if ( + options?.dayOf && + options?.dayOf === ENUM_HELPER_DATE_DAY_OF.START + ) { + mDate.startOf('day'); + } else if ( + options?.dayOf && + options?.dayOf === ENUM_HELPER_DATE_DAY_OF.END + ) { + mDate.endOf('day'); + } - forwardInHours(hours: number, options?: IHelperDateForwardOptions): Date { - return moment(options?.fromDate) - .tz(this.defTz) - .add(hours, 'hours') - .toDate(); + return mDate.toJSDate(); } - backwardInHours(hours: number, options?: IHelperDateBackwardOptions): Date { - return moment(options?.fromDate) - .tz(this.defTz) - .subtract(hours, 'hours') - .toDate(); + createInstance(date?: Date): DateTime { + return date ? DateTime.fromJSDate(date) : DateTime.now(); } - forwardInDays(days: number, options?: IHelperDateForwardOptions): Date { - return moment(options?.fromDate).tz(this.defTz).add(days, 'd').toDate(); - } + createFromIso(iso: string, options?: IHelperDateCreateOptions): Date { + const date = DateTime.fromISO(iso).setZone(this.defTz); - backwardInDays(days: number, options?: IHelperDateBackwardOptions): Date { - return moment(options?.fromDate) - .tz(this.defTz) - .subtract(days, 'days') - .toDate(); - } + if ( + options?.dayOf && + options?.dayOf === ENUM_HELPER_DATE_DAY_OF.START + ) { + date.startOf('day'); + } else if ( + options?.dayOf && + options?.dayOf === ENUM_HELPER_DATE_DAY_OF.END + ) { + date.endOf('day'); + } - forwardInMonths(months: number, options?: IHelperDateForwardOptions): Date { - return moment(options?.fromDate) - .tz(this.defTz) - .add(months, 'months') - .toDate(); + return date.toJSDate(); } - backwardInMonths( - months: number, - options?: IHelperDateBackwardOptions + createFromTimestamp( + timestamp?: number, + options?: IHelperDateCreateOptions ): Date { - return moment(options?.fromDate) - .tz(this.defTz) - .subtract(months, 'months') - .toDate(); - } - - forwardInYears(years: number, options?: IHelperDateForwardOptions): Date { - return moment(options?.fromDate) - .tz(this.defTz) - .add(years, 'years') - .toDate(); - } - - backwardInYears(years: number, options?: IHelperDateBackwardOptions): Date { - return moment(options?.fromDate) - .tz(this.defTz) - .subtract(years, 'years') - .toDate(); - } - - endOfMonth(date?: Date): Date { - return moment(date).tz(this.defTz).endOf('month').toDate(); - } - - startOfMonth(date?: Date): Date { - return moment(date).tz(this.defTz).startOf('month').toDate(); - } - - endOfYear(date?: Date): Date { - return moment(date).tz(this.defTz).endOf('year').toDate(); - } - - startOfYear(date?: Date): Date { - return moment(date).tz(this.defTz).startOf('year').toDate(); - } - - endOfDay(date?: Date): Date { - return moment(date).tz(this.defTz).endOf('day').toDate(); - } - - startOfDay(date?: Date): Date { - return moment(date).tz(this.defTz).startOf('day').toDate(); - } + const date = timestamp + ? DateTime.fromMillis(timestamp).setZone(this.defTz) + : DateTime.now().setZone(this.defTz); + + if ( + options?.dayOf && + options?.dayOf === ENUM_HELPER_DATE_DAY_OF.START + ) { + date.startOf('day'); + } else if ( + options?.dayOf && + options?.dayOf === ENUM_HELPER_DATE_DAY_OF.END + ) { + date.endOf('day'); + } - setTime( - date: Date, - { hour, minute, second, millisecond }: IHelperDateSetTimeOptions - ): Date { - return moment(date) - .tz(this.defTz) - .set({ - hour: hour, - minute: minute, - second: second, - millisecond: millisecond, - }) - .toDate(); + return date.toJSDate(); } - addTime( - date: Date, - { hour, minute, second, millisecond }: IHelperDateSetTimeOptions - ): Date { - return moment(date) - .tz(this.defTz) - .add({ - hour: hour, - minute: minute, - second: second, - millisecond: millisecond, - }) - .toDate(); + set(date: Date, units: DateObjectUnits): Date { + return DateTime.fromJSDate(date) + .setZone(this.defTz) + .set(units) + .toJSDate(); } - subtractTime( - date: Date, - { hour, minute, second }: IHelperDateSetTimeOptions - ): Date { - return moment(date) - .tz(this.defTz) - .subtract({ - hour: hour, - minute: minute, - second: second, - }) - .toDate(); + forward(date: Date, duration: Duration): Date { + return DateTime.fromJSDate(date) + .setZone(this.defTz) + .plus(duration) + .toJSDate(); } - roundDown(date: Date, options?: IHelperDateRoundDownOptions): Date { - const mDate = moment(date).tz(this.defTz); - - if (options?.hour) { - mDate.set({ hour: 0 }); - } - - if (options?.minute) { - mDate.set({ minute: 0 }); - } - - if (options?.second) { - mDate.set({ second: 0 }); - } - - if (options?.millisecond) { - mDate.set({ millisecond: 0 }); - } - - return mDate.toDate(); + backward(date: Date, duration: Duration): Date { + return DateTime.fromJSDate(date) + .setZone(this.defTz) + .minus(duration) + .toJSDate(); } } diff --git a/src/common/helper/services/helper.encryption.service.ts b/src/common/helper/services/helper.encryption.service.ts index 5036ed084..15a2dbd07 100644 --- a/src/common/helper/services/helper.encryption.service.ts +++ b/src/common/helper/services/helper.encryption.service.ts @@ -1,5 +1,4 @@ import { Injectable, Logger } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; import { JwtService } from '@nestjs/jwt'; import { AES, enc, mode, pad } from 'crypto-js'; import { IHelperEncryptionService } from 'src/common/helper/interfaces/helper.encryption-service.interface'; @@ -10,15 +9,9 @@ import { @Injectable() export class HelperEncryptionService implements IHelperEncryptionService { - private readonly debug: boolean; private readonly logger = new Logger(HelperEncryptionService.name); - constructor( - private readonly jwtService: JwtService, - private readonly configService: ConfigService - ) { - this.debug = this.configService.get('app.debug'); - } + constructor(private readonly jwtService: JwtService) {} base64Encrypt(data: string): string { const buff: Buffer = Buffer.from(data, 'utf8'); @@ -98,9 +91,7 @@ export class HelperEncryptionService implements IHelperEncryptionService { return true; } catch (err: unknown) { - if (this.debug) { - this.logger.error(err); - } + this.logger.error(err); return false; } diff --git a/src/common/helper/services/helper.number.service.ts b/src/common/helper/services/helper.number.service.ts index a9a5dfa33..14b378e7c 100644 --- a/src/common/helper/services/helper.number.service.ts +++ b/src/common/helper/services/helper.number.service.ts @@ -25,6 +25,7 @@ export class HelperNumberService implements IHelperNumberService { if (Number.isNaN(tValue) || !Number.isFinite(tValue)) { tValue = 0; } + return Number.parseFloat((tValue * 100).toFixed(2)); } } diff --git a/src/common/helper/services/helper.string.service.ts b/src/common/helper/services/helper.string.service.ts index b12c3c53f..d7c321cd5 100644 --- a/src/common/helper/services/helper.string.service.ts +++ b/src/common/helper/services/helper.string.service.ts @@ -1,9 +1,5 @@ -import { faker } from '@faker-js/faker'; import { Injectable } from '@nestjs/common'; -import { - IHelperStringCurrencyOptions, - IHelperStringPasswordOptions, -} from 'src/common/helper/interfaces/helper.interface'; +import { IHelperStringPasswordOptions } from 'src/common/helper/interfaces/helper.interface'; import { IHelperStringService } from 'src/common/helper/interfaces/helper.string-service.interface'; @Injectable() @@ -16,9 +12,20 @@ export class HelperStringService implements IHelperStringService { } random(length: number): string { - return faker.string.alphanumeric({ - length: { min: length, max: length }, - }); + let result = ''; + const characters = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + + let counter = 0; + while (counter < length) { + result += characters.charAt( + Math.floor(Math.random() * characters.length) + ); + + counter += 1; + } + + return result; } censor(text: string): string { @@ -34,11 +41,6 @@ export class HelperStringService implements IHelperStringService { return `${text.slice(0, 3)}${stringCensor}${text.slice(-4)}`; } - checkEmail(email: string): boolean { - const regex = new RegExp(/\S+@\S+\.\S+/); - return regex.test(email); - } - checkPasswordStrength( password: string, options?: IHelperStringPasswordOptions @@ -51,15 +53,7 @@ export class HelperStringService implements IHelperStringService { return regex.test(password); } - checkSafeString(text: string): boolean { - const regex = new RegExp('^[A-Za-z0-9]+$'); - return regex.test(text); - } - - formatCurrency( - num: number, - options?: IHelperStringCurrencyOptions - ): string { - return num.toLocaleString(options?.locale); + formatCurrency(num: number, locale: string): string { + return num.toLocaleString(locale); } } diff --git a/src/common/logger/logger.option.module.ts b/src/common/logger/logger.option.module.ts new file mode 100644 index 000000000..3955544e5 --- /dev/null +++ b/src/common/logger/logger.option.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { LoggerOptionService } from 'src/common/logger/services/logger.option.service'; + +@Module({ + imports: [], + providers: [LoggerOptionService], + exports: [LoggerOptionService], +}) +export class LoggerOptionModule {} diff --git a/src/common/logger/services/logger.option.service.ts b/src/common/logger/services/logger.option.service.ts new file mode 100644 index 000000000..9aba23b5e --- /dev/null +++ b/src/common/logger/services/logger.option.service.ts @@ -0,0 +1,119 @@ +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { Request, Response } from 'express'; +import { IncomingMessage } from 'http'; +import { Params } from 'nestjs-pino'; +import { ENUM_APP_ENVIRONMENT } from 'src/app/enums/app.enum'; +import { IRequestApp } from 'src/common/request/interfaces/request.interface'; + +@Injectable() +export class LoggerOptionService { + private readonly env: ENUM_APP_ENVIRONMENT; + private readonly name: string; + private readonly debugEnable: boolean; + + private readonly level: string; + + constructor(private configService: ConfigService) { + this.env = this.configService.get('app.env'); + this.name = this.configService.get('app.name'); + + this.debugEnable = this.configService.get('debug.enable'); + + switch (this.env) { + case ENUM_APP_ENVIRONMENT.PRODUCTION: + this.level = 'fatal'; + break; + case ENUM_APP_ENVIRONMENT.STAGING: + this.level = 'error'; + break; + case ENUM_APP_ENVIRONMENT.MIGRATION: + this.level = 'error'; + break; + case ENUM_APP_ENVIRONMENT.DEVELOPMENT: + this.level = 'info'; + break; + default: + this.level = 'debug'; + break; + } + } + + createOptions(): Params { + return { + pinoHttp: { + level: this.debugEnable ? this.level : 'silent', + formatters: { + level: (label: string) => ({ level: label.toUpperCase() }), + }, + messageKey: 'message', + timestamp: false, + base: { + app: this.name, + env: this.env, + date: new Date().toISOString(), + }, + customProps: (req: Request, _res: Response) => ({ + context: 'HTTP', + 'x-request-id': req.id, + }), + redact: { + paths: [ + 'req.headers.authorization', + 'req.headers["x-api-key"]', + 'req.body.password', + 'req.body.newPassword', + 'req.body.oldPassword', + 'res.headers["set-cookie"]', + ], + censor: '**REDACTED**', + }, + serializers: { + req(request: IRequestApp) { + return { + id: request.id, + method: request.method, + url: request.url, + queries: request.query, + parameters: request.params, + headers: request.headers, + body: request.body, + }; + }, + res(response) { + return { + statusCode: response.statusCode, + headers: response.headers, + }; + }, + err(error) { + return { + type: error.type, + message: error.message, + stack: error.stack, + code: error.code, + }; + }, + }, + transport: + this.env === ENUM_APP_ENVIRONMENT.LOCAL + ? { + target: 'pino-pretty', + options: { + colorize: true, + levelFirst: true, + translateTime: 'SYS:standard', + }, + } + : undefined, + autoLogging: + this.env === ENUM_APP_ENVIRONMENT.LOCAL + ? { + ignore: (req: IncomingMessage) => + req.url === '/api/hello', + } + : undefined, + }, + }; + } +} diff --git a/src/common/message/interfaces/message.service.interface.ts b/src/common/message/interfaces/message.service.interface.ts index 2052e8c21..9e06fe469 100644 --- a/src/common/message/interfaces/message.service.interface.ts +++ b/src/common/message/interfaces/message.service.interface.ts @@ -1,5 +1,4 @@ import { ValidationError } from '@nestjs/common'; -import { ENUM_MESSAGE_LANGUAGE } from 'src/common/message/enums/message.enum'; import { IMessageErrorOptions, IMessageSetOptions, @@ -9,8 +8,6 @@ import { } from 'src/common/message/interfaces/message.interface'; export interface IMessageService { - getAvailableLanguages(): ENUM_MESSAGE_LANGUAGE[]; - getLanguage(): ENUM_MESSAGE_LANGUAGE; filterLanguage(customLanguage: string): string[]; setMessage(path: string, options?: IMessageSetOptions): string; setValidationMessage( diff --git a/src/common/message/services/message.service.ts b/src/common/message/services/message.service.ts index 488f81320..a2eb54890 100644 --- a/src/common/message/services/message.service.ts +++ b/src/common/message/services/message.service.ts @@ -29,15 +29,7 @@ export class MessageService implements IMessageService { this.availableLanguage = this.configService.get< ENUM_MESSAGE_LANGUAGE[] >('message.availableLanguage'); - this.debug = this.configService.get('app.debug'); - } - - getAvailableLanguages(): ENUM_MESSAGE_LANGUAGE[] { - return this.availableLanguage; - } - - getLanguage(): ENUM_MESSAGE_LANGUAGE { - return this.defaultLanguage; + this.debug = this.configService.get('debug.enable'); } //! Filter message base on available language diff --git a/src/common/pagination/decorators/pagination.decorator.ts b/src/common/pagination/decorators/pagination.decorator.ts index 26e229d64..da8825588 100644 --- a/src/common/pagination/decorators/pagination.decorator.ts +++ b/src/common/pagination/decorators/pagination.decorator.ts @@ -1,11 +1,11 @@ import { Query } from '@nestjs/common'; import { - IPaginationFilterDateOptions, + IPaginationFilterDateBetweenOptions, IPaginationFilterEqualOptions, IPaginationFilterOptions, IPaginationQueryOptions, } from 'src/common/pagination/interfaces/pagination.interface'; -import { PaginationFilterDatePipe } from 'src/common/pagination/pipes/pagination.filter-date.pipe'; +import { PaginationFilterDateBetweenPipe } from 'src/common/pagination/pipes/pagination.filter-date-between.pipe'; import { PaginationFilterEqualPipe } from 'src/common/pagination/pipes/pagination.filter-equal.pipe'; import { PaginationFilterInBooleanPipe } from 'src/common/pagination/pipes/pagination.filter-in-boolean.pipe'; import { PaginationFilterInEnumPipe } from 'src/common/pagination/pipes/pagination.filter-in-enum.pipe'; @@ -16,7 +16,6 @@ import { PaginationOrderPipe } from 'src/common/pagination/pipes/pagination.orde import { PaginationPagingPipe } from 'src/common/pagination/pipes/pagination.paging.pipe'; import { PaginationSearchPipe } from 'src/common/pagination/pipes/pagination.search.pipe'; -//! Pagination query helper export function PaginationQuery( options?: IPaginationQueryOptions ): ParameterDecorator { @@ -31,7 +30,6 @@ export function PaginationQuery( ); } -//! Pagination query filter boolean will convert into repository query export function PaginationQueryFilterInBoolean( field: string, defaultValue: boolean[], @@ -43,7 +41,6 @@ export function PaginationQueryFilterInBoolean( ); } -//! Pagination query filter enum will convert into repository export function PaginationQueryFilterInEnum( field: string, defaultValue: T, @@ -56,7 +53,6 @@ export function PaginationQueryFilterInEnum( ); } -//! Pagination query filter enum will convert into repository export function PaginationQueryFilterNinEnum( field: string, defaultValue: T, @@ -74,7 +70,6 @@ export function PaginationQueryFilterNinEnum( ); } -//! Pagination query filter equal will convert into repository export function PaginationQueryFilterNotEqual( field: string, options?: IPaginationFilterEqualOptions @@ -85,7 +80,6 @@ export function PaginationQueryFilterNotEqual( ); } -//! Pagination query filter equal will convert into repository export function PaginationQueryFilterEqual( field: string, options?: IPaginationFilterEqualOptions @@ -96,7 +90,6 @@ export function PaginationQueryFilterEqual( ); } -//! Pagination query filter string contain will convert into repository export function PaginationQueryFilterStringContain( field: string, options?: IPaginationFilterOptions @@ -107,13 +100,12 @@ export function PaginationQueryFilterStringContain( ); } -//! Pagination query filter date will convert into repository -export function PaginationQueryFilterDate( - field: string, - options?: IPaginationFilterDateOptions +export function PaginationQueryFilterDateBetween( + fieldStart: string, + fieldEnd: string, + options?: IPaginationFilterDateBetweenOptions ): ParameterDecorator { return Query( - options?.queryField ?? field, - PaginationFilterDatePipe(field, options) + PaginationFilterDateBetweenPipe(fieldStart, fieldEnd, options) ); } diff --git a/src/common/pagination/enums/pagination.enum.ts b/src/common/pagination/enums/pagination.enum.ts index 307a5e22d..6102583da 100644 --- a/src/common/pagination/enums/pagination.enum.ts +++ b/src/common/pagination/enums/pagination.enum.ts @@ -2,8 +2,3 @@ export enum ENUM_PAGINATION_ORDER_DIRECTION_TYPE { ASC = 'asc', DESC = 'desc', } - -export enum ENUM_PAGINATION_FILTER_DATE_TIME_OPTIONS { - START_OF_DAY = 'START_OF_DAY', - END_OF_DAY = 'END_OF_DAY', -} diff --git a/src/common/pagination/interfaces/pagination.interface.ts b/src/common/pagination/interfaces/pagination.interface.ts index d6bca7c50..14c428320 100644 --- a/src/common/pagination/interfaces/pagination.interface.ts +++ b/src/common/pagination/interfaces/pagination.interface.ts @@ -1,7 +1,4 @@ -import { - ENUM_PAGINATION_FILTER_DATE_TIME_OPTIONS, - ENUM_PAGINATION_ORDER_DIRECTION_TYPE, -} from 'src/common/pagination/enums/pagination.enum'; +import { ENUM_PAGINATION_ORDER_DIRECTION_TYPE } from 'src/common/pagination/enums/pagination.enum'; export type IPaginationOrder = Record< string, @@ -21,9 +18,9 @@ export interface IPaginationFilterOptions { raw?: boolean; } -export interface IPaginationFilterDateOptions - extends Omit { - time?: ENUM_PAGINATION_FILTER_DATE_TIME_OPTIONS; +export interface IPaginationFilterDateBetweenOptions { + queryFieldStart?: string; + queryFieldEnd?: string; } export interface IPaginationFilterEqualOptions diff --git a/src/common/pagination/interfaces/pagination.service.interface.ts b/src/common/pagination/interfaces/pagination.service.interface.ts index 9f7ede9bb..60c78b420 100644 --- a/src/common/pagination/interfaces/pagination.service.interface.ts +++ b/src/common/pagination/interfaces/pagination.service.interface.ts @@ -12,17 +12,4 @@ export interface IPaginationService { availableOrderBy: string[] ): IPaginationOrder; search(searchValue: string, availableSearch: string[]): Record; - filterEqual(field: string, filterValue: T): Record; - filterNotEqual( - field: string, - filterValue: T - ): Record; - filterContain(field: string, filterValue: string): Record; - filterContainFullMatch( - field: string, - filterValue: string - ): Record; - filterIn(field: string, filterValue: T[]): Record; - filterNin(field: string, filterValue: T[]): Record; - filterDate(field: string, filterValue: Date): Record; } diff --git a/src/common/pagination/pipes/pagination.filter-date-between.pipe.ts b/src/common/pagination/pipes/pagination.filter-date-between.pipe.ts new file mode 100644 index 000000000..6eb84cdc5 --- /dev/null +++ b/src/common/pagination/pipes/pagination.filter-date-between.pipe.ts @@ -0,0 +1,73 @@ +import { Inject, Injectable, mixin, Type } from '@nestjs/common'; +import { PipeTransform, Scope } from '@nestjs/common/interfaces'; +import { REQUEST } from '@nestjs/core'; +import { DatabaseService } from 'src/common/database/services/database.service'; +import { ENUM_HELPER_DATE_DAY_OF } from 'src/common/helper/enums/helper.enum'; +import { HelperDateService } from 'src/common/helper/services/helper.date.service'; +import { IPaginationFilterDateBetweenOptions } from 'src/common/pagination/interfaces/pagination.interface'; +import { IRequestApp } from 'src/common/request/interfaces/request.interface'; + +export function PaginationFilterDateBetweenPipe( + fieldStart: string, + fieldEnd: string, + options?: IPaginationFilterDateBetweenOptions +): Type { + @Injectable({ scope: Scope.REQUEST }) + class MixinPaginationFilterDatePipe implements PipeTransform { + constructor( + @Inject(REQUEST) protected readonly request: IRequestApp, + private readonly databaseService: DatabaseService, + private readonly helperDateService: HelperDateService + ) {} + + async transform(): Promise { + const finalFieldStart = options?.queryFieldStart ?? fieldStart; + const finalFieldEnd = options?.queryFieldEnd ?? fieldEnd; + const { body } = this.request; + + if (!body[finalFieldStart] || !body[finalFieldEnd]) { + return; + } + + const finalStartValue: Date = this.helperDateService.createFromIso( + body[finalFieldStart], + { + dayOf: ENUM_HELPER_DATE_DAY_OF.START, + } + ); + const finalEndValue: Date = this.helperDateService.createFromIso( + body[finalFieldEnd], + { dayOf: ENUM_HELPER_DATE_DAY_OF.END } + ); + + this.addToRequestInstance(finalStartValue, finalEndValue); + return this.databaseService.filterDateBetween( + fieldStart, + fieldEnd, + finalStartValue, + finalEndValue + ); + } + + addToRequestInstance(startValue: Date, endValue: Date): void { + const finalFieldStart = options?.queryFieldStart ?? fieldStart; + const finalFieldEnd = options?.queryFieldEnd ?? fieldEnd; + + this.request.__pagination = { + ...this.request.__pagination, + filters: this.request.__pagination?.filters + ? { + ...this.request.__pagination?.filters, + [finalFieldStart]: startValue, + [finalFieldEnd]: endValue, + } + : { + [finalFieldStart]: startValue, + [finalFieldEnd]: endValue, + }, + }; + } + } + + return mixin(MixinPaginationFilterDatePipe); +} diff --git a/src/common/pagination/pipes/pagination.filter-date.pipe.ts b/src/common/pagination/pipes/pagination.filter-date.pipe.ts deleted file mode 100644 index d56b14c20..000000000 --- a/src/common/pagination/pipes/pagination.filter-date.pipe.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { Inject, Injectable, mixin, Type } from '@nestjs/common'; -import { PipeTransform, Scope } from '@nestjs/common/interfaces'; -import { REQUEST } from '@nestjs/core'; -import { HelperDateService } from 'src/common/helper/services/helper.date.service'; -import { ENUM_PAGINATION_FILTER_DATE_TIME_OPTIONS } from 'src/common/pagination/enums/pagination.enum'; -import { IPaginationFilterDateOptions } from 'src/common/pagination/interfaces/pagination.interface'; -import { PaginationService } from 'src/common/pagination/services/pagination.service'; -import { IRequestApp } from 'src/common/request/interfaces/request.interface'; - -export function PaginationFilterDatePipe( - field: string, - options?: IPaginationFilterDateOptions -): Type { - @Injectable({ scope: Scope.REQUEST }) - class MixinPaginationFilterDatePipe implements PipeTransform { - constructor( - @Inject(REQUEST) protected readonly request: IRequestApp, - private readonly paginationService: PaginationService, - private readonly helperDateService: HelperDateService - ) {} - - async transform(value: string): Promise> { - if (!value) { - return; - } - - if (options?.raw) { - this.addToRequestInstance(value); - return { - [field]: value, - }; - } - - let finalValue: Date = this.helperDateService.create(value); - - if ( - options?.time === - ENUM_PAGINATION_FILTER_DATE_TIME_OPTIONS.END_OF_DAY - ) { - finalValue = this.helperDateService.endOfDay(finalValue); - } else if ( - options?.time === - ENUM_PAGINATION_FILTER_DATE_TIME_OPTIONS.START_OF_DAY - ) { - finalValue = this.helperDateService.startOfDay(finalValue); - } - - this.addToRequestInstance(value); - return this.paginationService.filterDate(field, finalValue); - } - - addToRequestInstance(value: any): void { - this.request.__pagination = { - ...this.request.__pagination, - filters: this.request.__pagination?.filters - ? { - ...this.request.__pagination?.filters, - [field]: value, - } - : { [field]: value }, - }; - } - } - - return mixin(MixinPaginationFilterDatePipe); -} diff --git a/src/common/pagination/pipes/pagination.filter-equal.pipe.ts b/src/common/pagination/pipes/pagination.filter-equal.pipe.ts index e06045ab0..fa7423c63 100644 --- a/src/common/pagination/pipes/pagination.filter-equal.pipe.ts +++ b/src/common/pagination/pipes/pagination.filter-equal.pipe.ts @@ -1,8 +1,8 @@ import { Inject, Injectable, mixin, Type } from '@nestjs/common'; import { PipeTransform, Scope } from '@nestjs/common/interfaces'; import { REQUEST } from '@nestjs/core'; +import { DatabaseService } from 'src/common/database/services/database.service'; import { IPaginationFilterEqualOptions } from 'src/common/pagination/interfaces/pagination.interface'; -import { PaginationService } from 'src/common/pagination/services/pagination.service'; import { IRequestApp } from 'src/common/request/interfaces/request.interface'; export function PaginationFilterEqualPipe( @@ -13,12 +13,10 @@ export function PaginationFilterEqualPipe( class MixinPaginationFilterEqualPipe implements PipeTransform { constructor( @Inject(REQUEST) protected readonly request: IRequestApp, - private readonly paginationService: PaginationService + private readonly databaseService: DatabaseService ) {} - async transform( - value: string - ): Promise> { + async transform(value: string): Promise { if (!value) { return; } @@ -35,7 +33,7 @@ export function PaginationFilterEqualPipe( : value.trim(); this.addToRequestInstance(finalValue); - return this.paginationService.filterEqual(field, finalValue); + return this.databaseService.filterEqual(field, finalValue); } addToRequestInstance(value: any): void { diff --git a/src/common/pagination/pipes/pagination.filter-in-boolean.pipe.ts b/src/common/pagination/pipes/pagination.filter-in-boolean.pipe.ts index cecbcfe6b..c9feab13d 100644 --- a/src/common/pagination/pipes/pagination.filter-in-boolean.pipe.ts +++ b/src/common/pagination/pipes/pagination.filter-in-boolean.pipe.ts @@ -1,9 +1,9 @@ import { Inject, Injectable, mixin, Type } from '@nestjs/common'; import { PipeTransform, Scope } from '@nestjs/common/interfaces'; import { REQUEST } from '@nestjs/core'; +import { DatabaseService } from 'src/common/database/services/database.service'; import { HelperArrayService } from 'src/common/helper/services/helper.array.service'; import { IPaginationFilterOptions } from 'src/common/pagination/interfaces/pagination.interface'; -import { PaginationService } from 'src/common/pagination/services/pagination.service'; import { IRequestApp } from 'src/common/request/interfaces/request.interface'; export function PaginationFilterInBooleanPipe( @@ -15,11 +15,11 @@ export function PaginationFilterInBooleanPipe( class MixinPaginationFilterInBooleanPipe implements PipeTransform { constructor( @Inject(REQUEST) protected readonly request: IRequestApp, - private readonly paginationService: PaginationService, + private readonly databaseService: DatabaseService, private readonly helperArrayService: HelperArrayService ) {} - async transform(value: string): Promise> { + async transform(value: string): Promise { if (options?.raw) { this.addToRequestInstance(value); return { @@ -34,7 +34,7 @@ export function PaginationFilterInBooleanPipe( : defaultValue; this.addToRequestInstance(finalValue); - return this.paginationService.filterIn(field, finalValue); + return this.databaseService.filterIn(field, finalValue); } addToRequestInstance(value: any): void { diff --git a/src/common/pagination/pipes/pagination.filter-in-enum.pipe.ts b/src/common/pagination/pipes/pagination.filter-in-enum.pipe.ts index 4e360d260..d18bdc39a 100644 --- a/src/common/pagination/pipes/pagination.filter-in-enum.pipe.ts +++ b/src/common/pagination/pipes/pagination.filter-in-enum.pipe.ts @@ -1,9 +1,9 @@ import { Inject, Injectable, mixin, Type } from '@nestjs/common'; import { PipeTransform, Scope } from '@nestjs/common/interfaces'; import { REQUEST } from '@nestjs/core'; +import { DatabaseService } from 'src/common/database/services/database.service'; import { HelperArrayService } from 'src/common/helper/services/helper.array.service'; import { IPaginationFilterOptions } from 'src/common/pagination/interfaces/pagination.interface'; -import { PaginationService } from 'src/common/pagination/services/pagination.service'; import { IRequestApp } from 'src/common/request/interfaces/request.interface'; export function PaginationFilterInEnumPipe( @@ -16,11 +16,11 @@ export function PaginationFilterInEnumPipe( class MixinPaginationFilterInEnumPipe implements PipeTransform { constructor( @Inject(REQUEST) protected readonly request: IRequestApp, - private readonly paginationService: PaginationService, + private readonly databaseService: DatabaseService, private readonly helperArrayService: HelperArrayService ) {} - async transform(value: string): Promise> { + async transform(value: string): Promise { if (options?.raw) { this.addToRequestInstance(value); return { @@ -35,7 +35,7 @@ export function PaginationFilterInEnumPipe( ) : (defaultValue as T[]); - return this.paginationService.filterIn(field, finalValue); + return this.databaseService.filterIn(field, finalValue); } addToRequestInstance(value: any): void { diff --git a/src/common/pagination/pipes/pagination.filter-nin-enum.pipe.ts b/src/common/pagination/pipes/pagination.filter-nin-enum.pipe.ts index c89c50d33..e1ce7bd06 100644 --- a/src/common/pagination/pipes/pagination.filter-nin-enum.pipe.ts +++ b/src/common/pagination/pipes/pagination.filter-nin-enum.pipe.ts @@ -1,9 +1,9 @@ import { Inject, Injectable, mixin, Type } from '@nestjs/common'; import { PipeTransform, Scope } from '@nestjs/common/interfaces'; import { REQUEST } from '@nestjs/core'; +import { DatabaseService } from 'src/common/database/services/database.service'; import { HelperArrayService } from 'src/common/helper/services/helper.array.service'; import { IPaginationFilterOptions } from 'src/common/pagination/interfaces/pagination.interface'; -import { PaginationService } from 'src/common/pagination/services/pagination.service'; import { IRequestApp } from 'src/common/request/interfaces/request.interface'; export function PaginationFilterNinEnumPipe( @@ -16,11 +16,11 @@ export function PaginationFilterNinEnumPipe( class MixinPaginationFilterInEnumPipe implements PipeTransform { constructor( @Inject(REQUEST) protected readonly request: IRequestApp, - private readonly paginationService: PaginationService, + private readonly databaseService: DatabaseService, private readonly helperArrayService: HelperArrayService ) {} - async transform(value: string): Promise> { + async transform(value: string): Promise { if (options?.raw) { this.addToRequestInstance(value); return { @@ -35,7 +35,7 @@ export function PaginationFilterNinEnumPipe( ) : (defaultValue as T[]); - return this.paginationService.filterNin(field, finalValue); + return this.databaseService.filterNin(field, finalValue); } addToRequestInstance(value: any): void { diff --git a/src/common/pagination/pipes/pagination.filter-not-equal.pipe.ts b/src/common/pagination/pipes/pagination.filter-not-equal.pipe.ts index f8cb5db6e..d42e87b03 100644 --- a/src/common/pagination/pipes/pagination.filter-not-equal.pipe.ts +++ b/src/common/pagination/pipes/pagination.filter-not-equal.pipe.ts @@ -1,8 +1,8 @@ import { Inject, Injectable, mixin, Type } from '@nestjs/common'; import { PipeTransform, Scope } from '@nestjs/common/interfaces'; import { REQUEST } from '@nestjs/core'; +import { DatabaseService } from 'src/common/database/services/database.service'; import { IPaginationFilterEqualOptions } from 'src/common/pagination/interfaces/pagination.interface'; -import { PaginationService } from 'src/common/pagination/services/pagination.service'; import { IRequestApp } from 'src/common/request/interfaces/request.interface'; export function PaginationFilterNotEqualPipe( @@ -13,12 +13,10 @@ export function PaginationFilterNotEqualPipe( class MixinPaginationFilterEqualPipe implements PipeTransform { constructor( @Inject(REQUEST) protected readonly request: IRequestApp, - private readonly paginationService: PaginationService + private readonly databaseService: DatabaseService ) {} - async transform( - value: string - ): Promise> { + async transform(value: string): Promise { if (!value) { return; } @@ -35,7 +33,7 @@ export function PaginationFilterNotEqualPipe( : value.trim(); this.addToRequestInstance(finalValue); - return this.paginationService.filterNotEqual(field, finalValue); + return this.databaseService.filterNotEqual(field, finalValue); } addToRequestInstance(value: any): void { diff --git a/src/common/pagination/pipes/pagination.filter-string-contain.pipe.ts b/src/common/pagination/pipes/pagination.filter-string-contain.pipe.ts index 66c2ea46e..e4a166a03 100644 --- a/src/common/pagination/pipes/pagination.filter-string-contain.pipe.ts +++ b/src/common/pagination/pipes/pagination.filter-string-contain.pipe.ts @@ -1,8 +1,8 @@ import { Inject, Injectable, mixin, Type } from '@nestjs/common'; import { PipeTransform, Scope } from '@nestjs/common/interfaces'; import { REQUEST } from '@nestjs/core'; +import { DatabaseService } from 'src/common/database/services/database.service'; import { IPaginationFilterOptions } from 'src/common/pagination/interfaces/pagination.interface'; -import { PaginationService } from 'src/common/pagination/services/pagination.service'; import { IRequestApp } from 'src/common/request/interfaces/request.interface'; export function PaginationFilterStringContainPipe( @@ -13,10 +13,10 @@ export function PaginationFilterStringContainPipe( class MixinPaginationFilterContainPipe implements PipeTransform { constructor( @Inject(REQUEST) protected readonly request: IRequestApp, - private readonly paginationService: PaginationService + private readonly databaseService: DatabaseService ) {} - async transform(value: string): Promise> { + async transform(value: string): Promise { if (!value) { return; } @@ -31,7 +31,7 @@ export function PaginationFilterStringContainPipe( value = value.trim(); this.addToRequestInstance(value); - return this.paginationService.filterContain(field, value); + return this.databaseService.filterContain(field, value); } addToRequestInstance(value: any): void { diff --git a/src/common/pagination/pipes/pagination.paging.pipe.ts b/src/common/pagination/pipes/pagination.paging.pipe.ts index f7d473eb6..7bfdab361 100644 --- a/src/common/pagination/pipes/pagination.paging.pipe.ts +++ b/src/common/pagination/pipes/pagination.paging.pipe.ts @@ -1,12 +1,12 @@ import { Inject, Injectable, mixin, Type } from '@nestjs/common'; import { PipeTransform, Scope } from '@nestjs/common/interfaces'; import { REQUEST } from '@nestjs/core'; -import { PAGINATION_DEFAULT_PAGE } from 'src/common/pagination/constants/pagination.constant'; +import { PAGINATION_DEFAULT_PER_PAGE } from 'src/common/pagination/constants/pagination.constant'; import { PaginationService } from 'src/common/pagination/services/pagination.service'; import { IRequestApp } from 'src/common/request/interfaces/request.interface'; export function PaginationPagingPipe( - defaultPerPage: number = PAGINATION_DEFAULT_PAGE + defaultPerPage: number = PAGINATION_DEFAULT_PER_PAGE ): Type { @Injectable({ scope: Scope.REQUEST }) class MixinPaginationPagingPipe implements PipeTransform { diff --git a/src/common/pagination/services/pagination.service.ts b/src/common/pagination/services/pagination.service.ts index 218da2713..0b67d794d 100644 --- a/src/common/pagination/services/pagination.service.ts +++ b/src/common/pagination/services/pagination.service.ts @@ -1,12 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { - DatabaseQueryContain, - DatabaseQueryEqual, - DatabaseQueryIn, - DatabaseQueryNin, - DatabaseQueryNotEqual, - DatabaseQueryOr, -} from 'src/common/database/decorators/database.decorator'; +import { DatabaseHelperQueryContain } from 'src/common/database/decorators/database.decorator'; import { PAGINATION_DEFAULT_AVAILABLE_ORDER_BY, PAGINATION_DEFAULT_MAX_PAGE, @@ -83,45 +76,10 @@ export class PaginationService implements IPaginationService { return; } - return DatabaseQueryOr( - availableSearch.map(val => DatabaseQueryContain(val, searchValue)) - ); - } - - filterEqual(field: string, filterValue: T): Record { - return DatabaseQueryEqual(field, filterValue); - } - - filterNotEqual( - field: string, - filterValue: T - ): Record { - return DatabaseQueryNotEqual(field, filterValue); - } - - filterContain(field: string, filterValue: string): Record { - return DatabaseQueryContain(field, filterValue); - } - - filterContainFullMatch( - field: string, - filterValue: string - ): Record { - return DatabaseQueryContain(field, filterValue, { fullWord: true }); - } - - filterIn(field: string, filterValue: T[]): Record { - return DatabaseQueryIn(field, filterValue); - } - - filterNin( - field: string, - filterValue: T[] - ): Record { - return DatabaseQueryNin(field, filterValue); - } - - filterDate(field: string, filterValue: Date): Record { - return DatabaseQueryEqual(field, filterValue); + return { + $or: availableSearch.map(val => + DatabaseHelperQueryContain(val, searchValue) + ), + }; } } diff --git a/src/common/request/decorators/request.decorator.ts b/src/common/request/decorators/request.decorator.ts index 61a89600e..0db8cb6b8 100644 --- a/src/common/request/decorators/request.decorator.ts +++ b/src/common/request/decorators/request.decorator.ts @@ -4,7 +4,6 @@ import { REQUEST_CUSTOM_TIMEOUT_VALUE_META_KEY, } from 'src/common/request/constants/request.constant'; -//! custom app timeout export function RequestTimeout(seconds: string): MethodDecorator { return applyDecorators( SetMetadata(REQUEST_CUSTOM_TIMEOUT_META_KEY, true), diff --git a/src/common/request/interfaces/request.interface.ts b/src/common/request/interfaces/request.interface.ts index 34bcccfa5..ded689c81 100644 --- a/src/common/request/interfaces/request.interface.ts +++ b/src/common/request/interfaces/request.interface.ts @@ -7,6 +7,7 @@ export interface IRequestApp extends Request { apiKey?: B; user?: T; + workspace?: string; __language: string; __version: string; diff --git a/src/common/request/request.module.ts b/src/common/request/request.module.ts index 89f39f385..46bd96be7 100644 --- a/src/common/request/request.module.ts +++ b/src/common/request/request.module.ts @@ -25,7 +25,6 @@ import { LessThanEqualOtherPropertyConstraint, LessThanOtherPropertyConstraint, } from 'src/common/request/validations/request.less-than-other-property.validation'; -import { SafeStringConstraint } from 'src/common/request/validations/request.safe-string.validation'; @Module({}) export class RequestModule { @@ -61,7 +60,6 @@ export class RequestModule { IsPasswordConstraint, LessThanEqualOtherPropertyConstraint, LessThanOtherPropertyConstraint, - SafeStringConstraint, ], imports: [], }; diff --git a/src/common/request/validations/request.safe-string.validation.ts b/src/common/request/validations/request.safe-string.validation.ts deleted file mode 100644 index a5ff9abbe..000000000 --- a/src/common/request/validations/request.safe-string.validation.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { - registerDecorator, - ValidationOptions, - ValidatorConstraint, - ValidatorConstraintInterface, -} from 'class-validator'; -import { HelperStringService } from 'src/common/helper/services/helper.string.service'; - -@ValidatorConstraint({ async: true }) -@Injectable() -export class SafeStringConstraint implements ValidatorConstraintInterface { - constructor(protected readonly helperStringService: HelperStringService) {} - - validate(value: string): boolean { - return value ? this.helperStringService.checkSafeString(value) : false; - } -} - -export function SafeString(validationOptions?: ValidationOptions) { - return function (object: Record, propertyName: string): void { - registerDecorator({ - name: 'SafeString', - target: object.constructor, - propertyName: propertyName, - options: validationOptions, - validator: SafeStringConstraint, - }); - }; -} diff --git a/src/common/response/dtos/response.paging.dto.ts b/src/common/response/dtos/response.paging.dto.ts index 978a3b857..6687f8c01 100644 --- a/src/common/response/dtos/response.paging.dto.ts +++ b/src/common/response/dtos/response.paging.dto.ts @@ -7,7 +7,7 @@ import { ResponseMetadataDto, } from 'src/common/response/dtos/response.dto'; -export class ResponsePagingMetadataRequestDto { +export class ResponsePagingMetadataPaginationRequestDto { @ApiProperty({ required: true, nullable: false, @@ -18,11 +18,10 @@ export class ResponsePagingMetadataRequestDto { @ApiProperty({ required: true, nullable: false, - example: {}, }) filters: Record< string, - string | number | boolean | Array + string | number | boolean | Array | Date >; @ApiProperty({ @@ -49,6 +48,7 @@ export class ResponsePagingMetadataRequestDto { @ApiProperty({ required: true, nullable: false, + enum: ENUM_PAGINATION_ORDER_DIRECTION_TYPE, example: ENUM_PAGINATION_ORDER_DIRECTION_TYPE.ASC, }) orderDirection: ENUM_PAGINATION_ORDER_DIRECTION_TYPE; @@ -63,6 +63,7 @@ export class ResponsePagingMetadataRequestDto { @ApiProperty({ required: true, nullable: false, + isArray: true, example: ['name', 'createdAt'], }) availableOrderBy: string[]; @@ -70,12 +71,12 @@ export class ResponsePagingMetadataRequestDto { @ApiProperty({ required: true, nullable: false, + enum: ENUM_PAGINATION_ORDER_DIRECTION_TYPE, + isArray: true, example: Object.values(ENUM_PAGINATION_ORDER_DIRECTION_TYPE), }) availableOrderDirection: ENUM_PAGINATION_ORDER_DIRECTION_TYPE[]; -} -export class ResponsePagingMetadataPaginationDto extends ResponsePagingMetadataRequestDto { @ApiProperty({ required: false, }) @@ -90,9 +91,9 @@ export class ResponsePagingMetadataPaginationDto extends ResponsePagingMetadataR export class ResponsePagingMetadataDto extends ResponseMetadataDto { @ApiProperty({ required: false, - type: ResponsePagingMetadataPaginationDto, + type: ResponsePagingMetadataPaginationRequestDto, }) - pagination?: ResponsePagingMetadataPaginationDto; + pagination?: ResponsePagingMetadataPaginationRequestDto; } export class ResponsePagingDto extends PickType(ResponseDto, [ @@ -133,7 +134,6 @@ export class ResponsePagingDto extends PickType(ResponseDto, [ @ApiProperty({ required: true, isArray: true, - default: [], }) data: Record[]; } diff --git a/src/common/response/interceptors/response.file.interceptor.ts b/src/common/response/interceptors/response.file.interceptor.ts index 1cccdd49e..f49fda519 100644 --- a/src/common/response/interceptors/response.file.interceptor.ts +++ b/src/common/response/interceptors/response.file.interceptor.ts @@ -57,6 +57,10 @@ export class ResponseFileExcelInterceptor throw new Error('Field data must in array'); } + const today = this.helperDateService.create(); + const timestamp = + this.helperDateService.getTimestamp(today); + if (type === ENUM_HELPER_FILE_EXCEL_TYPE.XLSX) { // create file const file: Buffer = this.fileService.writeExcel( @@ -64,8 +68,6 @@ export class ResponseFileExcelInterceptor ); // set headers - const timestamp = - this.helperDateService.createTimestamp(); response .setHeader('Content-Type', ENUM_FILE_MIME.XLSX) .setHeader( @@ -83,7 +85,6 @@ export class ResponseFileExcelInterceptor ); // set headers - const timestamp = this.helperDateService.createTimestamp(); response .setHeader('Content-Type', ENUM_FILE_MIME.CSV) .setHeader( diff --git a/src/common/response/interceptors/response.interceptor.ts b/src/common/response/interceptors/response.interceptor.ts index 6c7d104a9..7bcdc2cb1 100644 --- a/src/common/response/interceptors/response.interceptor.ts +++ b/src/common/response/interceptors/response.interceptor.ts @@ -24,6 +24,7 @@ import { } from 'src/common/response/dtos/response.dto'; import { ConfigService } from '@nestjs/config'; import { HelperDateService } from 'src/common/helper/services/helper.date.service'; +import { ENUM_MESSAGE_LANGUAGE } from 'src/common/message/enums/message.enum'; @Injectable() export class ResponseInterceptor @@ -63,12 +64,16 @@ export class ResponseInterceptor let data: Record = undefined; // metadata + const today = this.helperDateService.create(); const xPath = request.path; const xLanguage: string = - request.__language ?? this.messageService.getLanguage(); - const xTimestamp = this.helperDateService.createTimestamp(); - const xTimezone = - Intl.DateTimeFormat().resolvedOptions().timeZone; + request.__language ?? + this.configService.get( + 'message.language' + ); + const xTimestamp = + this.helperDateService.getTimestamp(today); + const xTimezone = this.helperDateService.getZone(today); const xVersion = request.__version ?? this.configService.get( diff --git a/src/common/response/interceptors/response.paging.interceptor.ts b/src/common/response/interceptors/response.paging.interceptor.ts index 7d7477249..150aad59c 100644 --- a/src/common/response/interceptors/response.paging.interceptor.ts +++ b/src/common/response/interceptors/response.paging.interceptor.ts @@ -24,6 +24,7 @@ import { } from 'src/common/response/dtos/response.paging.dto'; import { ConfigService } from '@nestjs/config'; import { HelperDateService } from 'src/common/helper/services/helper.date.service'; +import { ENUM_MESSAGE_LANGUAGE } from 'src/common/message/enums/message.enum'; @Injectable() export class ResponsePagingInterceptor @@ -62,13 +63,17 @@ export class ResponsePagingInterceptor let data: Record[] = []; // metadata + const today = this.helperDateService.create(); const xPath = request.path; const xPagination = request.__pagination; const xLanguage: string = - request.__language ?? this.messageService.getLanguage(); - const xTimestamp = this.helperDateService.createTimestamp(); - const xTimezone = - Intl.DateTimeFormat().resolvedOptions().timeZone; + request.__language ?? + this.configService.get( + 'message.language' + ); + const xTimestamp = + this.helperDateService.getTimestamp(today); + const xTimezone = this.helperDateService.getZone(today); const xVersion = request.__version ?? this.configService.get( diff --git a/src/configs/app.config.ts b/src/configs/app.config.ts index f92ee2b67..4be897b3e 100644 --- a/src/configs/app.config.ts +++ b/src/configs/app.config.ts @@ -1,6 +1,5 @@ import { registerAs } from '@nestjs/config'; import { version } from 'package.json'; -import { ENUM_APP_ENVIRONMENT } from 'src/app/enums/app.enum'; export default registerAs( 'app', @@ -9,14 +8,7 @@ export default registerAs( env: process.env.APP_ENV, timezone: process.env.APP_TIMEZONE, repoVersion: version, - globalPrefix: - process.env.APP_ENV === ENUM_APP_ENVIRONMENT.PRODUCTION - ? '' - : '/api', - - debug: process.env.APP_DEBUG === 'true', - - jobEnable: process.env.JOB_ENABLE === 'true', + globalPrefix: '/api', http: { enable: process.env.HTTP_ENABLE === 'true', @@ -24,7 +16,7 @@ export default registerAs( port: Number.parseInt(process.env.HTTP_PORT), }, urlVersion: { - enable: process.env.URL_VERSION_ENABLE === 'true', + enable: process.env.URL_VERSIONING_ENABLE === 'true', prefix: 'v', version: process.env.URL_VERSION, }, diff --git a/src/configs/auth.config.ts b/src/configs/auth.config.ts index 96e30f1f5..ba26e98cb 100644 --- a/src/configs/auth.config.ts +++ b/src/configs/auth.config.ts @@ -8,13 +8,13 @@ export default registerAs( accessToken: { secretKey: process.env.AUTH_JWT_ACCESS_TOKEN_SECRET_KEY, expirationTime: - ms(process.env.AUTH_JWT_ACCESS_TOKEN_EXPIRED) / 1000, // 1 hours + ms(process.env.AUTH_JWT_ACCESS_TOKEN_EXPIRED) / 1000, }, refreshToken: { secretKey: process.env.AUTH_JWT_REFRESH_TOKEN_SECRET_KEY, expirationTime: - ms(process.env.AUTH_JWT_REFRESH_TOKEN_EXPIRED) / 1000, // 1 hours + ms(process.env.AUTH_JWT_REFRESH_TOKEN_EXPIRED) / 1000, }, subject: process.env.AUTH_JWT_SUBJECT, diff --git a/src/configs/aws.config.ts b/src/configs/aws.config.ts index 34e536312..53c289480 100644 --- a/src/configs/aws.config.ts +++ b/src/configs/aws.config.ts @@ -4,14 +4,29 @@ export default registerAs( 'aws', (): Record => ({ s3: { - credential: { - key: process.env.AWS_S3_CREDENTIAL_KEY, - secret: process.env.AWS_S3_CREDENTIAL_SECRET, + presignExpired: 30 * 60 * 1000, // 30 mins + config: { + public: { + credential: { + key: process.env.AWS_S3_PUBLIC_CREDENTIAL_KEY, + secret: process.env.AWS_S3_PUBLIC_CREDENTIAL_SECRET, + }, + bucket: process.env.AWS_S3_PUBLIC_BUCKET ?? 'bucketPublic', + region: process.env.AWS_S3_PUBLIC_REGION, + baseUrl: `https://${process.env.AWS_S3_PUBLIC_BUCKET}.s3.${process.env.AWS_S3_PUBLIC_REGION}.amazonaws.com`, + cdnUrl: `https://${process.env.AWS_S3_PUBLIC_CDN}`, + }, + private: { + credential: { + key: process.env.AWS_S3_PRIVATE_CREDENTIAL_KEY, + secret: process.env.AWS_S3_PRIVATE_CREDENTIAL_SECRET, + }, + bucket: + process.env.AWS_S3_PRIVATE_BUCKET ?? 'bucketPrivate', + region: process.env.AWS_S3_PRIVATE_REGION, + baseUrl: `https://${process.env.AWS_S3_PRIVATE_BUCKET}.s3.${process.env.AWS_S3_PRIVATE_REGION}.amazonaws.com`, + }, }, - bucket: process.env.AWS_S3_BUCKET ?? 'bucket', - region: process.env.AWS_S3_REGION, - baseUrl: `https://${process.env.AWS_S3_BUCKET}.s3.${process.env.AWS_S3_REGION}.amazonaws.com`, - presignUrlExpired: 30 * 60 * 1000, }, ses: { credential: { diff --git a/src/configs/debug.config.ts b/src/configs/debug.config.ts index 988373a44..68a47f442 100644 --- a/src/configs/debug.config.ts +++ b/src/configs/debug.config.ts @@ -4,13 +4,10 @@ import ms from 'ms'; export default registerAs( 'debug', (): Record => ({ + enable: process.env.DEBUG_ENABLE === 'true', sentry: { dsn: process.env.SENTRY_DSN, timeout: ms('10s'), - logLevels: { - exception: ['fatal'], - request: ['log'], - }, }, }) ); diff --git a/src/configs/email.config.ts b/src/configs/email.config.ts index e2f26d112..db75dfc72 100644 --- a/src/configs/email.config.ts +++ b/src/configs/email.config.ts @@ -3,6 +3,7 @@ import { registerAs } from '@nestjs/config'; export default registerAs( 'email', (): Record => ({ + name: process.env.APP_NAME, fromEmail: 'noreply@mail.com', supportEmail: 'support@mail.com', diff --git a/src/configs/index.ts b/src/configs/index.ts index 3a9d68f55..21b10ff0d 100644 --- a/src/configs/index.ts +++ b/src/configs/index.ts @@ -5,11 +5,11 @@ import HelperConfig from 'src/configs/helper.config'; import AwsConfig from 'src/configs/aws.config'; import UserConfig from 'src/configs/user.config'; import MiddlewareConfig from 'src/configs/middleware.config'; -import RequestConfig from 'src/configs/request.config'; import DocConfig from 'src/configs/doc.config'; import MessageConfig from 'src/configs/message.config'; import EmailConfig from 'src/configs/email.config'; import RedisConfig from 'src/configs/redis.config'; +import DebugConfig from 'src/configs/debug.config'; export default [ AppConfig, @@ -19,9 +19,9 @@ export default [ AwsConfig, UserConfig, MiddlewareConfig, - RequestConfig, DocConfig, MessageConfig, EmailConfig, RedisConfig, + DebugConfig, ]; diff --git a/src/configs/middleware.config.ts b/src/configs/middleware.config.ts index 79aa7b819..20f64a561 100644 --- a/src/configs/middleware.config.ts +++ b/src/configs/middleware.config.ts @@ -21,10 +21,8 @@ export default registerAs( }, timeout: ms('30s'), // 30s based on ms module cors: { - allowMethod: ['GET', 'DELETE', 'PUT', 'PATCH', 'POST'], - allowOrigin: '*', // allow all origin - // allowOrigin: [/example\.com(\:\d{1,4})?$/], // allow all subdomain, and all port - // allowOrigin: [/example\.com$/], // allow all subdomain without port + allowMethod: ['GET', 'DELETE', 'PUT', 'PATCH', 'POST', 'HEAD'], + allowOrigin: process.env.MIDDLEWARE_CORS_ORIGIN?.split(',') ?? [], allowHeader: [ 'Accept', 'Accept-Language', diff --git a/src/configs/redis.config.ts b/src/configs/redis.config.ts index e6e85c78d..df77fe96b 100644 --- a/src/configs/redis.config.ts +++ b/src/configs/redis.config.ts @@ -3,13 +3,19 @@ import { registerAs } from '@nestjs/config'; export default registerAs( 'redis', (): Record => ({ - host: process.env.REDIS_HOST, - port: Number.parseInt(process.env.REDIS_PORT), - password: process.env.REDIS_PASSWORD, - tls: process.env.REDIS_TLS === 'true' ? {} : null, cached: { + host: process.env.REDIS_HOST, + port: Number.parseInt(process.env.REDIS_PORT), + password: process.env.REDIS_PASSWORD, + username: process.env.REDIS_USERNAME, ttl: 5 * 1000, // 5 mins max: 10, }, + queue: { + host: process.env.REDIS_HOST, + port: Number.parseInt(process.env.REDIS_PORT), + password: process.env.REDIS_PASSWORD, + username: process.env.REDIS_USERNAME, + }, }) ); diff --git a/src/configs/request.config.ts b/src/configs/request.config.ts deleted file mode 100644 index dd793db97..000000000 --- a/src/configs/request.config.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { registerAs } from '@nestjs/config'; -import ms from 'ms'; - -export default registerAs( - 'request', - (): Record => ({ - timeout: ms('30s'), // 30s based on ms module - }) -); diff --git a/src/configs/user.config.ts b/src/configs/user.config.ts index 3398fa147..af7e5bb77 100644 --- a/src/configs/user.config.ts +++ b/src/configs/user.config.ts @@ -1,14 +1,10 @@ import { registerAs } from '@nestjs/config'; -import { ENUM_APP_ENVIRONMENT } from 'src/app/enums/app.enum'; export default registerAs( 'user', (): Record => ({ usernamePrefix: 'user', usernamePattern: /^[a-zA-Z0-9-_]+$/, - uploadPath: - process.env.APP_ENV === ENUM_APP_ENVIRONMENT.PRODUCTION - ? '/user/{user}' - : '/test/user/{user}', + uploadPath: '/users/{user}', }) ); diff --git a/src/instrument.ts b/src/instrument.ts new file mode 100644 index 000000000..37f921bef --- /dev/null +++ b/src/instrument.ts @@ -0,0 +1,21 @@ +import 'dotenv/config'; +import * as Sentry from '@sentry/nestjs'; +import { nodeProfilingIntegration } from '@sentry/profiling-node'; +import debugConfigFunction from 'src/configs/debug.config'; +import appConfigFunction from 'src/configs/app.config'; + +const appConfigs = appConfigFunction(); +const debugConfigs = debugConfigFunction(); + +if (debugConfigs.sentry.dsn) { + Sentry.init({ + dsn: debugConfigs.sentry.dsn, + debug: false, + environment: appConfigs.env, + release: appConfigs.repoVersion, + shutdownTimeout: debugConfigs.sentry.timeout, + integrations: [nodeProfilingIntegration()], + tracesSampleRate: 1.0, + profilesSampleRate: 1.0, + }); +} diff --git a/src/languages/en/activity.json b/src/languages/en/activity.json new file mode 100644 index 000000000..441741909 --- /dev/null +++ b/src/languages/en/activity.json @@ -0,0 +1,12 @@ +{ + "list": "User activity logs are available here.", + "user": { + "createByAdmin": "Account has been created by an administrator", + "updateByAdmin": "Account information has been updated by an administrator.", + "inactiveByAdmin": "Account has been deactivated by an administrator.", + "activeByAdmin": "Account has been reactivated by an administrator.", + "blockedByAdmin": "Account has been blocked by an administrator.", + "changePassword": "Your password has been successfully changed.", + "uploadProfile": "Your profile picture has been successfully uploaded." + } +} diff --git a/src/languages/en/country.json b/src/languages/en/country.json index 4d06d48fc..322e6875b 100644 --- a/src/languages/en/country.json +++ b/src/languages/en/country.json @@ -1,11 +1,3 @@ { - "list": "List of countries retrieved successfully.", - "get": "Country details fetched successfully.", - "inactive": "Country marked as inactive.", - "active": "Country marked as active.", - "all": "Get all countries retrieved successfully.", - "error": { - "isActiveInvalid": "Invalid active status for the country.", - "notFound": "The requested country was not found." - } + "list": "List of countries retrieved successfully." } diff --git a/src/languages/en/passwordHistory.json b/src/languages/en/passwordHistory.json new file mode 100644 index 000000000..7dbd436ee --- /dev/null +++ b/src/languages/en/passwordHistory.json @@ -0,0 +1,3 @@ +{ + "list": "List of password histories retrieved successfully." +} diff --git a/src/languages/en/session.json b/src/languages/en/session.json new file mode 100644 index 000000000..0bb9ec17e --- /dev/null +++ b/src/languages/en/session.json @@ -0,0 +1,8 @@ +{ + "list": "List of sessions retrieved successfully.", + "revoke": "Successfully revoke session", + "error": { + "notFound": "Session not found", + "forbiddenRevoke": "You cannot revoke your current session" + } +} diff --git a/src/languages/en/user.json b/src/languages/en/user.json index 3fd833118..43b225eaf 100644 --- a/src/languages/en/user.json +++ b/src/languages/en/user.json @@ -2,12 +2,11 @@ "list": "List of users retrieved successfully.", "get": "User details fetched successfully.", "create": "New user created successfully.", - "inactive": "User marked as inactive.", - "active": "User marked as active.", - "blocked": "User blocked successfully.", + "updateStatus": "User marked as {status}.", "profile": "User profile retrieved successfully.", "updateProfile": "User profile updated successfully.", - "updateProfileUpload": "User profile picture uploaded successfully.", + "uploadPhotoProfile": "User request to upload a profile photo successfully.", + "updatePhotoProfile": "User profile picture uploaded successfully.", "deleteSelf": "User deleted successfully.", "updateMobileNumber": "User update mobile number successfully.", "updateClaimUsername": "User claim username successfully.", diff --git a/src/main.ts b/src/main.ts index 95091ba50..46f0855c7 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,3 +1,5 @@ +import './instrument'; + import { NestApplication, NestFactory } from '@nestjs/core'; import { Logger, VersioningType } from '@nestjs/common'; import { AppModule } from 'src/app/app.module'; @@ -8,20 +10,21 @@ import { plainToInstance } from 'class-transformer'; import { AppEnvDto } from 'src/app/dtos/app.env.dto'; import { MessageService } from 'src/common/message/services/message.service'; import { ENUM_APP_ENVIRONMENT } from 'src/app/enums/app.enum'; +import compression from 'compression'; +import { Logger as PinoLogger } from 'nestjs-pino'; async function bootstrap() { const app: NestApplication = await NestFactory.create(AppModule, { abortOnError: false, + bufferLogs: true, }); + const configService = app.get(ConfigService); const databaseUri: string = configService.get('database.uri'); const env: string = configService.get('app.env'); const timezone: string = configService.get('app.timezone'); const host: string = configService.get('app.http.host'); - const port: number = - env !== ENUM_APP_ENVIRONMENT.MIGRATION - ? configService.get('app.http.port') - : 9999; + const port: number = configService.get('app.http.port'); const globalPrefix: string = configService.get('app.globalPrefix'); const versioningPrefix: string = configService.get( 'app.urlVersion.prefix' @@ -33,12 +36,17 @@ async function bootstrap() { const versionEnable: string = configService.get( 'app.urlVersion.enable' ); - const jobEnable: boolean = configService.get('app.jobEnable'); const logger = new Logger('NestJs-Main'); process.env.NODE_ENV = env; process.env.TZ = timezone; + // logger + app.useLogger(app.get(PinoLogger)); + + // Compression + app.use(compression()); + // Global app.setGlobalPrefix(globalPrefix); @@ -60,9 +68,10 @@ async function bootstrap() { if (errors.length > 0) { const messageService = app.get(MessageService); const errorsMessage = messageService.setValidationMessage(errors); - logger.log(errorsMessage); - throw new Error('Env Variable Invalid'); + throw new Error('Env Variable Invalid', { + cause: errorsMessage, + }); } // Swagger @@ -71,28 +80,13 @@ async function bootstrap() { // Listen await app.listen(port, host); - logger.log(`==========================================================`); - - logger.log(`Environment Variable`); - - logger.log(JSON.parse(JSON.stringify(process.env))); - - logger.log(`==========================================================`); - if (env === ENUM_APP_ENVIRONMENT.MIGRATION) { logger.log(`On migrate the schema`); await app.close(); - - logger.log(`Migrate done`); - logger.log( - `==========================================================` - ); - - return; + process.exit(0); } - logger.log(`Job is ${jobEnable}`); logger.log( `Http is ${httpEnable}, ${ httpEnable ? 'routes registered' : 'no routes registered' @@ -107,6 +101,6 @@ async function bootstrap() { ); logger.log(`Database uri ${databaseUri}`); - logger.log(`==========================================================`); + return; } bootstrap(); diff --git a/src/migration/migration.module.ts b/src/migration/migration.module.ts index 47cc0befe..2f71829b2 100644 --- a/src/migration/migration.module.ts +++ b/src/migration/migration.module.ts @@ -3,15 +3,19 @@ import { CommandModule } from 'nestjs-command'; import { CommonModule } from 'src/common/common.module'; import { MigrationApiKeySeed } from 'src/migration/seeds/migration.api-key.seed'; import { MigrationCountrySeed } from 'src/migration/seeds/migration.country.seed'; -import { MigrationEmailSeed } from 'src/migration/seeds/migration.email.seed'; import { MigrationRoleSeed } from 'src/migration/seeds/migration.role.seed'; +import { MigrationTemplateSeed } from 'src/migration/seeds/migration.template.seed'; import { MigrationUserSeed } from 'src/migration/seeds/migration.user.seed'; +import { ActivityModule } from 'src/modules/activity/activity.module'; import { ApiKeyModule } from 'src/modules/api-key/api-key.module'; import { AuthModule } from 'src/modules/auth/auth.module'; import { CountryModule } from 'src/modules/country/country.module'; import { EmailModule } from 'src/modules/email/email.module'; +import { PasswordHistoryModule } from 'src/modules/password-history/password-history.module'; import { RoleModule } from 'src/modules/role/role.module'; +import { SessionModule } from 'src/modules/session/session.module'; import { UserModule } from 'src/modules/user/user.module'; + @Module({ imports: [ CommonModule, @@ -22,13 +26,17 @@ import { UserModule } from 'src/modules/user/user.module'; AuthModule, RoleModule, UserModule, + ActivityModule, + PasswordHistoryModule, + SessionModule, + CountryModule, ], providers: [ MigrationApiKeySeed, MigrationCountrySeed, - MigrationEmailSeed, MigrationUserSeed, MigrationRoleSeed, + MigrationTemplateSeed, ], exports: [], }) diff --git a/src/migration/seeds/migration.country.seed.ts b/src/migration/seeds/migration.country.seed.ts index 73e54f38e..5512d7e90 100644 --- a/src/migration/seeds/migration.country.seed.ts +++ b/src/migration/seeds/migration.country.seed.ts @@ -24,6 +24,7 @@ export class MigrationCountrySeed { phoneCode: ['62'], continent: 'Asia', timeZone: 'Asia/Jakarta', + currency: 'IDR', }, ]; diff --git a/src/migration/seeds/migration.role.seed.ts b/src/migration/seeds/migration.role.seed.ts index 316dee561..ebaa653a4 100644 --- a/src/migration/seeds/migration.role.seed.ts +++ b/src/migration/seeds/migration.role.seed.ts @@ -33,7 +33,7 @@ export class MigrationRoleSeed { }, { name: 'member', - type: ENUM_POLICY_ROLE_TYPE.USER, + type: ENUM_POLICY_ROLE_TYPE.MEMBER, permissions: [], }, { diff --git a/src/migration/seeds/migration.email.seed.ts b/src/migration/seeds/migration.template.seed.ts similarity index 62% rename from src/migration/seeds/migration.email.seed.ts rename to src/migration/seeds/migration.template.seed.ts index b35d433f8..a2884593c 100644 --- a/src/migration/seeds/migration.email.seed.ts +++ b/src/migration/seeds/migration.template.seed.ts @@ -3,28 +3,34 @@ import { Injectable } from '@nestjs/common'; import { EmailService } from 'src/modules/email/services/email.service'; @Injectable() -export class MigrationEmailSeed { +export class MigrationTemplateSeed { constructor(private readonly emailService: EmailService) {} @Command({ - command: 'migrate:email', - describe: 'migrate emails', + command: 'migrate:template', + describe: 'migrate templates', }) async migrate(): Promise { try { - await this.emailService.createWelcome(); + await this.emailService.importWelcome(); } catch (err: any) { throw new Error(err); } try { - await this.emailService.createChangePassword(); + await this.emailService.importWelcomeAdmin(); } catch (err: any) { throw new Error(err); } try { - await this.emailService.createTempPassword(); + await this.emailService.importChangePassword(); + } catch (err: any) { + throw new Error(err); + } + + try { + await this.emailService.importTempPassword(); } catch (err: any) { throw new Error(err); } @@ -33,8 +39,8 @@ export class MigrationEmailSeed { } @Command({ - command: 'rollback:email', - describe: 'rollback emails', + command: 'rollback:template', + describe: 'rollback templates', }) async rollback(): Promise { try { @@ -43,6 +49,12 @@ export class MigrationEmailSeed { throw new Error(err); } + try { + await this.emailService.deleteWelcomeAdmin(); + } catch (err: any) { + throw new Error(err); + } + try { await this.emailService.deleteChangePassword(); } catch (err: any) { diff --git a/src/migration/seeds/migration.user.seed.ts b/src/migration/seeds/migration.user.seed.ts index 03a8e0719..773de92ba 100644 --- a/src/migration/seeds/migration.user.seed.ts +++ b/src/migration/seeds/migration.user.seed.ts @@ -4,10 +4,18 @@ import { AuthService } from 'src/modules/auth/services/auth.service'; import { UserService } from 'src/modules/user/services/user.service'; import { RoleDoc } from 'src/modules/role/repository/entities/role.entity'; import { RoleService } from 'src/modules/role/services/role.service'; -import { ENUM_USER_SIGN_UP_FROM } from 'src/modules/user/enums/user.enum'; +import { + ENUM_USER_GENDER, + ENUM_USER_SIGN_UP_FROM, +} from 'src/modules/user/enums/user.enum'; import { CountryDoc } from 'src/modules/country/repository/entities/country.entity'; import { CountryService } from 'src/modules/country/services/country.service'; import { faker } from '@faker-js/faker'; +import { PasswordHistoryService } from 'src/modules/password-history/services/password-history.service'; +import { ActivityService } from 'src/modules/activity/services/activity.service'; +import { MessageService } from 'src/common/message/services/message.service'; +import { ENUM_PASSWORD_HISTORY_TYPE } from 'src/modules/password-history/enums/password-history.enum'; +import { SessionService } from 'src/modules/session/services/session.service'; @Injectable() export class MigrationUserSeed { @@ -15,7 +23,11 @@ export class MigrationUserSeed { private readonly authService: AuthService, private readonly userService: UserService, private readonly roleService: RoleService, - private readonly countryService: CountryService + private readonly countryService: CountryService, + private readonly passwordHistoryService: PasswordHistoryService, + private readonly activityService: ActivityService, + private readonly messageService: MessageService, + private readonly sessionService: SessionService ) {} @Command({ @@ -29,73 +41,106 @@ export class MigrationUserSeed { await this.roleService.findOneByName('superadmin'); const adminRole: RoleDoc = await this.roleService.findOneByName('admin'); + const country: CountryDoc = await this.countryService.findOneByAlpha2('ID'); const memberRole: RoleDoc = await this.roleService.findOneByName('member'); const userRole: RoleDoc = await this.roleService.findOneByName('user'); - try { - await this.userService.create( - { - role: superAdminRole._id, - name: 'superadmin', - email: 'superadmin@mail.com', - country: country._id, - }, - passwordHash, - ENUM_USER_SIGN_UP_FROM.SEED - ); - await this.userService.create( - { - role: adminRole._id, - name: 'admin', - email: 'admin@mail.com', - country: country._id, - }, - passwordHash, - ENUM_USER_SIGN_UP_FROM.SEED - ); - - await this.userService.create( - { - role: memberRole._id, - name: 'member', - email: 'member@mail.com', - country: country._id, - }, - passwordHash, - ENUM_USER_SIGN_UP_FROM.SEED - ); - await this.userService.create( - { - role: userRole._id, - name: 'user', - email: 'user@mail.com', - country: country._id, - }, - passwordHash, - ENUM_USER_SIGN_UP_FROM.SEED - ); + try { + const [superAdmin, admin, member, user] = await Promise.all([ + this.userService.create( + { + role: superAdminRole._id, + name: 'superadmin', + email: 'superadmin@mail.com', + country: country._id, + gender: ENUM_USER_GENDER.MALE, + }, + passwordHash, + ENUM_USER_SIGN_UP_FROM.SEED + ), + this.userService.create( + { + role: adminRole._id, + name: 'admin', + email: 'admin@mail.com', + country: country._id, + gender: ENUM_USER_GENDER.MALE, + }, + passwordHash, + ENUM_USER_SIGN_UP_FROM.SEED + ), + this.userService.create( + { + role: memberRole._id, + name: 'member', + email: 'member@mail.com', + country: country._id, + gender: ENUM_USER_GENDER.MALE, + }, + passwordHash, + ENUM_USER_SIGN_UP_FROM.SEED + ), + this.userService.create( + { + role: userRole._id, + name: 'user', + email: 'user@mail.com', + country: country._id, + gender: ENUM_USER_GENDER.MALE, + }, + passwordHash, + ENUM_USER_SIGN_UP_FROM.SEED + ), + ]); - // Add random user - const randomUser = Array(30) - .fill(0) - .map(() => - this.userService.create( - { - role: userRole._id, - name: faker.person.fullName(), - email: faker.internet.email(), - country: country._id, - }, - passwordHash, - ENUM_USER_SIGN_UP_FROM.SEED - ) - ); + const promises = [ + this.activityService.createByAdmin(superAdmin, { + by: superAdmin._id, + description: this.messageService.setMessage( + 'activity.user.createByAdmin' + ), + }), + this.passwordHistoryService.createByAdmin(superAdmin, { + by: superAdmin._id, + type: ENUM_PASSWORD_HISTORY_TYPE.SIGN_UP, + }), + this.activityService.createByAdmin(admin, { + by: superAdmin._id, + description: this.messageService.setMessage( + 'activity.user.createByAdmin' + ), + }), + this.passwordHistoryService.createByAdmin(admin, { + by: superAdmin._id, + type: ENUM_PASSWORD_HISTORY_TYPE.SIGN_UP, + }), + this.activityService.createByAdmin(member, { + by: superAdmin._id, + description: this.messageService.setMessage( + 'activity.user.createByAdmin' + ), + }), + this.passwordHistoryService.createByAdmin(member, { + by: superAdmin._id, + type: ENUM_PASSWORD_HISTORY_TYPE.SIGN_UP, + }), + this.activityService.createByAdmin(user, { + by: superAdmin._id, + description: this.messageService.setMessage( + 'activity.user.createByAdmin' + ), + }), + this.passwordHistoryService.createByAdmin(user, { + by: superAdmin._id, + type: ENUM_PASSWORD_HISTORY_TYPE.SIGN_UP, + }), + ]; - await Promise.all(randomUser); + await Promise.all(promises); } catch (err: any) { throw new Error(err); } @@ -110,6 +155,10 @@ export class MigrationUserSeed { async remove(): Promise { try { await this.userService.deleteMany({}); + await this.activityService.deleteMany({}); + await this.passwordHistoryService.deleteMany({}); + await this.sessionService.resetLoginSession(); + await this.sessionService.deleteMany({}); } catch (err: any) { throw new Error(err); } diff --git a/src/modules/activity/activity.module.ts b/src/modules/activity/activity.module.ts new file mode 100644 index 000000000..5d6d8e9f4 --- /dev/null +++ b/src/modules/activity/activity.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { ActivityRepositoryModule } from 'src/modules/activity/repository/activity.repository.module'; +import { ActivityService } from 'src/modules/activity/services/activity.service'; + +@Module({ + imports: [ActivityRepositoryModule], + exports: [ActivityService], + providers: [ActivityService], + controllers: [], +}) +export class ActivityModule {} diff --git a/src/modules/activity/controllers/activity.admin.controller.ts b/src/modules/activity/controllers/activity.admin.controller.ts new file mode 100644 index 000000000..04a94690c --- /dev/null +++ b/src/modules/activity/controllers/activity.admin.controller.ts @@ -0,0 +1,81 @@ +import { Controller, Get, Param } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; +import { PaginationQuery } from 'src/common/pagination/decorators/pagination.decorator'; +import { PaginationListDto } from 'src/common/pagination/dtos/pagination.list.dto'; +import { PaginationService } from 'src/common/pagination/services/pagination.service'; +import { RequestRequiredPipe } from 'src/common/request/pipes/request.required.pipe'; +import { ResponsePaging } from 'src/common/response/decorators/response.decorator'; +import { IResponsePaging } from 'src/common/response/interfaces/response.interface'; +import { ActivityAdminListDoc } from 'src/modules/activity/docs/activity.admin.doc'; +import { ActivityListResponseDto } from 'src/modules/activity/dtos/response/activity.list.response.dto'; +import { IActivityDoc } from 'src/modules/activity/interfaces/activity.interface'; +import { ActivityService } from 'src/modules/activity/services/activity.service'; +import { ApiKeyProtected } from 'src/modules/api-key/decorators/api-key.decorator'; +import { AuthJwtAccessProtected } from 'src/modules/auth/decorators/auth.jwt.decorator'; +import { + PolicyAbilityProtected, + PolicyRoleProtected, +} from 'src/modules/policy/decorators/policy.decorator'; +import { + ENUM_POLICY_ACTION, + ENUM_POLICY_ROLE_TYPE, + ENUM_POLICY_SUBJECT, +} from 'src/modules/policy/enums/policy.enum'; +import { UserParsePipe } from 'src/modules/user/pipes/user.parse.pipe'; +import { UserDoc } from 'src/modules/user/repository/entities/user.entity'; + +@ApiTags('modules.admin.activity') +@Controller({ + version: '1', + path: '/activity/:user', +}) +export class ActivityAdminController { + constructor( + private readonly paginationService: PaginationService, + private readonly activityService: ActivityService + ) {} + + @ActivityAdminListDoc() + @ResponsePaging('activity.list') + @PolicyAbilityProtected({ + subject: ENUM_POLICY_SUBJECT.ACTIVITY, + action: [ENUM_POLICY_ACTION.READ], + }) + @PolicyRoleProtected(ENUM_POLICY_ROLE_TYPE.ADMIN) + @AuthJwtAccessProtected() + @ApiKeyProtected() + @Get('/list') + async list( + @Param('user', RequestRequiredPipe, UserParsePipe) user: UserDoc, + @PaginationQuery() + { _search, _limit, _offset, _order }: PaginationListDto + ): Promise> { + const find: Record = { + ..._search, + }; + + const userHistories: IActivityDoc[] = + await this.activityService.findAllByUser(user._id, find, { + paging: { + limit: _limit, + offset: _offset, + }, + order: _order, + }); + const total: number = await this.activityService.getTotalByUser( + user._id, + find + ); + const totalPage: number = this.paginationService.totalPage( + total, + _limit + ); + + const mapped = await this.activityService.mapList(userHistories); + + return { + _pagination: { total, totalPage }, + data: mapped, + }; + } +} diff --git a/src/modules/activity/controllers/activity.shared.controller.ts b/src/modules/activity/controllers/activity.shared.controller.ts new file mode 100644 index 000000000..48c45c0b4 --- /dev/null +++ b/src/modules/activity/controllers/activity.shared.controller.ts @@ -0,0 +1,71 @@ +import { Controller, Get } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; +import { PaginationQuery } from 'src/common/pagination/decorators/pagination.decorator'; +import { PaginationListDto } from 'src/common/pagination/dtos/pagination.list.dto'; +import { PaginationService } from 'src/common/pagination/services/pagination.service'; +import { RequestRequiredPipe } from 'src/common/request/pipes/request.required.pipe'; +import { ResponsePaging } from 'src/common/response/decorators/response.decorator'; +import { IResponsePaging } from 'src/common/response/interfaces/response.interface'; +import { ActivitySharedListDoc } from 'src/modules/activity/docs/activity.shared.doc'; +import { ActivityListResponseDto } from 'src/modules/activity/dtos/response/activity.list.response.dto'; +import { IActivityDoc } from 'src/modules/activity/interfaces/activity.interface'; +import { ActivityService } from 'src/modules/activity/services/activity.service'; +import { ApiKeyProtected } from 'src/modules/api-key/decorators/api-key.decorator'; +import { + AuthJwtAccessProtected, + AuthJwtPayload, +} from 'src/modules/auth/decorators/auth.jwt.decorator'; +import { UserParsePipe } from 'src/modules/user/pipes/user.parse.pipe'; +import { UserDoc } from 'src/modules/user/repository/entities/user.entity'; + +@ApiTags('modules.shared.activity') +@Controller({ + version: '1', + path: '/activity', +}) +export class ActivitySharedController { + constructor( + private readonly paginationService: PaginationService, + private readonly activityService: ActivityService + ) {} + + @ActivitySharedListDoc() + @ResponsePaging('activity.list') + @AuthJwtAccessProtected() + @ApiKeyProtected() + @Get('/list') + async list( + @AuthJwtPayload('_id', RequestRequiredPipe, UserParsePipe) + user: UserDoc, + @PaginationQuery() + { _search, _limit, _offset, _order }: PaginationListDto + ): Promise> { + const find: Record = { + ..._search, + }; + + const userHistories: IActivityDoc[] = + await this.activityService.findAllByUser(user._id, find, { + paging: { + limit: _limit, + offset: _offset, + }, + order: _order, + }); + const total: number = await this.activityService.getTotalByUser( + user._id, + find + ); + const totalPage: number = this.paginationService.totalPage( + total, + _limit + ); + + const mapped = await this.activityService.mapList(userHistories); + + return { + _pagination: { total, totalPage }, + data: mapped, + }; + } +} diff --git a/src/modules/activity/docs/activity.admin.doc.ts b/src/modules/activity/docs/activity.admin.doc.ts new file mode 100644 index 000000000..7058eb192 --- /dev/null +++ b/src/modules/activity/docs/activity.admin.doc.ts @@ -0,0 +1,29 @@ +import { applyDecorators } from '@nestjs/common'; +import { + Doc, + DocAuth, + DocGuard, + DocRequest, + DocResponsePaging, +} from 'src/common/doc/decorators/doc.decorator'; +import { ActivityListResponseDto } from 'src/modules/activity/dtos/response/activity.list.response.dto'; +import { UserDocParamsId } from 'src/modules/user/constants/user.doc.constant'; + +export function ActivityAdminListDoc(): MethodDecorator { + return applyDecorators( + Doc({ + summary: 'get all user activities', + }), + DocRequest({ + params: UserDocParamsId, + }), + DocAuth({ + xApiKey: true, + jwtAccessToken: true, + }), + DocGuard({ role: true, policy: true }), + DocResponsePaging('activity.list', { + dto: ActivityListResponseDto, + }) + ); +} diff --git a/src/modules/activity/docs/activity.shared.doc.ts b/src/modules/activity/docs/activity.shared.doc.ts new file mode 100644 index 000000000..4d21c73c2 --- /dev/null +++ b/src/modules/activity/docs/activity.shared.doc.ts @@ -0,0 +1,22 @@ +import { applyDecorators } from '@nestjs/common'; +import { + Doc, + DocAuth, + DocResponsePaging, +} from 'src/common/doc/decorators/doc.decorator'; +import { ActivityListResponseDto } from 'src/modules/activity/dtos/response/activity.list.response.dto'; + +export function ActivitySharedListDoc(): MethodDecorator { + return applyDecorators( + Doc({ + summary: 'get all activity user itself', + }), + DocAuth({ + xApiKey: true, + jwtAccessToken: true, + }), + DocResponsePaging('activity.list', { + dto: ActivityListResponseDto, + }) + ); +} diff --git a/src/modules/activity/dtos/request/activity.create-by-admin.response.dto.ts b/src/modules/activity/dtos/request/activity.create-by-admin.response.dto.ts new file mode 100644 index 000000000..376b7bb16 --- /dev/null +++ b/src/modules/activity/dtos/request/activity.create-by-admin.response.dto.ts @@ -0,0 +1,8 @@ +import { IntersectionType, PickType } from '@nestjs/swagger'; +import { ActivityCreateResponse } from 'src/modules/activity/dtos/request/activity.create.response.dto'; +import { PasswordHistoryCreateByAdminRequestDto } from 'src/modules/password-history/dtos/request/password-history.create-by-admin.request.dto'; + +export class ActivityCreateByAdminResponse extends IntersectionType( + PickType(PasswordHistoryCreateByAdminRequestDto, ['by'] as const), + ActivityCreateResponse +) {} diff --git a/src/modules/activity/dtos/request/activity.create.response.dto.ts b/src/modules/activity/dtos/request/activity.create.response.dto.ts new file mode 100644 index 000000000..e4cc2c32c --- /dev/null +++ b/src/modules/activity/dtos/request/activity.create.response.dto.ts @@ -0,0 +1,13 @@ +import { faker } from '@faker-js/faker'; +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsString } from 'class-validator'; + +export class ActivityCreateResponse { + @ApiProperty({ + example: faker.string.uuid(), + required: true, + }) + @IsNotEmpty() + @IsString() + description: string; +} diff --git a/src/modules/activity/dtos/response/activity.list.response.dto.ts b/src/modules/activity/dtos/response/activity.list.response.dto.ts new file mode 100644 index 000000000..7fbda7a4b --- /dev/null +++ b/src/modules/activity/dtos/response/activity.list.response.dto.ts @@ -0,0 +1,27 @@ +import { faker } from '@faker-js/faker'; +import { ApiProperty, getSchemaPath } from '@nestjs/swagger'; +import { Type } from 'class-transformer'; +import { DatabaseDto } from 'src/common/database/dtos/database.dto'; +import { UserShortResponseDto } from 'src/modules/user/dtos/response/user.short.response.dto'; + +export class ActivityListResponseDto extends DatabaseDto { + @ApiProperty({ + required: true, + example: faker.string.uuid(), + }) + user: string; + + @ApiProperty({ + required: true, + example: faker.lorem.paragraph(), + }) + description: string; + + @ApiProperty({ + required: true, + type: UserShortResponseDto, + oneOf: [{ $ref: getSchemaPath(UserShortResponseDto) }], + }) + @Type(() => UserShortResponseDto) + by: UserShortResponseDto; +} diff --git a/src/modules/activity/interfaces/activity.interface.ts b/src/modules/activity/interfaces/activity.interface.ts new file mode 100644 index 000000000..225f56580 --- /dev/null +++ b/src/modules/activity/interfaces/activity.interface.ts @@ -0,0 +1,16 @@ +import { + ActivityDoc, + ActivityEntity, +} from 'src/modules/activity/repository/entities/activity.entity'; +import { + UserDoc, + UserEntity, +} from 'src/modules/user/repository/entities/user.entity'; + +export interface IActivityEntity extends Omit { + by: UserEntity; +} + +export interface IActivityDoc extends Omit { + by: UserDoc; +} diff --git a/src/modules/activity/interfaces/activity.service.interface.ts b/src/modules/activity/interfaces/activity.service.interface.ts new file mode 100644 index 000000000..7af48a597 --- /dev/null +++ b/src/modules/activity/interfaces/activity.service.interface.ts @@ -0,0 +1,59 @@ +import { + IDatabaseCreateOptions, + IDatabaseDeleteManyOptions, + IDatabaseFindAllOptions, + IDatabaseGetTotalOptions, + IDatabaseOptions, +} from 'src/common/database/interfaces/database.interface'; +import { ActivityCreateByAdminResponse } from 'src/modules/activity/dtos/request/activity.create-by-admin.response.dto'; +import { ActivityCreateResponse } from 'src/modules/activity/dtos/request/activity.create.response.dto'; +import { ActivityListResponseDto } from 'src/modules/activity/dtos/response/activity.list.response.dto'; +import { + IActivityDoc, + IActivityEntity, +} from 'src/modules/activity/interfaces/activity.interface'; +import { ActivityDoc } from 'src/modules/activity/repository/entities/activity.entity'; +import { UserDoc } from 'src/modules/user/repository/entities/user.entity'; + +export interface IActivityService { + findAll( + find?: Record, + options?: IDatabaseFindAllOptions + ): Promise; + findAllByUser( + user: string, + find?: Record, + options?: IDatabaseFindAllOptions + ): Promise; + findOneById(_id: string, options?: IDatabaseOptions): Promise; + findOne( + find: Record, + options?: IDatabaseOptions + ): Promise; + getTotal( + find?: Record, + options?: IDatabaseGetTotalOptions + ): Promise; + getTotalByUser( + user: string, + find?: Record, + options?: IDatabaseGetTotalOptions + ): Promise; + createByUser( + user: UserDoc, + { description }: ActivityCreateResponse, + options?: IDatabaseCreateOptions + ): Promise; + createByAdmin( + user: UserDoc, + { by, description }: ActivityCreateByAdminResponse, + options?: IDatabaseCreateOptions + ): Promise; + deleteMany( + find: Record, + options?: IDatabaseDeleteManyOptions + ): Promise; + mapList( + userHistories: IActivityDoc[] | IActivityEntity[] + ): Promise; +} diff --git a/src/modules/activity/repository/activity.repository.module.ts b/src/modules/activity/repository/activity.repository.module.ts new file mode 100644 index 000000000..354f9134e --- /dev/null +++ b/src/modules/activity/repository/activity.repository.module.ts @@ -0,0 +1,26 @@ +import { Module } from '@nestjs/common'; +import { MongooseModule } from '@nestjs/mongoose'; +import { DATABASE_CONNECTION_NAME } from 'src/common/database/constants/database.constant'; +import { + ActivityEntity, + ActivitySchema, +} from 'src/modules/activity/repository/entities/activity.entity'; +import { ActivityRepository } from 'src/modules/activity/repository/repositories/activity.repository'; + +@Module({ + providers: [ActivityRepository], + exports: [ActivityRepository], + controllers: [], + imports: [ + MongooseModule.forFeature( + [ + { + name: ActivityEntity.name, + schema: ActivitySchema, + }, + ], + DATABASE_CONNECTION_NAME + ), + ], +}) +export class ActivityRepositoryModule {} diff --git a/src/modules/activity/repository/entities/activity.entity.ts b/src/modules/activity/repository/entities/activity.entity.ts new file mode 100644 index 000000000..b51f6e366 --- /dev/null +++ b/src/modules/activity/repository/entities/activity.entity.ts @@ -0,0 +1,39 @@ +import { DatabaseEntityBase } from 'src/common/database/bases/database.entity'; +import { + DatabaseEntity, + DatabaseProp, + DatabaseSchema, +} from 'src/common/database/decorators/database.decorator'; +import { IDatabaseDocument } from 'src/common/database/interfaces/database.interface'; +import { UserEntity } from 'src/modules/user/repository/entities/user.entity'; + +export const ActivityTableName = 'Activities'; + +@DatabaseEntity({ collection: ActivityTableName }) +export class ActivityEntity extends DatabaseEntityBase { + @DatabaseProp({ + required: true, + index: true, + trim: true, + type: String, + ref: UserEntity.name, + }) + user: string; + + @DatabaseProp({ + required: true, + type: String, + }) + description: string; + + @DatabaseProp({ + required: true, + trim: true, + type: String, + ref: UserEntity.name, + }) + by: string; +} + +export const ActivitySchema = DatabaseSchema(ActivityEntity); +export type ActivityDoc = IDatabaseDocument; diff --git a/src/modules/activity/repository/repositories/activity.repository.ts b/src/modules/activity/repository/repositories/activity.repository.ts new file mode 100644 index 000000000..43a03ddd5 --- /dev/null +++ b/src/modules/activity/repository/repositories/activity.repository.ts @@ -0,0 +1,53 @@ +import { Injectable } from '@nestjs/common'; +import { Model } from 'mongoose'; +import { DatabaseRepositoryBase } from 'src/common/database/bases/database.repository'; +import { InjectDatabaseModel } from 'src/common/database/decorators/database.decorator'; +import { + ActivityDoc, + ActivityEntity, +} from 'src/modules/activity/repository/entities/activity.entity'; +import { CountryEntity } from 'src/modules/country/repository/entities/country.entity'; +import { RoleEntity } from 'src/modules/role/repository/entities/role.entity'; +import { UserEntity } from 'src/modules/user/repository/entities/user.entity'; + +@Injectable() +export class ActivityRepository extends DatabaseRepositoryBase< + ActivityEntity, + ActivityDoc +> { + constructor( + @InjectDatabaseModel(ActivityEntity.name) + private readonly activityModel: Model + ) { + super(activityModel, { + path: 'by', + localField: 'by', + foreignField: '_id', + model: UserEntity.name, + justOne: true, + populate: [ + { + path: 'role', + localField: 'role', + foreignField: '_id', + model: RoleEntity.name, + justOne: true, + }, + { + path: 'country', + localField: 'country', + foreignField: '_id', + model: CountryEntity.name, + justOne: true, + }, + { + path: 'mobileNumber.country', + localField: 'mobileNumber.country', + foreignField: '_id', + model: CountryEntity.name, + justOne: true, + }, + ], + }); + } +} diff --git a/src/modules/activity/services/activity.service.ts b/src/modules/activity/services/activity.service.ts new file mode 100644 index 000000000..c0e1619c6 --- /dev/null +++ b/src/modules/activity/services/activity.service.ts @@ -0,0 +1,125 @@ +import { Injectable } from '@nestjs/common'; +import { plainToInstance } from 'class-transformer'; +import { Document } from 'mongoose'; +import { + IDatabaseCreateOptions, + IDatabaseDeleteManyOptions, + IDatabaseFindAllOptions, + IDatabaseFindOneOptions, + IDatabaseGetTotalOptions, +} from 'src/common/database/interfaces/database.interface'; +import { ActivityCreateByAdminResponse } from 'src/modules/activity/dtos/request/activity.create-by-admin.response.dto'; +import { ActivityCreateResponse } from 'src/modules/activity/dtos/request/activity.create.response.dto'; +import { ActivityListResponseDto } from 'src/modules/activity/dtos/response/activity.list.response.dto'; +import { + IActivityDoc, + IActivityEntity, +} from 'src/modules/activity/interfaces/activity.interface'; +import { IActivityService } from 'src/modules/activity/interfaces/activity.service.interface'; +import { + ActivityDoc, + ActivityEntity, +} from 'src/modules/activity/repository/entities/activity.entity'; +import { ActivityRepository } from 'src/modules/activity/repository/repositories/activity.repository'; +import { UserDoc } from 'src/modules/user/repository/entities/user.entity'; + +@Injectable() +export class ActivityService implements IActivityService { + constructor(private readonly activityRepository: ActivityRepository) {} + + async findAll( + find?: Record, + options?: IDatabaseFindAllOptions + ): Promise { + return this.activityRepository.findAll(find, { + ...options, + join: true, + }); + } + + async findAllByUser( + user: string, + find?: Record, + options?: IDatabaseFindAllOptions + ): Promise { + return this.activityRepository.findAll( + { ...find, user }, + { ...options, join: true } + ); + } + + async findOneById( + _id: string, + options?: IDatabaseFindOneOptions + ): Promise { + return this.activityRepository.findOneById(_id, options); + } + + async findOne( + find: Record, + options?: IDatabaseFindOneOptions + ): Promise { + return this.activityRepository.findOne(find, options); + } + + async getTotal( + find?: Record, + options?: IDatabaseGetTotalOptions + ): Promise { + return this.activityRepository.getTotal(find, options); + } + + async getTotalByUser( + user: string, + find?: Record, + options?: IDatabaseGetTotalOptions + ): Promise { + return this.activityRepository.getTotal({ ...find, user }, options); + } + + async createByUser( + user: UserDoc, + { description }: ActivityCreateResponse, + options?: IDatabaseCreateOptions + ): Promise { + const create: ActivityEntity = new ActivityEntity(); + create.description = description; + create.user = user._id; + create.by = user._id; + + return this.activityRepository.create(create, options); + } + + async createByAdmin( + user: UserDoc, + { by, description }: ActivityCreateByAdminResponse, + options?: IDatabaseCreateOptions + ): Promise { + const create: ActivityEntity = new ActivityEntity(); + create.description = description; + create.user = user._id; + create.by = by; + + return this.activityRepository.create(create, options); + } + + async deleteMany( + find: Record, + options?: IDatabaseDeleteManyOptions + ): Promise { + await this.activityRepository.deleteMany(find, options); + + return true; + } + + async mapList( + userHistories: IActivityDoc[] | IActivityEntity[] + ): Promise { + return plainToInstance( + ActivityListResponseDto, + userHistories.map((e: IActivityDoc | IActivityEntity) => + e instanceof Document ? e.toObject() : e + ) + ); + } +} diff --git a/src/modules/api-key/controllers/api-key.admin.controller.ts b/src/modules/api-key/controllers/api-key.admin.controller.ts index a8cbfe7a2..6f866f41b 100644 --- a/src/modules/api-key/controllers/api-key.admin.controller.ts +++ b/src/modules/api-key/controllers/api-key.admin.controller.ts @@ -177,13 +177,7 @@ export class ApiKeyAdminController { @ApiKeyProtected() @Patch('/update/:apiKey/reset') async reset( - @Param( - 'apiKey', - RequestRequiredPipe, - ApiKeyParsePipe, - new ApiKeyIsActivePipe([true]), - ApiKeyNotExpiredPipe - ) + @Param('apiKey', RequestRequiredPipe, ApiKeyParsePipe) apiKey: ApiKeyDoc ): Promise> { const updated: ApiKeyResetResponseDto = @@ -206,13 +200,7 @@ export class ApiKeyAdminController { @Put('/update/:apiKey') async update( @Body() body: ApiKeyUpdateRequestDto, - @Param( - 'apiKey', - RequestRequiredPipe, - ApiKeyParsePipe, - new ApiKeyIsActivePipe([true]), - ApiKeyNotExpiredPipe - ) + @Param('apiKey', RequestRequiredPipe, ApiKeyParsePipe) apiKey: ApiKeyDoc ): Promise> { await this.apiKeyService.update(apiKey, body); @@ -282,12 +270,7 @@ export class ApiKeyAdminController { @Put('/update/:apiKey/date') async updateDate( @Body() body: ApiKeyUpdateDateRequestDto, - @Param( - 'apiKey', - RequestRequiredPipe, - ApiKeyParsePipe, - new ApiKeyIsActivePipe([true]) - ) + @Param('apiKey', RequestRequiredPipe, ApiKeyParsePipe) apiKey: ApiKeyDoc ): Promise> { await this.apiKeyService.updateDate(apiKey, body); diff --git a/src/modules/api-key/dtos/response/api-key.get.response.dto.ts b/src/modules/api-key/dtos/response/api-key.get.response.dto.ts index d290626b7..32eb722a1 100644 --- a/src/modules/api-key/dtos/response/api-key.get.response.dto.ts +++ b/src/modules/api-key/dtos/response/api-key.get.response.dto.ts @@ -44,6 +44,7 @@ export class ApiKeyGetResponseDto extends DatabaseDto { @ApiProperty({ description: 'Type of api key', example: ENUM_API_KEY_TYPE.DEFAULT, + enum: ENUM_API_KEY_TYPE, required: true, nullable: false, }) diff --git a/src/modules/api-key/repository/entities/api-key.entity.ts b/src/modules/api-key/repository/entities/api-key.entity.ts index b71c278bd..18f57c8f2 100644 --- a/src/modules/api-key/repository/entities/api-key.entity.ts +++ b/src/modules/api-key/repository/entities/api-key.entity.ts @@ -5,15 +5,16 @@ import { } from 'src/common/database/decorators/database.decorator'; import { ENUM_API_KEY_TYPE } from 'src/modules/api-key/enums/api-key.enum'; import { IDatabaseDocument } from 'src/common/database/interfaces/database.interface'; -import { DatabaseEntityAbstract } from 'src/common/database/abstracts/database.entity.abstract'; +import { DatabaseEntityBase } from 'src/common/database/bases/database.entity'; export const ApiKeyTableName = 'ApiKeys'; @DatabaseEntity({ collection: ApiKeyTableName }) -export class ApiKeyEntity extends DatabaseEntityAbstract { +export class ApiKeyEntity extends DatabaseEntityBase { @DatabaseProp({ required: true, enum: ENUM_API_KEY_TYPE, + type: String, index: true, trim: true, }) diff --git a/src/modules/api-key/repository/repositories/api-key.repository.ts b/src/modules/api-key/repository/repositories/api-key.repository.ts index b25d74322..053807e6a 100644 --- a/src/modules/api-key/repository/repositories/api-key.repository.ts +++ b/src/modules/api-key/repository/repositories/api-key.repository.ts @@ -1,19 +1,19 @@ import { Injectable } from '@nestjs/common'; import { Model } from 'mongoose'; -import { DatabaseRepositoryAbstract } from 'src/common/database/abstracts/database.repository.abstract'; -import { DatabaseModel } from 'src/common/database/decorators/database.decorator'; +import { DatabaseRepositoryBase } from 'src/common/database/bases/database.repository'; +import { InjectDatabaseModel } from 'src/common/database/decorators/database.decorator'; import { ApiKeyDoc, ApiKeyEntity, } from 'src/modules/api-key/repository/entities/api-key.entity'; @Injectable() -export class ApiKeyRepository extends DatabaseRepositoryAbstract< +export class ApiKeyRepository extends DatabaseRepositoryBase< ApiKeyEntity, ApiKeyDoc > { constructor( - @DatabaseModel(ApiKeyEntity.name) + @InjectDatabaseModel(ApiKeyEntity.name) private readonly ApiKeyDoc: Model ) { super(ApiKeyDoc); diff --git a/src/modules/api-key/services/api-key.service.ts b/src/modules/api-key/services/api-key.service.ts index 338e71f08..49ab18a70 100644 --- a/src/modules/api-key/services/api-key.service.ts +++ b/src/modules/api-key/services/api-key.service.ts @@ -5,8 +5,8 @@ import { IDatabaseCreateOptions, IDatabaseDeleteManyOptions, IDatabaseFindAllOptions, + IDatabaseFindOneOptions, IDatabaseGetTotalOptions, - IDatabaseOptions, IDatabaseSaveOptions, IDatabaseUpdateManyOptions, } from 'src/common/database/interfaces/database.interface'; @@ -30,6 +30,7 @@ import { } from 'src/modules/api-key/repository/entities/api-key.entity'; import { ApiKeyRepository } from 'src/modules/api-key/repository/repositories/api-key.repository'; import { Document } from 'mongoose'; +import { ENUM_HELPER_DATE_DAY_OF } from 'src/common/helper/enums/helper.enum'; @Injectable() export class ApiKeyService implements IApiKeyService { @@ -54,28 +55,28 @@ export class ApiKeyService implements IApiKeyService { async findOneById( _id: string, - options?: IDatabaseOptions + options?: IDatabaseFindOneOptions ): Promise { return this.apiKeyRepository.findOneById(_id, options); } async findOne( find: Record, - options?: IDatabaseOptions + options?: IDatabaseFindOneOptions ): Promise { return this.apiKeyRepository.findOne(find, options); } async findOneByKey( key: string, - options?: IDatabaseOptions + options?: IDatabaseFindOneOptions ): Promise { return this.apiKeyRepository.findOne({ key }, options); } async findOneByActiveKey( key: string, - options?: IDatabaseOptions + options?: IDatabaseFindOneOptions ): Promise { return this.apiKeyRepository.findOne( { @@ -101,20 +102,24 @@ export class ApiKeyService implements IApiKeyService { const secret = await this.createSecret(); const hash: string = await this.createHashApiKey(key, secret); - const dto: ApiKeyEntity = new ApiKeyEntity(); - dto.name = name; - dto.key = key; - dto.hash = hash; - dto.isActive = true; - dto.type = type; + const data: ApiKeyEntity = new ApiKeyEntity(); + data.name = name; + data.key = key; + data.hash = hash; + data.isActive = true; + data.type = type; if (startDate && endDate) { - dto.startDate = this.helperDateService.startOfDay(startDate); - dto.endDate = this.helperDateService.endOfDay(endDate); + data.startDate = this.helperDateService.create(startDate, { + dayOf: ENUM_HELPER_DATE_DAY_OF.START, + }); + data.endDate = this.helperDateService.create(endDate, { + dayOf: ENUM_HELPER_DATE_DAY_OF.END, + }); } const created: ApiKeyDoc = - await this.apiKeyRepository.create(dto, options); + await this.apiKeyRepository.create(data, options); return { _id: created._id, key: created.key, secret }; } @@ -132,20 +137,24 @@ export class ApiKeyService implements IApiKeyService { ): Promise { const hash: string = await this.createHashApiKey(key, secret); - const dto: ApiKeyEntity = new ApiKeyEntity(); - dto.name = name; - dto.key = key; - dto.hash = hash; - dto.isActive = true; - dto.type = type; + const data: ApiKeyEntity = new ApiKeyEntity(); + data.name = name; + data.key = key; + data.hash = hash; + data.isActive = true; + data.type = type; if (startDate && endDate) { - dto.startDate = this.helperDateService.startOfDay(startDate); - dto.endDate = this.helperDateService.endOfDay(endDate); + data.startDate = this.helperDateService.create(startDate, { + dayOf: ENUM_HELPER_DATE_DAY_OF.START, + }); + data.endDate = this.helperDateService.create(endDate, { + dayOf: ENUM_HELPER_DATE_DAY_OF.END, + }); } const created: ApiKeyDoc = - await this.apiKeyRepository.create(dto, options); + await this.apiKeyRepository.create(data, options); return { _id: created._id, key: created.key, secret }; } @@ -183,8 +192,12 @@ export class ApiKeyService implements IApiKeyService { { startDate, endDate }: ApiKeyUpdateDateRequestDto, options?: IDatabaseSaveOptions ): Promise { - repository.startDate = this.helperDateService.startOfDay(startDate); - repository.endDate = this.helperDateService.endOfDay(endDate); + repository.startDate = this.helperDateService.create(startDate, { + dayOf: ENUM_HELPER_DATE_DAY_OF.START, + }); + repository.endDate = this.helperDateService.create(endDate, { + dayOf: ENUM_HELPER_DATE_DAY_OF.END, + }); return this.apiKeyRepository.save(repository, options); } @@ -237,36 +250,29 @@ export class ApiKeyService implements IApiKeyService { find: Record, options?: IDatabaseDeleteManyOptions ): Promise { - try { - await this.apiKeyRepository.deleteMany(find, options); + await this.apiKeyRepository.deleteMany(find, options); - return true; - } catch (error: unknown) { - throw error; - } + return true; } async inactiveManyByEndDate( options?: IDatabaseUpdateManyOptions ): Promise { - try { - await this.apiKeyRepository.updateMany( - { - endDate: { - $lte: this.helperDateService.create(), - }, - isActive: true, - }, - { - isActive: false, + const today = this.helperDateService.create(); + await this.apiKeyRepository.updateMany( + { + endDate: { + $lte: today, }, - options - ); + isActive: true, + }, + { + isActive: false, + }, + options + ); - return true; - } catch (error: unknown) { - throw error; - } + return true; } async mapList( diff --git a/src/modules/auth/controllers/auth.admin.controller.ts b/src/modules/auth/controllers/auth.admin.controller.ts index 487b94741..9dec78504 100644 --- a/src/modules/auth/controllers/auth.admin.controller.ts +++ b/src/modules/auth/controllers/auth.admin.controller.ts @@ -1,3 +1,4 @@ +import { InjectQueue } from '@nestjs/bullmq'; import { Controller, InternalServerErrorException, @@ -8,14 +9,19 @@ import { ApiTags } from '@nestjs/swagger'; import { Queue } from 'bullmq'; import { ClientSession, Connection } from 'mongoose'; import { ENUM_APP_STATUS_CODE_ERROR } from 'src/app/enums/app.status-code.enum'; -import { DatabaseConnection } from 'src/common/database/decorators/database.decorator'; +import { InjectDatabaseConnection } from 'src/common/database/decorators/database.decorator'; import { RequestRequiredPipe } from 'src/common/request/pipes/request.required.pipe'; import { Response } from 'src/common/response/decorators/response.decorator'; import { ApiKeyProtected } from 'src/modules/api-key/decorators/api-key.decorator'; -import { AuthJwtAccessProtected } from 'src/modules/auth/decorators/auth.jwt.decorator'; +import { + AuthJwtAccessProtected, + AuthJwtPayload, +} from 'src/modules/auth/decorators/auth.jwt.decorator'; import { AuthAdminUpdatePasswordDoc } from 'src/modules/auth/docs/auth.admin.doc'; import { AuthService } from 'src/modules/auth/services/auth.service'; -import { ENUM_EMAIL } from 'src/modules/email/enums/email.enum'; +import { ENUM_SEND_EMAIL_PROCESS } from 'src/modules/email/enums/email.enum'; +import { ENUM_PASSWORD_HISTORY_TYPE } from 'src/modules/password-history/enums/password-history.enum'; +import { PasswordHistoryService } from 'src/modules/password-history/services/password-history.service'; import { PolicyAbilityProtected, PolicyRoleProtected, @@ -29,7 +35,6 @@ import { UserNotSelfPipe } from 'src/modules/user/pipes/user.not-self.pipe'; import { UserParsePipe } from 'src/modules/user/pipes/user.parse.pipe'; import { UserDoc } from 'src/modules/user/repository/entities/user.entity'; import { UserService } from 'src/modules/user/services/user.service'; -import { WorkerQueue } from 'src/worker/decorators/worker.decorator'; import { ENUM_WORKER_QUEUES } from 'src/worker/enums/worker.enum'; @ApiTags('modules.admin.auth') @@ -39,11 +44,13 @@ import { ENUM_WORKER_QUEUES } from 'src/worker/enums/worker.enum'; }) export class AuthAdminController { constructor( - @DatabaseConnection() private readonly databaseConnection: Connection, - @WorkerQueue(ENUM_WORKER_QUEUES.EMAIL_QUEUE) + @InjectDatabaseConnection() + private readonly databaseConnection: Connection, + @InjectQueue(ENUM_WORKER_QUEUES.EMAIL_QUEUE) private readonly emailQueue: Queue, private readonly authService: AuthService, - private readonly userService: UserService + private readonly userService: UserService, + private readonly passwordHistoryService: PasswordHistoryService ) {} @AuthAdminUpdatePasswordDoc() @@ -57,6 +64,7 @@ export class AuthAdminController { @ApiKeyProtected() @Put('/update/:user/password') async updatePassword( + @AuthJwtPayload('_id') _id: string, @Param('user', RequestRequiredPipe, UserParsePipe, UserNotSelfPipe) user: UserDoc ): Promise { @@ -80,17 +88,25 @@ export class AuthAdminController { session, }); + await this.passwordHistoryService.createByAdmin( + user, + { + by: _id, + type: ENUM_PASSWORD_HISTORY_TYPE.TEMPORARY, + }, + { session } + ); + this.emailQueue.add( - ENUM_EMAIL.TEMP_PASSWORD, + ENUM_SEND_EMAIL_PROCESS.TEMPORARY_PASSWORD, { - email: user.email, - name: user.name, + send: { email: user.email, name: user.name }, passwordExpiredAt: password.passwordExpired, password: passwordString, }, { debounce: { - id: `${ENUM_EMAIL.TEMP_PASSWORD}-${user._id}`, + id: `${ENUM_SEND_EMAIL_PROCESS.TEMPORARY_PASSWORD}-${user._id}`, ttl: 1000, }, } diff --git a/src/modules/auth/controllers/auth.public.controller.ts b/src/modules/auth/controllers/auth.public.controller.ts index b86c82fd0..bc2266215 100644 --- a/src/modules/auth/controllers/auth.public.controller.ts +++ b/src/modules/auth/controllers/auth.public.controller.ts @@ -9,17 +9,15 @@ import { InternalServerErrorException, NotFoundException, Post, + Req, } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { ApiKeyProtected } from 'src/modules/api-key/decorators/api-key.decorator'; -import { ENUM_AUTH_LOGIN_FROM } from 'src/modules/auth/enums/auth.enum'; import { AuthJwtPayload } from 'src/modules/auth/decorators/auth.jwt.decorator'; import { AuthSocialAppleProtected, AuthSocialGoogleProtected, } from 'src/modules/auth/decorators/auth.social.decorator'; -import { AuthJwtAccessPayloadDto } from 'src/modules/auth/dtos/jwt/auth.jwt.access-payload.dto'; -import { AuthJwtRefreshPayloadDto } from 'src/modules/auth/dtos/jwt/auth.jwt.refresh-payload.dto'; import { AuthSocialGooglePayloadDto } from 'src/modules/auth/dtos/social/auth.social.google-payload.dto'; import { AuthService } from 'src/modules/auth/services/auth.service'; import { Response } from 'src/common/response/decorators/response.decorator'; @@ -42,15 +40,21 @@ import { AuthSocialApplePayloadDto } from 'src/modules/auth/dtos/social/auth.soc import { AuthSignUpRequestDto } from 'src/modules/auth/dtos/request/auth.sign-up.request.dto'; import { ENUM_COUNTRY_STATUS_CODE_ERROR } from 'src/modules/country/enums/country.status-code.enum'; import { ClientSession } from 'mongoose'; -import { ENUM_EMAIL } from 'src/modules/email/enums/email.enum'; +import { ENUM_SEND_EMAIL_PROCESS } from 'src/modules/email/enums/email.enum'; import { ENUM_APP_STATUS_CODE_ERROR } from 'src/app/enums/app.status-code.enum'; -import { DatabaseConnection } from 'src/common/database/decorators/database.decorator'; +import { InjectDatabaseConnection } from 'src/common/database/decorators/database.decorator'; import { ENUM_WORKER_QUEUES } from 'src/worker/enums/worker.enum'; -import { WorkerQueue } from 'src/worker/decorators/worker.decorator'; import { Connection } from 'mongoose'; import { Queue } from 'bullmq'; import { CountryService } from 'src/modules/country/services/country.service'; import { RoleService } from 'src/modules/role/services/role.service'; +import { PasswordHistoryService } from 'src/modules/password-history/services/password-history.service'; +import { ENUM_PASSWORD_HISTORY_TYPE } from 'src/modules/password-history/enums/password-history.enum'; +import { SessionService } from 'src/modules/session/services/session.service'; +import { IRequestApp } from 'src/common/request/interfaces/request.interface'; +import { ActivityService } from 'src/modules/activity/services/activity.service'; +import { MessageService } from 'src/common/message/services/message.service'; +import { InjectQueue } from '@nestjs/bullmq'; @ApiTags('modules.public.auth') @Controller({ @@ -59,13 +63,18 @@ import { RoleService } from 'src/modules/role/services/role.service'; }) export class AuthPublicController { constructor( - @DatabaseConnection() private readonly databaseConnection: Connection, - @WorkerQueue(ENUM_WORKER_QUEUES.EMAIL_QUEUE) + @InjectDatabaseConnection() + private readonly databaseConnection: Connection, + @InjectQueue(ENUM_WORKER_QUEUES.EMAIL_QUEUE) private readonly emailQueue: Queue, private readonly userService: UserService, private readonly authService: AuthService, private readonly countryService: CountryService, - private readonly roleService: RoleService + private readonly roleService: RoleService, + private readonly passwordHistoryService: PasswordHistoryService, + private readonly sessionService: SessionService, + private readonly activityService: ActivityService, + private readonly messageService: MessageService ) {} @AuthPublicLoginCredentialDoc() @@ -74,7 +83,8 @@ export class AuthPublicController { @HttpCode(HttpStatus.OK) @Post('/login/credential') async loginWithCredential( - @Body() { email, password }: AuthLoginRequestDto + @Body() { email, password }: AuthLoginRequestDto, + @Req() request: IRequestApp ): Promise> { let user: UserDoc = await this.userService.findOneByEmail(email); if (!user) { @@ -135,39 +145,41 @@ export class AuthPublicController { }); } - const roleType = userWithRole.role.type; - const tokenType: string = await this.authService.getTokenType(); + const databaseSession: ClientSession = + await this.databaseConnection.startSession(); + databaseSession.startTransaction(); - const expiresInAccessToken: number = - await this.authService.getAccessTokenExpirationTime(); - const payloadAccessToken: AuthJwtAccessPayloadDto = - await this.authService.createPayloadAccessToken( + try { + const session = await this.sessionService.create( + request, + { + user: user._id, + }, + { session: databaseSession } + ); + const token = await this.authService.createToken( userWithRole, - ENUM_AUTH_LOGIN_FROM.CREDENTIAL + session._id ); - const accessToken: string = await this.authService.createAccessToken( - user.email, - payloadAccessToken - ); - const payloadRefreshToken: AuthJwtRefreshPayloadDto = - await this.authService.createPayloadRefreshToken( - payloadAccessToken - ); - const refreshToken: string = await this.authService.createRefreshToken( - user.email, - payloadRefreshToken - ); + await this.sessionService.setLoginSession(userWithRole, session); - return { - data: { - tokenType, - roleType, - expiresIn: expiresInAccessToken, - accessToken, - refreshToken, - }, - }; + await databaseSession.commitTransaction(); + await databaseSession.endSession(); + + return { + data: token, + }; + } catch (err: any) { + await databaseSession.abortTransaction(); + await databaseSession.endSession(); + + throw new InternalServerErrorException({ + statusCode: ENUM_APP_STATUS_CODE_ERROR.UNKNOWN, + message: 'http.serverError.internalServerError', + _error: err.message, + }); + } } @AuthPublicLoginSocialGoogleDoc() @@ -176,7 +188,8 @@ export class AuthPublicController { @Post('/login/social/google') async loginWithGoogle( @AuthJwtPayload() - { email }: AuthSocialGooglePayloadDto + { email }: AuthSocialGooglePayloadDto, + @Req() request: IRequestApp ): Promise> { const user: UserDoc = await this.userService.findOneByEmail(email); if (!user) { @@ -210,39 +223,41 @@ export class AuthPublicController { }); } - const roleType = userWithRole.role.type; - const tokenType: string = await this.authService.getTokenType(); + const databaseSession: ClientSession = + await this.databaseConnection.startSession(); + databaseSession.startTransaction(); - const expiresInAccessToken: number = - await this.authService.getAccessTokenExpirationTime(); - const payloadAccessToken: AuthJwtAccessPayloadDto = - await this.authService.createPayloadAccessToken( + try { + const session = await this.sessionService.create( + request, + { + user: user._id, + }, + { session: databaseSession } + ); + const token = await this.authService.createToken( userWithRole, - ENUM_AUTH_LOGIN_FROM.SOCIAL_GOOGLE + session._id ); - const accessToken: string = await this.authService.createAccessToken( - user.email, - payloadAccessToken - ); - const payloadRefreshToken: AuthJwtRefreshPayloadDto = - await this.authService.createPayloadRefreshToken( - payloadAccessToken - ); - const refreshToken: string = await this.authService.createRefreshToken( - user.email, - payloadRefreshToken - ); + await this.sessionService.setLoginSession(userWithRole, session); + + await databaseSession.commitTransaction(); + await databaseSession.endSession(); + + return { + data: token, + }; + } catch (err: any) { + await databaseSession.abortTransaction(); + await databaseSession.endSession(); - return { - data: { - tokenType, - roleType, - expiresIn: expiresInAccessToken, - accessToken, - refreshToken, - }, - }; + throw new InternalServerErrorException({ + statusCode: ENUM_APP_STATUS_CODE_ERROR.UNKNOWN, + message: 'http.serverError.internalServerError', + _error: err.message, + }); + } } @AuthPublicLoginSocialAppleDoc() @@ -251,7 +266,8 @@ export class AuthPublicController { @Post('/login/social/apple') async loginWithApple( @AuthJwtPayload() - { email }: AuthSocialApplePayloadDto + { email }: AuthSocialApplePayloadDto, + @Req() request: IRequestApp ): Promise> { const user: UserDoc = await this.userService.findOneByEmail(email); if (!user) { @@ -285,39 +301,41 @@ export class AuthPublicController { }); } - const roleType = userWithRole.role.type; - const tokenType: string = await this.authService.getTokenType(); + const databaseSession: ClientSession = + await this.databaseConnection.startSession(); + databaseSession.startTransaction(); - const expiresInAccessToken: number = - await this.authService.getAccessTokenExpirationTime(); - const payloadAccessToken: AuthJwtAccessPayloadDto = - await this.authService.createPayloadAccessToken( + try { + const session = await this.sessionService.create( + request, + { + user: user._id, + }, + { session: databaseSession } + ); + const token = await this.authService.createToken( userWithRole, - ENUM_AUTH_LOGIN_FROM.SOCIAL_GOOGLE + session._id ); - const accessToken: string = await this.authService.createAccessToken( - user.email, - payloadAccessToken - ); - const payloadRefreshToken: AuthJwtRefreshPayloadDto = - await this.authService.createPayloadRefreshToken( - payloadAccessToken - ); - const refreshToken: string = await this.authService.createRefreshToken( - user.email, - payloadRefreshToken - ); + await this.sessionService.setLoginSession(userWithRole, session); + + await databaseSession.commitTransaction(); + await databaseSession.endSession(); + + return { + data: token, + }; + } catch (err: any) { + await databaseSession.abortTransaction(); + await databaseSession.endSession(); - return { - data: { - tokenType, - roleType, - expiresIn: expiresInAccessToken, - accessToken, - refreshToken, - }, - }; + throw new InternalServerErrorException({ + statusCode: ENUM_APP_STATUS_CODE_ERROR.UNKNOWN, + message: 'http.serverError.internalServerError', + _error: err.message, + }); + } } @AuthPublicSignUpDoc() @@ -331,7 +349,7 @@ export class AuthPublicController { const promises: Promise[] = [ this.roleService.findOneByName('user'), this.userService.existByEmail(email), - this.countryService.findOneActiveById(country), + this.countryService.findOneById(country), ]; const [role, emailExist, checkCountry] = await Promise.all(promises); @@ -372,15 +390,32 @@ export class AuthPublicController { { session } ); + await this.passwordHistoryService.createByUser( + user, + { + type: ENUM_PASSWORD_HISTORY_TYPE.SIGN_UP, + }, + { session } + ); + + await this.activityService.createByUser( + user, + { + description: this.messageService.setMessage( + 'activity.user.signUp' + ), + }, + { session } + ); + this.emailQueue.add( - ENUM_EMAIL.WELCOME, + ENUM_SEND_EMAIL_PROCESS.WELCOME, { - email, - name, + send: { email, name }, }, { debounce: { - id: `${ENUM_EMAIL.WELCOME}-${user._id}`, + id: `${ENUM_SEND_EMAIL_PROCESS.WELCOME}-${user._id}`, ttl: 1000, }, } diff --git a/src/modules/auth/controllers/auth.shared.controller.ts b/src/modules/auth/controllers/auth.shared.controller.ts index f8265b1d6..99157d129 100644 --- a/src/modules/auth/controllers/auth.shared.controller.ts +++ b/src/modules/auth/controllers/auth.shared.controller.ts @@ -8,6 +8,7 @@ import { InternalServerErrorException, Patch, Post, + UnauthorizedException, } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { ClientSession, Connection } from 'mongoose'; @@ -22,7 +23,7 @@ import { AuthJwtAccessPayloadDto } from 'src/modules/auth/dtos/jwt/auth.jwt.acce import { AuthJwtRefreshPayloadDto } from 'src/modules/auth/dtos/jwt/auth.jwt.refresh-payload.dto'; import { IAuthPassword } from 'src/modules/auth/interfaces/auth.interface'; import { AuthService } from 'src/modules/auth/services/auth.service'; -import { DatabaseConnection } from 'src/common/database/decorators/database.decorator'; +import { InjectDatabaseConnection } from 'src/common/database/decorators/database.decorator'; import { Response } from 'src/common/response/decorators/response.decorator'; import { IResponse } from 'src/common/response/interfaces/response.interface'; import { ENUM_USER_STATUS_CODE_ERROR } from 'src/modules/user/enums/user.status-code.enum'; @@ -34,10 +35,17 @@ import { AuthSharedRefreshDoc, } from 'src/modules/auth/docs/auth.shared.doc'; import { ENUM_APP_STATUS_CODE_ERROR } from 'src/app/enums/app.status-code.enum'; -import { WorkerQueue } from 'src/worker/decorators/worker.decorator'; import { ENUM_WORKER_QUEUES } from 'src/worker/enums/worker.enum'; import { Queue } from 'bullmq'; -import { ENUM_EMAIL } from 'src/modules/email/enums/email.enum'; +import { ENUM_PASSWORD_HISTORY_TYPE } from 'src/modules/password-history/enums/password-history.enum'; +import { PasswordHistoryService } from 'src/modules/password-history/services/password-history.service'; +import { SessionService } from 'src/modules/session/services/session.service'; +import { ENUM_SESSION_STATUS_CODE_ERROR } from 'src/modules/session/enums/session.status-code.enum'; +import { ActivityService } from 'src/modules/activity/services/activity.service'; +import { MessageService } from 'src/common/message/services/message.service'; +import { ENUM_SEND_EMAIL_PROCESS } from 'src/modules/email/enums/email.enum'; +import { InjectQueue } from '@nestjs/bullmq'; +import { IUserDoc } from 'src/modules/user/interfaces/user.interface'; @ApiTags('modules.shared.auth') @Controller({ @@ -46,11 +54,16 @@ import { ENUM_EMAIL } from 'src/modules/email/enums/email.enum'; }) export class AuthSharedController { constructor( - @DatabaseConnection() private readonly databaseConnection: Connection, - @WorkerQueue(ENUM_WORKER_QUEUES.EMAIL_QUEUE) + @InjectDatabaseConnection() + private readonly databaseConnection: Connection, + @InjectQueue(ENUM_WORKER_QUEUES.EMAIL_QUEUE) private readonly emailQueue: Queue, private readonly userService: UserService, - private readonly authService: AuthService + private readonly authService: AuthService, + private readonly passwordHistoryService: PasswordHistoryService, + private readonly sessionService: SessionService, + private readonly activityService: ActivityService, + private readonly messageService: MessageService ) {} @AuthSharedRefreshDoc() @@ -62,30 +75,21 @@ export class AuthSharedController { async refresh( @AuthJwtToken() refreshToken: string, @AuthJwtPayload() - { _id, loginFrom }: AuthJwtRefreshPayloadDto + { _id, session }: AuthJwtRefreshPayloadDto ): Promise> { - const user = await this.userService.findOneActiveById(_id); - - const roleType = user.role.type; - const tokenType: string = await this.authService.getTokenType(); - - const expiresInAccessToken: number = - await this.authService.getAccessTokenExpirationTime(); - const payloadAccessToken: AuthJwtAccessPayloadDto = - await this.authService.createPayloadAccessToken(user, loginFrom); - const accessToken: string = await this.authService.createAccessToken( - user.email, - payloadAccessToken - ); + const checkActive = await this.sessionService.findLoginSession(session); + if (!checkActive) { + throw new UnauthorizedException({ + statusCode: ENUM_SESSION_STATUS_CODE_ERROR.NOT_FOUND, + message: 'session.error.notFound', + }); + } + + const user: IUserDoc = await this.userService.findOneActiveById(_id); + const token = await this.authService.refreshToken(user, refreshToken); return { - data: { - tokenType, - roleType, - expiresIn: expiresInAccessToken, - accessToken, - refreshToken, - }, + data: token, }; } @@ -129,6 +133,28 @@ export class AuthSharedController { body.newPassword ); + const checkPassword = + await this.passwordHistoryService.findOneUsedByUser( + user._id, + user.password + ); + if (checkPassword) { + const passwordPeriod = + await this.passwordHistoryService.getPasswordPeriod(); + throw new BadRequestException({ + statusCode: ENUM_USER_STATUS_CODE_ERROR.PASSWORD_MUST_NEW, + message: 'user.error.passwordMustNew', + _metadata: { + customProperty: { + messageProperties: { + period: passwordPeriod, + expiredAt: checkPassword.expiredAt, + }, + }, + }, + }); + } + const session: ClientSession = await this.databaseConnection.startSession(); session.startTransaction(); @@ -141,15 +167,37 @@ export class AuthSharedController { session, }); + await this.passwordHistoryService.createByUser( + user, + { + type: ENUM_PASSWORD_HISTORY_TYPE.CHANGE, + }, + { session } + ); + + await this.activityService.createByUser( + user, + { + description: this.messageService.setMessage( + 'activity.user.changePassword' + ), + }, + { session } + ); + + // remove all session + await this.sessionService.updateManyRevokeByUser(user._id, { + session, + }); + this.emailQueue.add( - ENUM_EMAIL.CHANGE_PASSWORD, + ENUM_SEND_EMAIL_PROCESS.CHANGE_PASSWORD, { - email: user.email, - name: user.name, + send: { email: user.email, name: user.name }, }, { debounce: { - id: `${ENUM_EMAIL.CHANGE_PASSWORD}-${user._id}`, + id: `${ENUM_SEND_EMAIL_PROCESS.CHANGE_PASSWORD}-${user._id}`, ttl: 1000, }, } diff --git a/src/modules/auth/dtos/jwt/auth.jwt.access-payload.dto.ts b/src/modules/auth/dtos/jwt/auth.jwt.access-payload.dto.ts index f4f0a4cdd..faa59639e 100644 --- a/src/modules/auth/dtos/jwt/auth.jwt.access-payload.dto.ts +++ b/src/modules/auth/dtos/jwt/auth.jwt.access-payload.dto.ts @@ -1,5 +1,5 @@ import { faker } from '@faker-js/faker'; -import { ApiProperty } from '@nestjs/swagger'; +import { ApiProperty, getSchemaPath } from '@nestjs/swagger'; import { Transform, Type } from 'class-transformer'; import { ENUM_AUTH_LOGIN_FROM } from 'src/modules/auth/enums/auth.enum'; import { @@ -67,6 +67,12 @@ export class AuthJwtAccessPayloadDto { }) _id: string; + @ApiProperty({ + required: true, + nullable: false, + }) + session: string; + @ApiProperty({ required: true, nullable: false, @@ -90,8 +96,8 @@ export class AuthJwtAccessPayloadDto { required: true, nullable: false, type: AuthJwtAccessPayloadPermissionDto, + oneOf: [{ $ref: getSchemaPath(AuthJwtAccessPayloadPermissionDto) }], isArray: true, - default: [], }) @Type(() => AuthJwtAccessPayloadPermissionDto) permissions: AuthJwtAccessPayloadPermissionDto[]; diff --git a/src/modules/auth/dtos/request/auth.sign-up.request.dto.ts b/src/modules/auth/dtos/request/auth.sign-up.request.dto.ts index 5ead06980..16e9e9b2c 100644 --- a/src/modules/auth/dtos/request/auth.sign-up.request.dto.ts +++ b/src/modules/auth/dtos/request/auth.sign-up.request.dto.ts @@ -6,6 +6,7 @@ import { UserCreateRequestDto } from 'src/modules/user/dtos/request/user.create. export class AuthSignUpRequestDto extends OmitType(UserCreateRequestDto, [ 'role', + 'gender', ] as const) { @ApiProperty({ description: 'string password', diff --git a/src/modules/auth/guards/jwt/auth.jwt.access.guard.ts b/src/modules/auth/guards/jwt/auth.jwt.access.guard.ts index a87beb219..7e3b9e7a9 100644 --- a/src/modules/auth/guards/jwt/auth.jwt.access.guard.ts +++ b/src/modules/auth/guards/jwt/auth.jwt.access.guard.ts @@ -1,10 +1,15 @@ import { AuthGuard } from '@nestjs/passport'; import { Injectable, UnauthorizedException } from '@nestjs/common'; import { ENUM_AUTH_STATUS_CODE_ERROR } from 'src/modules/auth/enums/auth.status-code.enum'; +import { AuthJwtAccessPayloadDto } from 'src/modules/auth/dtos/jwt/auth.jwt.access-payload.dto'; @Injectable() export class AuthJwtAccessGuard extends AuthGuard('jwtAccess') { - handleRequest(err: Error, user: TUser, info: Error): TUser { + handleRequest( + err: Error, + user: T, + info: Error + ): T { if (err || !user) { throw new UnauthorizedException({ statusCode: ENUM_AUTH_STATUS_CODE_ERROR.JWT_ACCESS_TOKEN, diff --git a/src/modules/auth/guards/jwt/auth.jwt.refresh.guard.ts b/src/modules/auth/guards/jwt/auth.jwt.refresh.guard.ts index ec728a97d..c7a912f9f 100644 --- a/src/modules/auth/guards/jwt/auth.jwt.refresh.guard.ts +++ b/src/modules/auth/guards/jwt/auth.jwt.refresh.guard.ts @@ -1,10 +1,15 @@ import { AuthGuard } from '@nestjs/passport'; import { Injectable, UnauthorizedException } from '@nestjs/common'; import { ENUM_AUTH_STATUS_CODE_ERROR } from 'src/modules/auth/enums/auth.status-code.enum'; +import { AuthJwtRefreshPayloadDto } from 'src/modules/auth/dtos/jwt/auth.jwt.refresh-payload.dto'; @Injectable() export class AuthJwtRefreshGuard extends AuthGuard('jwtRefresh') { - handleRequest(err: Error, user: TUser, info: Error): TUser { + handleRequest( + err: Error, + user: T, + info: Error + ): T { if (err || !user) { throw new UnauthorizedException({ statusCode: ENUM_AUTH_STATUS_CODE_ERROR.JWT_REFRESH_TOKEN, diff --git a/src/modules/auth/interfaces/auth.service.interface.ts b/src/modules/auth/interfaces/auth.service.interface.ts index c67fef379..989f15012 100644 --- a/src/modules/auth/interfaces/auth.service.interface.ts +++ b/src/modules/auth/interfaces/auth.service.interface.ts @@ -1,4 +1,3 @@ -import { Document } from 'mongoose'; import { ENUM_AUTH_LOGIN_FROM } from 'src/modules/auth/enums/auth.enum'; import { AuthJwtAccessPayloadDto } from 'src/modules/auth/dtos/jwt/auth.jwt.access-payload.dto'; import { AuthJwtRefreshPayloadDto } from 'src/modules/auth/dtos/jwt/auth.jwt.refresh-payload.dto'; @@ -8,6 +7,8 @@ import { IAuthPassword, IAuthPasswordOptions, } from 'src/modules/auth/interfaces/auth.interface'; +import { IUserDoc } from 'src/modules/user/interfaces/user.interface'; +import { AuthLoginResponseDto } from 'src/modules/auth/dtos/response/auth.login.response.dto'; export interface IAuthService { createAccessToken( @@ -26,8 +27,10 @@ export interface IAuthService { passwordString: string, passwordHash: string ): Promise; - createPayloadAccessToken( - data: T, + createPayloadAccessToken( + data: IUserDoc, + session: string, + loginDate: Date, loginFrom: ENUM_AUTH_LOGIN_FROM ): Promise; createPayloadRefreshToken({ @@ -42,11 +45,7 @@ export interface IAuthService { ): Promise; createPasswordRandom(): Promise; checkPasswordExpired(passwordExpired: Date): Promise; - getTokenType(): Promise; - getAccessTokenExpirationTime(): Promise; - getRefreshTokenExpirationTime(): Promise; - getIssuer(): Promise; - getAudience(): Promise; + createToken(user: IUserDoc, session: string): Promise; getPasswordAttempt(): Promise; getPasswordMaxAttempt(): Promise; appleGetTokenInfo(code: string): Promise; diff --git a/src/modules/auth/services/auth.service.ts b/src/modules/auth/services/auth.service.ts index eaca8eda5..af8ca3e9d 100644 --- a/src/modules/auth/services/auth.service.ts +++ b/src/modules/auth/services/auth.service.ts @@ -17,7 +17,9 @@ import { AuthSocialApplePayloadDto } from 'src/modules/auth/dtos/social/auth.soc import { AuthSocialGooglePayloadDto } from 'src/modules/auth/dtos/social/auth.social.google-payload.dto'; import { ENUM_AUTH_LOGIN_FROM } from 'src/modules/auth/enums/auth.enum'; import { plainToInstance } from 'class-transformer'; -import { Document } from 'mongoose'; +import { IUserDoc } from 'src/modules/user/interfaces/user.interface'; +import { AuthLoginResponseDto } from 'src/modules/auth/dtos/response/auth.login.response.dto'; +import { Duration } from 'luxon'; @Injectable() export class AuthService implements IAuthService { @@ -188,11 +190,12 @@ export class AuthService implements IAuthService { ); } - async createPayloadAccessToken( - data: T, + async createPayloadAccessToken( + data: IUserDoc, + session: string, + loginDate: Date, loginFrom: ENUM_AUTH_LOGIN_FROM ): Promise { - const loginDate = this.helperDateService.create(); const plainObject: any = data.toObject(); return plainToInstance(AuthJwtAccessPayloadDto, { @@ -201,6 +204,7 @@ export class AuthService implements IAuthService { role: plainObject.role._id, email: plainObject.email, permissions: plainObject.role.permissions, + session, loginDate, loginFrom, }); @@ -208,11 +212,13 @@ export class AuthService implements IAuthService { async createPayloadRefreshToken({ _id, + session, loginFrom, loginDate, }: AuthJwtAccessPayloadDto): Promise { return { _id, + session, loginFrom, loginDate, }; @@ -228,10 +234,14 @@ export class AuthService implements IAuthService { ): Promise { const salt: string = await this.createSalt(this.passwordSaltLength); - const passwordExpired: Date = this.helperDateService.forwardInSeconds( - options?.temporary - ? this.passwordExpiredTemporary - : this.passwordExpiredIn + const today = this.helperDateService.create(); + const passwordExpired: Date = this.helperDateService.forward( + today, + Duration.fromObject({ + seconds: options?.temporary + ? this.passwordExpiredTemporary + : this.passwordExpiredIn, + }) ); const passwordCreated: Date = this.helperDateService.create(); const passwordHash = this.helperHashService.bcrypt(password, salt); @@ -255,24 +265,70 @@ export class AuthService implements IAuthService { return today > passwordExpiredConvert; } - async getTokenType(): Promise { - return this.jwtPrefixAuthorization; - } + async createToken( + user: IUserDoc, + session: string + ): Promise { + const loginDate = this.helperDateService.create(); + const roleType = user.role.type; + + const payloadAccessToken: AuthJwtAccessPayloadDto = + await this.createPayloadAccessToken( + user, + session, + loginDate, + ENUM_AUTH_LOGIN_FROM.CREDENTIAL + ); + const accessToken: string = await this.createAccessToken( + user.email, + payloadAccessToken + ); - async getAccessTokenExpirationTime(): Promise { - return this.jwtAccessTokenExpirationTime; - } + const payloadRefreshToken: AuthJwtRefreshPayloadDto = + await this.createPayloadRefreshToken(payloadAccessToken); + const refreshToken: string = await this.createRefreshToken( + user.email, + payloadRefreshToken + ); - async getRefreshTokenExpirationTime(): Promise { - return this.jwtRefreshTokenExpirationTime; + return { + tokenType: this.jwtPrefixAuthorization, + roleType, + expiresIn: this.jwtAccessTokenExpirationTime, + accessToken, + refreshToken, + }; } - async getIssuer(): Promise { - return this.jwtIssuer; - } + async refreshToken( + user: IUserDoc, + refreshTokenFromRequest: string + ): Promise { + const roleType = user.role.type; + + const payloadRefreshToken = + this.helperEncryptionService.jwtDecrypt( + refreshTokenFromRequest + ); + const payloadAccessToken: AuthJwtAccessPayloadDto = + await this.createPayloadAccessToken( + user, + payloadRefreshToken.session, + payloadRefreshToken.loginDate, + payloadRefreshToken.loginFrom + ); + const accessToken: string = await this.createAccessToken( + user.email, + payloadAccessToken + ); - async getAudience(): Promise { - return this.jwtAudience; + return { + tokenType: this.jwtPrefixAuthorization, + roleType, + expiresIn: this.jwtAccessTokenExpirationTime, + accessToken, + refreshToken: refreshTokenFromRequest, + }; } async getPasswordAttempt(): Promise { diff --git a/src/modules/aws/dtos/aws.s3-multipart.dto.ts b/src/modules/aws/dtos/aws.s3-multipart.dto.ts index 12258dfaf..b3bbdf1bd 100644 --- a/src/modules/aws/dtos/aws.s3-multipart.dto.ts +++ b/src/modules/aws/dtos/aws.s3-multipart.dto.ts @@ -10,7 +10,6 @@ export class AwsS3MultipartPartDto { example: faker.string.alpha({ length: 10, casing: 'upper' }), description: 'ETag from aws after init multipart', }) - @Type(() => String) eTag: string; @ApiProperty({ @@ -18,7 +17,6 @@ export class AwsS3MultipartPartDto { nullable: false, example: 1, }) - @Type(() => Number) partNumber: number; @ApiProperty({ @@ -26,7 +24,6 @@ export class AwsS3MultipartPartDto { nullable: false, example: 1, }) - @Type(() => Number) size: number; } @@ -37,7 +34,6 @@ export class AwsS3MultipartDto extends AwsS3Dto { example: faker.string.alpha({ length: 20, casing: 'upper' }), description: 'Upload id from aws after init multipart', }) - @Type(() => String) uploadId: string; @ApiProperty({ @@ -46,7 +42,6 @@ export class AwsS3MultipartDto extends AwsS3Dto { example: 1, description: 'Last part number uploaded', }) - @Type(() => Number) lastPartNumber: number; @ApiProperty({ @@ -55,9 +50,16 @@ export class AwsS3MultipartDto extends AwsS3Dto { example: 200, description: 'Max part number, or length of the chunk', }) - @Type(() => Number) maxPartNumber: number; + @ApiProperty({ + required: true, + nullable: false, + example: 1, + description: 'Size uploaded', + }) + exactSize: number; + @ApiProperty({ required: true, nullable: false, diff --git a/src/modules/aws/dtos/aws.s3-presign-url.dto.ts b/src/modules/aws/dtos/aws.s3-presign-url.dto.ts deleted file mode 100644 index 75faccbc7..000000000 --- a/src/modules/aws/dtos/aws.s3-presign-url.dto.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { AwsS3Dto } from 'src/modules/aws/dtos/aws.s3.dto'; - -export class AwsS3PresignUrlDto extends AwsS3Dto { - @ApiProperty({ - required: true, - nullable: false, - example: 10000, - description: 'Expired in millisecond for each presign url', - }) - expiredIn: number; -} diff --git a/src/modules/aws/dtos/aws.s3.dto.ts b/src/modules/aws/dtos/aws.s3.dto.ts index e31dda9f5..5bb0098de 100644 --- a/src/modules/aws/dtos/aws.s3.dto.ts +++ b/src/modules/aws/dtos/aws.s3.dto.ts @@ -1,21 +1,24 @@ import { faker } from '@faker-js/faker'; import { ApiProperty } from '@nestjs/swagger'; -import { Type } from 'class-transformer'; export class AwsS3Dto { @ApiProperty({ required: true, nullable: false, }) - @Type(() => String) bucket: string; + @ApiProperty({ + required: true, + nullable: false, + }) + key: string; + @ApiProperty({ required: true, nullable: false, example: faker.system.directoryPath(), }) - @Type(() => String) path: string; @ApiProperty({ @@ -23,7 +26,6 @@ export class AwsS3Dto { nullable: false, example: faker.system.filePath(), }) - @Type(() => String) pathWithFilename: string; @ApiProperty({ @@ -31,7 +33,6 @@ export class AwsS3Dto { nullable: false, example: faker.system.fileName(), }) - @Type(() => String) filename: string; @ApiProperty({ @@ -39,15 +40,19 @@ export class AwsS3Dto { nullable: false, example: `${faker.internet.url()}/${faker.system.filePath()}`, }) - @Type(() => String) completedUrl: string; + @ApiProperty({ + required: false, + example: `${faker.internet.url()}/${faker.system.filePath()}`, + }) + cdnUrl?: string; + @ApiProperty({ required: true, nullable: false, example: faker.internet.url(), }) - @Type(() => String) baseUrl: string; @ApiProperty({ @@ -55,7 +60,6 @@ export class AwsS3Dto { nullable: false, example: faker.system.mimeType(), }) - @Type(() => String) mime: string; @ApiProperty({ diff --git a/src/modules/aws/dtos/aws.ses.dto.ts b/src/modules/aws/dtos/aws.ses.dto.ts index 897f5d41c..f1a73f561 100644 --- a/src/modules/aws/dtos/aws.ses.dto.ts +++ b/src/modules/aws/dtos/aws.ses.dto.ts @@ -1,4 +1,9 @@ -import { ApiProperty, OmitType, PickType } from '@nestjs/swagger'; +import { + ApiProperty, + getSchemaPath, + OmitType, + PickType, +} from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { ArrayNotEmpty, @@ -133,6 +138,7 @@ export class AwsSESSendBulkDto extends OmitType(AwsSESSendDto, [ required: true, isArray: true, type: AwsSESSendBulkRecipientsDto, + oneOf: [{ $ref: getSchemaPath(AwsSESSendBulkRecipientsDto) }], }) @IsNotEmpty() @IsArray() diff --git a/src/modules/aws/dtos/request/aws.s3-presign.request.dto.ts b/src/modules/aws/dtos/request/aws.s3-presign.request.dto.ts new file mode 100644 index 000000000..cd184c721 --- /dev/null +++ b/src/modules/aws/dtos/request/aws.s3-presign.request.dto.ts @@ -0,0 +1,51 @@ +import { faker } from '@faker-js/faker'; +import { ApiProperty } from '@nestjs/swagger'; +import { + IsInt, + IsNotEmpty, + IsNumber, + IsOptional, + IsString, +} from 'class-validator'; + +export class AwsS3PresignRequestDto { + @ApiProperty({ + required: true, + nullable: false, + example: faker.system.filePath(), + description: 'key of aws s3', + }) + @IsString() + @IsNotEmpty() + key: string; + + @ApiProperty({ + required: true, + nullable: false, + example: 1000, + description: 'Unit in milliseconds', + }) + @IsInt() + @IsNumber({ + allowInfinity: false, + allowNaN: false, + maxDecimalPlaces: 0, + }) + @IsOptional() + duration?: number; + + @ApiProperty({ + required: true, + nullable: false, + example: 10000, + description: 'Unit in bytes', + }) + @IsInt() + @IsNumber({ + allowInfinity: false, + allowNaN: false, + maxDecimalPlaces: 0, + }) + @IsNotEmpty() + size: number; +} diff --git a/src/modules/aws/dtos/response/aws.s3-presign.response.dto.ts b/src/modules/aws/dtos/response/aws.s3-presign.response.dto.ts new file mode 100644 index 000000000..3bb2d2367 --- /dev/null +++ b/src/modules/aws/dtos/response/aws.s3-presign.response.dto.ts @@ -0,0 +1,24 @@ +import { faker } from '@faker-js/faker'; +import { ApiProperty } from '@nestjs/swagger'; + +export class AwsS3PresignResponseDto { + @ApiProperty({ + required: true, + example: faker.internet.url(), + }) + presignUrl: string; + + @ApiProperty({ + required: true, + example: faker.system.filePath(), + }) + key: string; + + @ApiProperty({ + required: true, + nullable: false, + example: 10000, + description: 'Expired in millisecond for each presign url', + }) + expiredIn: number; +} diff --git a/src/modules/aws/dtos/response/aws.s3-response.dto.ts b/src/modules/aws/dtos/response/aws.s3-response.dto.ts new file mode 100644 index 000000000..2621e3f23 --- /dev/null +++ b/src/modules/aws/dtos/response/aws.s3-response.dto.ts @@ -0,0 +1,31 @@ +import { ApiHideProperty, OmitType } from '@nestjs/swagger'; +import { Exclude } from 'class-transformer'; +import { AwsS3Dto } from 'src/modules/aws/dtos/aws.s3.dto'; + +export class AwsS3ResponseDto extends OmitType(AwsS3Dto, [ + 'bucket', + 'baseUrl', + 'filename', + 'mime', + 'path', +]) { + @Exclude() + @ApiHideProperty() + bucket: string; + + @Exclude() + @ApiHideProperty() + path: string; + + @Exclude() + @ApiHideProperty() + filename: string; + + @Exclude() + @ApiHideProperty() + baseUrl: string; + + @Exclude() + @ApiHideProperty() + mime: string; +} diff --git a/src/modules/aws/enums/aws.enum.ts b/src/modules/aws/enums/aws.enum.ts new file mode 100644 index 000000000..1e94a6144 --- /dev/null +++ b/src/modules/aws/enums/aws.enum.ts @@ -0,0 +1,4 @@ +export enum ENUM_AWS_S3_ACCESSIBILITY { + PUBLIC = 'PUBLIC', + PRIVATE = 'PRIVATE', +} diff --git a/src/modules/aws/interfaces/aws.interface.ts b/src/modules/aws/interfaces/aws.interface.ts index 3daf500a1..7ac87c24a 100644 --- a/src/modules/aws/interfaces/aws.interface.ts +++ b/src/modules/aws/interfaces/aws.interface.ts @@ -1,26 +1,46 @@ -import { ObjectCannedACL } from '@aws-sdk/client-s3'; +import { ObjectCannedACL, S3Client } from '@aws-sdk/client-s3'; +import { ENUM_AWS_S3_ACCESSIBILITY } from 'src/modules/aws/enums/aws.enum'; -export interface IAwsS3PutItemOptions { - path?: string; - customFilename?: string; +export interface IAwsS3Options { + access?: ENUM_AWS_S3_ACCESSIBILITY; } -export interface IAwsS3PutItemWithAclOptions extends IAwsS3PutItemOptions { +export interface IAwsS3GetItemsOptions extends IAwsS3Options { + continuationToken?: string; +} + +export type IAwsS3DeleteDirOptions = IAwsS3GetItemsOptions; + +export interface IAwsS3PutItemWithAclOptions extends IAwsS3Options { acl?: ObjectCannedACL; } -export interface IAwsS3PutPresignUrlOptions { - path?: string; +export interface IAwsS3PresignOptions extends IAwsS3Options { + allowedSize?: number; } export interface IAwsS3PutItem { - buffer: string | Uint8Array | Buffer; - originalname: string; + file: Buffer; + key: string; size: number; + duration?: number; } -export interface IAwsS3PutPresignUrlFile { - filename: string; - size: number; - duration?: number; +export interface IAwsS3ConfigCredential { + key: string; + secret: string; +} + +export interface IAwsS3ConfigBucket { + credential: IAwsS3ConfigCredential; + bucket: string; + region: string; + baseUrl: string; + cdnUrl?: string; + client?: S3Client; +} + +export interface IAwsS3Config { + public: IAwsS3ConfigBucket; + private: IAwsS3ConfigBucket; } diff --git a/src/modules/aws/interfaces/aws.s3-service.interface.ts b/src/modules/aws/interfaces/aws.s3-service.interface.ts index 95f7823af..d23fbca3c 100644 --- a/src/modules/aws/interfaces/aws.s3-service.interface.ts +++ b/src/modules/aws/interfaces/aws.s3-service.interface.ts @@ -1,60 +1,78 @@ -import { HeadBucketCommandOutput, UploadPartRequest } from '@aws-sdk/client-s3'; +import { _Object } from '@aws-sdk/client-s3'; import { AwsS3MultipartDto, AwsS3MultipartPartDto, } from 'src/modules/aws/dtos/aws.s3-multipart.dto'; -import { AwsS3PresignUrlDto } from 'src/modules/aws/dtos/aws.s3-presign-url.dto'; import { AwsS3Dto } from 'src/modules/aws/dtos/aws.s3.dto'; +import { AwsS3PresignRequestDto } from 'src/modules/aws/dtos/request/aws.s3-presign.request.dto'; +import { AwsS3PresignResponseDto } from 'src/modules/aws/dtos/response/aws.s3-presign.response.dto'; +import { AwsS3ResponseDto } from 'src/modules/aws/dtos/response/aws.s3-response.dto'; import { + IAwsS3DeleteDirOptions, + IAwsS3GetItemsOptions, + IAwsS3Options, + IAwsS3PresignOptions, IAwsS3PutItem, - IAwsS3PutItemOptions, IAwsS3PutItemWithAclOptions, - IAwsS3PutPresignUrlFile, - IAwsS3PutPresignUrlOptions, } from 'src/modules/aws/interfaces/aws.interface'; -import { Readable } from 'stream'; export interface IAwsS3Service { - checkBucketExistence(): Promise; - listBucket(): Promise; - listItemInBucket(prefix?: string): Promise; - getItemInBucket( - pathWithFilename: string - ): Promise | Blob>; - putItemInBucket( - file: IAwsS3PutItem, - options?: IAwsS3PutItemOptions - ): Promise; - putItemInBucketWithAcl( + onModuleInit(): void; + checkBucket(options?: IAwsS3Options): Promise; + checkItem(key: string, options?: IAwsS3Options): Promise; + getItems( + path: string, + options?: IAwsS3GetItemsOptions + ): Promise; + getItem(key: string, options?: IAwsS3Options): Promise; + putItem(file: IAwsS3PutItem, options?: IAwsS3Options): Promise; + putItemWithAcl( file: IAwsS3PutItem, options?: IAwsS3PutItemWithAclOptions ): Promise; - deleteItemInBucket(pathWithFilename: string): Promise; - deleteItemsInBucket(pathWithFilename: string[]): Promise; - deleteFolder(dir: string): Promise; + deleteItem(key: string, options?: IAwsS3Options): Promise; + deleteItems(keys: string[], options?: IAwsS3Options): Promise; + deleteDir( + path: string, + options?: IAwsS3DeleteDirOptions + ): Promise; createMultiPart( file: IAwsS3PutItem, maxPartNumber: number, - options?: IAwsS3PutItemOptions + options?: IAwsS3Options ): Promise; createMultiPartWithAcl( file: IAwsS3PutItem, maxPartNumber: number, options?: IAwsS3PutItemWithAclOptions ): Promise; - uploadPart( + putItemMultiPart( multipart: AwsS3MultipartDto, partNumber: number, - content: UploadPartRequest['Body'] | string | Uint8Array | Buffer - ): Promise; + file: Buffer, + options?: IAwsS3Options + ): Promise; updateMultiPart( - { size, parts, ...others }: AwsS3MultipartDto, + { exactSize, parts, ...others }: AwsS3MultipartDto, part: AwsS3MultipartPartDto - ): Promise; - completeMultipart(multipart: AwsS3MultipartDto): Promise; - abortMultipart(multipart: AwsS3MultipartDto): Promise; - setPresignUrl( - { filename, size, duration }: IAwsS3PutPresignUrlFile, - options?: IAwsS3PutPresignUrlOptions - ): Promise; + ): AwsS3MultipartDto; + completeMultipart( + multipart: AwsS3MultipartDto, + options?: IAwsS3Options + ): Promise; + abortMultipart( + multipart: AwsS3MultipartDto, + options?: IAwsS3Options + ): Promise; + presign( + key: string, + options?: IAwsS3PresignOptions + ): Promise; + mapPresign( + { key, size, duration }: AwsS3PresignRequestDto, + options?: IAwsS3Options + ): AwsS3Dto; + getBucket(options?: IAwsS3Options): string; + getRegion(options?: IAwsS3Options): string; + mapResponse(dto: AwsS3Dto): AwsS3ResponseDto; } diff --git a/src/modules/aws/repository/entities/aws.s3-multipart.entity.ts b/src/modules/aws/repository/entities/aws.s3-multipart.entity.ts index dc096c27f..e6b4d56a3 100644 --- a/src/modules/aws/repository/entities/aws.s3-multipart.entity.ts +++ b/src/modules/aws/repository/entities/aws.s3-multipart.entity.ts @@ -31,7 +31,6 @@ export class AwsS3MultipartEntity { @DatabaseProp({ required: true, nullable: false, - default: [], schema: AwsS3MultipartPartSchema, }) parts: AwsS3MultipartPartEntity[]; diff --git a/src/modules/aws/services/aws.s3.service.ts b/src/modules/aws/services/aws.s3.service.ts index 8900153f8..775ef417f 100644 --- a/src/modules/aws/services/aws.s3.service.ts +++ b/src/modules/aws/services/aws.s3.service.ts @@ -1,10 +1,8 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, OnModuleInit } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; -import { Readable } from 'stream'; import { S3Client, GetObjectCommand, - ListBucketsCommand, ListObjectsV2Command, PutObjectCommand, DeleteObjectCommand, @@ -19,9 +17,6 @@ import { GetObjectCommandInput, AbortMultipartUploadCommand, AbortMultipartUploadCommandInput, - HeadBucketCommand, - HeadBucketCommandOutput, - ListBucketsOutput, ListObjectsV2Output, GetObjectOutput, DeleteObjectsCommandInput, @@ -30,9 +25,6 @@ import { DeleteObjectsCommandOutput, DeleteObjectCommandInput, DeleteObjectCommandOutput, - HeadBucketCommandInput, - ListBucketsCommandInput, - ListBucketsCommandOutput, GetObjectCommandOutput, PutObjectCommandInput, PutObjectCommandOutput, @@ -40,19 +32,26 @@ import { UploadPartCommandOutput, CompleteMultipartUploadCommandOutput, AbortMultipartUploadCommandOutput, - Bucket, _Object, ObjectCannedACL, - CompletedPart, + HeadObjectCommand, + HeadObjectCommandInput, + HeadObjectCommandOutput, + NoSuchKey, + HeadBucketCommandOutput, + HeadBucketCommand, + HeadBucketCommandInput, } from '@aws-sdk/client-s3'; import { IAwsS3Service } from 'src/modules/aws/interfaces/aws.s3-service.interface'; import { AwsS3Dto } from 'src/modules/aws/dtos/aws.s3.dto'; import { + IAwsS3Config, + IAwsS3DeleteDirOptions, + IAwsS3GetItemsOptions, + IAwsS3Options, + IAwsS3PresignOptions, IAwsS3PutItem, - IAwsS3PutItemOptions, IAwsS3PutItemWithAclOptions, - IAwsS3PutPresignUrlFile, - IAwsS3PutPresignUrlOptions, } from 'src/modules/aws/interfaces/aws.interface'; import { AwsS3MultipartDto, @@ -60,353 +59,500 @@ import { } from 'src/modules/aws/dtos/aws.s3-multipart.dto'; import { AWS_S3_MAX_PART_NUMBER } from 'src/modules/aws/constants/aws.constant'; import { getSignedUrl } from '@aws-sdk/s3-request-presigner'; -import { AwsS3PresignUrlDto } from 'src/modules/aws/dtos/aws.s3-presign-url.dto'; +import { ENUM_AWS_S3_ACCESSIBILITY } from 'src/modules/aws/enums/aws.enum'; +import { HelperArrayService } from 'src/common/helper/services/helper.array.service'; +import { FILE_SIZE_IN_BYTES } from 'src/common/file/constants/file.constant'; +import { AwsS3PresignResponseDto } from 'src/modules/aws/dtos/response/aws.s3-presign.response.dto'; +import { AwsS3PresignRequestDto } from 'src/modules/aws/dtos/request/aws.s3-presign.request.dto'; +import { AwsS3ResponseDto } from 'src/modules/aws/dtos/response/aws.s3-response.dto'; +import { plainToInstance } from 'class-transformer'; @Injectable() -export class AwsS3Service implements IAwsS3Service { - private readonly s3Client: S3Client; - private readonly bucket: string; - private readonly baseUrl: string; - private readonly presignUrlExpired: number; - - constructor(private readonly configService: ConfigService) { - this.s3Client = new S3Client({ +export class AwsS3Service implements OnModuleInit, IAwsS3Service { + private readonly presignExpired: number; + private config: IAwsS3Config; + + constructor( + private readonly configService: ConfigService, + private readonly helperArrayService: HelperArrayService + ) { + this.presignExpired = this.configService.get( + 'aws.s3.presignExpired' + ); + this.config = this.configService.get('aws.s3.config'); + } + + onModuleInit(): void { + this.config.public.client = new S3Client({ credentials: { - accessKeyId: this.configService.get( - 'aws.s3.credential.key' - ), - secretAccessKey: this.configService.get( - 'aws.s3.credential.secret' - ), + accessKeyId: this.config.public.credential.key, + secretAccessKey: this.config.public.credential.secret, }, - region: this.configService.get('aws.s3.region'), + region: this.config.public.region, }); - this.bucket = this.configService.get('aws.s3.bucket'); - this.baseUrl = this.configService.get('aws.s3.baseUrl'); - this.presignUrlExpired = this.configService.get( - 'aws.s3.presignUrlExpired' - ); + this.config.private.client = new S3Client({ + credentials: { + accessKeyId: this.config.private.credential.key, + secretAccessKey: this.config.private.credential.secret, + }, + region: this.config.private.region, + }); } - async checkBucketExistence(): Promise { + async checkBucket(options?: IAwsS3Options): Promise { + const config = + options?.access === ENUM_AWS_S3_ACCESSIBILITY.PRIVATE + ? this.config.private + : this.config.public; + const command: HeadBucketCommand = new HeadBucketCommand({ - Bucket: this.bucket, + Bucket: config.bucket, }); - try { - const check = await this.s3Client.send< - HeadBucketCommandInput, - HeadBucketCommandOutput - >(command); - return check; - } catch (err: any) { - throw err; - } - } + await config.client.send< + HeadBucketCommandInput, + HeadBucketCommandOutput + >(command); - async listBucket(): Promise { - const command: ListBucketsCommand = new ListBucketsCommand({}); + return true; + } - try { - const listBucket: ListBucketsOutput = await this.s3Client.send< - ListBucketsCommandInput, - ListBucketsCommandOutput - >(command); - const mapList: string[] = listBucket.Buckets.map( - (val: Bucket) => val.Name - ); - return mapList; - } catch (err: any) { - throw err; + async checkItem(key: string, options?: IAwsS3Options): Promise { + if (key.startsWith('/')) { + throw new Error('Key should not start with "/"'); } + + const config = + options?.access === ENUM_AWS_S3_ACCESSIBILITY.PRIVATE + ? this.config.private + : this.config.public; + + const headCommand = new HeadObjectCommand({ + Bucket: config.bucket, + Key: key, + }); + + const item = await config.client.send< + HeadObjectCommandInput, + HeadObjectCommandOutput + >(headCommand); + + const path: string = `/${key.substring(0, key.lastIndexOf('/'))}`; + const pathWithFilename: string = `/${key}`; + const filename: string = key.substring( + key.lastIndexOf('/') + 1, + key.length + ); + const mime: string = filename.substring( + filename.lastIndexOf('.') + 1, + filename.length + ); + + return { + bucket: config.bucket, + key, + path, + pathWithFilename, + filename: filename, + completedUrl: `${config.baseUrl}${pathWithFilename}`, + cdnUrl: config.cdnUrl + ? `${config.cdnUrl}${pathWithFilename}` + : undefined, + baseUrl: config.baseUrl, + mime, + size: item.ContentLength, + }; } - async listItemInBucket(path?: string): Promise { + async getItems( + path: string, + options?: IAwsS3GetItemsOptions + ): Promise { + if (path.startsWith('/')) { + throw new Error('Path should not start with "/"'); + } + + const config = + options?.access === ENUM_AWS_S3_ACCESSIBILITY.PRIVATE + ? this.config.private + : this.config.public; + const command: ListObjectsV2Command = new ListObjectsV2Command({ - Bucket: this.bucket, + Bucket: config.bucket, Prefix: path, + MaxKeys: 1000, + ContinuationToken: options?.continuationToken, }); - try { - const listItems: ListObjectsV2Output = await this.s3Client.send< - ListObjectsV2CommandInput, - ListObjectsV2CommandOutput - >(command); - - const mapList = listItems.Contents.map((val: _Object) => { - const lastIndex: number = val.Key.lastIndexOf('/'); - const path: string = val.Key.substring(0, lastIndex); - const filename: string = val.Key.substring( - lastIndex + 1, - val.Key.length - ); - const mime: string = filename.substring( - filename.lastIndexOf('.') + 1, - filename.length - ); - - return { - bucket: this.bucket, - path, - pathWithFilename: val.Key, - filename: filename, - completedUrl: `${this.baseUrl}${val.Key}`, - baseUrl: this.baseUrl, - mime, - size: val.Size, - }; + const listItems: ListObjectsV2Output = await config.client.send< + ListObjectsV2CommandInput, + ListObjectsV2CommandOutput + >(command); + + const mapList: AwsS3Dto[] = listItems.Contents.map((item: _Object) => { + const path: string = `/${item.Key.substring(0, item.Key.lastIndexOf('/'))}`; + const pathWithFilename: string = `/${item.Key}`; + const filename: string = item.Key.substring( + item.Key.lastIndexOf('/') + 1, + item.Key.length + ); + const mime: string = filename.substring( + filename.lastIndexOf('.') + 1, + filename.length + ); + + return { + bucket: config.bucket, + key: item.Key, + path, + pathWithFilename: pathWithFilename, + filename: filename, + completedUrl: `${config.baseUrl}${pathWithFilename}`, + cdnUrl: config.cdnUrl + ? `${config.cdnUrl}${pathWithFilename}` + : undefined, + baseUrl: config.baseUrl, + mime, + size: item.Size, + }; + }); + + if (listItems.IsTruncated) { + const nextItems: AwsS3Dto[] = await this.getItems(path, { + ...options, + continuationToken: listItems.ContinuationToken, + }); + + mapList.push(...nextItems); + } + + for (const dir of listItems.CommonPrefixes) { + const dirItems = await this.getItems(dir.Prefix, { + access: options?.access, }); - return mapList; - } catch (err: any) { - throw err; + mapList.push(...dirItems); } + + return mapList; } - async getItemInBucket( - pathWithFilename: string - ): Promise | Blob> { + async getItem(key: string, options?: IAwsS3Options): Promise { + if (key.startsWith('/')) { + throw new Error('Key should not start with "/"'); + } + + const config = + options?.access === ENUM_AWS_S3_ACCESSIBILITY.PRIVATE + ? this.config.private + : this.config.public; + const command: GetObjectCommand = new GetObjectCommand({ - Bucket: this.bucket, - Key: pathWithFilename, + Bucket: config.bucket, + Key: key, }); - try { - const item: GetObjectOutput = await this.s3Client.send< - GetObjectCommandInput, - GetObjectCommandOutput - >(command); - return item.Body; - } catch (err: any) { - throw err; - } + const item: GetObjectOutput = await config.client.send< + GetObjectCommandInput, + GetObjectCommandOutput + >(command); + + const path: string = `/${key.substring(0, key.lastIndexOf('/'))}`; + const pathWithFilename: string = `/${key}`; + const filename: string = key.substring( + key.lastIndexOf('/') + 1, + key.length + ); + const mime: string = filename.substring( + filename.lastIndexOf('.') + 1, + filename.length + ); + + return { + bucket: config.bucket, + key, + path, + pathWithFilename, + filename: filename, + completedUrl: `${config.baseUrl}${pathWithFilename}`, + cdnUrl: config.cdnUrl + ? `${config.cdnUrl}${pathWithFilename}` + : undefined, + baseUrl: config.baseUrl, + mime, + size: item.ContentLength, + }; } - async putItemInBucket( + async putItem( file: IAwsS3PutItem, - options?: IAwsS3PutItemOptions + options?: IAwsS3Options ): Promise { - const path: string = `/${options?.path?.replace(/^\/*|\/*$/g, '') ?? ''}`; - const mime: string = file.originalname.substring( - file.originalname.lastIndexOf('.') + 1, - file.originalname.length + if (file.key.startsWith('/')) { + throw new Error('Key should not start with "/"'); + } + + const config = + options?.access === ENUM_AWS_S3_ACCESSIBILITY.PRIVATE + ? this.config.private + : this.config.public; + + const path: string = `/${file.key.substring(0, file.key.lastIndexOf('/'))}`; + const pathWithFilename: string = `/${file.key}`; + const filename: string = file.key.substring( + file.key.lastIndexOf('/') + 1, + file.key.length ); - const filename = options?.customFilename - ? `${options?.customFilename.replace(/^\/*|\/*$/g, '')}.${mime}` - : file.originalname.replace(/^\/*|\/*$/g, ''); - const content: string | Uint8Array | Buffer = file.buffer; + const mime: string = filename.substring( + filename.lastIndexOf('.') + 1, + filename.length + ); + + const content: Buffer = file.file; const key: string = path === '/' ? `${path}${filename}` : `${path}/${filename}`; const command: PutObjectCommand = new PutObjectCommand({ - Bucket: this.bucket, + Bucket: config.bucket, Key: key, Body: content, }); - try { - await this.s3Client.send< - PutObjectCommandInput, - PutObjectCommandOutput - >(command); + await config.client.send( + command + ); - return { - bucket: this.bucket, - path, - pathWithFilename: key, - filename: filename, - completedUrl: `${this.baseUrl}${key}`, - baseUrl: this.baseUrl, - mime, - size: file.size, - }; - } catch (err: any) { - throw err; - } + return { + bucket: config.bucket, + key, + path, + pathWithFilename, + filename: filename, + completedUrl: `${config.baseUrl}${pathWithFilename}`, + cdnUrl: config.cdnUrl + ? `${config.cdnUrl}${pathWithFilename}` + : undefined, + baseUrl: config.baseUrl, + mime, + size: file.size, + duration: file.duration, + }; } - async putItemInBucketWithAcl( + async putItemWithAcl( file: IAwsS3PutItem, options?: IAwsS3PutItemWithAclOptions ): Promise { - const path: string = `/${options?.path?.replace(/^\/*|\/*$/g, '') ?? ''}`; - const acl: ObjectCannedACL = options?.acl - ? (options.acl as ObjectCannedACL) - : ObjectCannedACL.public_read; - - const mime: string = file.originalname.substring( - file.originalname.lastIndexOf('.') + 1, - file.originalname.length + if (file.key.startsWith('/')) { + throw new Error('Key should not start with "/"'); + } + + const config = + options?.access === ENUM_AWS_S3_ACCESSIBILITY.PRIVATE + ? this.config.private + : this.config.public; + + const path: string = `/${file.key.substring(0, file.key.lastIndexOf('/'))}`; + const pathWithFilename: string = `/${file.key}`; + const filename: string = file.key.substring( + file.key.lastIndexOf('/') + 1, + file.key.length + ); + const mime: string = filename.substring( + filename.lastIndexOf('.') + 1, + filename.length ); - const filename = options?.customFilename - ? `${options?.customFilename.replace(/^\/*|\/*$/g, '')}.${mime}` - : file.originalname.replace(/^\/*|\/*$/g, ''); - const content: string | Uint8Array | Buffer = file.buffer; - const key: string = - path === '/' ? `${path}${filename}` : `${path}/${filename}`; + const content: Buffer = file.file; const command: PutObjectCommand = new PutObjectCommand({ - Bucket: this.bucket, - Key: key, + Bucket: config.bucket, + Key: file.key, Body: content, - ACL: acl, + ACL: options?.acl ?? ObjectCannedACL.public_read, }); - try { - await this.s3Client.send< - PutObjectCommandInput, - PutObjectCommandOutput - >(command); + await config.client.send( + command + ); - return { - bucket: this.bucket, - path, - pathWithFilename: key, - filename: filename, - completedUrl: `${this.baseUrl}${key}`, - baseUrl: this.baseUrl, - mime, - size: file.size, - }; - } catch (err: any) { - throw err; - } + return { + bucket: config.bucket, + key: file.key, + path, + pathWithFilename, + filename: filename, + completedUrl: `${config.baseUrl}${pathWithFilename}`, + cdnUrl: config.cdnUrl + ? `${config.cdnUrl}${pathWithFilename}` + : undefined, + baseUrl: config.baseUrl, + mime, + size: file.size, + duration: file.duration, + }; } - async deleteItemInBucket(pathWithFilename: string): Promise { + async deleteItem(key: string, options?: IAwsS3Options): Promise { + if (key.startsWith('/')) { + throw new Error('Key should not start with "/"'); + } + + const config = + options?.access === ENUM_AWS_S3_ACCESSIBILITY.PRIVATE + ? this.config.private + : this.config.public; + const command: DeleteObjectCommand = new DeleteObjectCommand({ - Bucket: this.bucket, - Key: pathWithFilename, + Bucket: config.bucket, + Key: key, }); - try { - await this.s3Client.send< - DeleteObjectCommandInput, - DeleteObjectCommandOutput - >(command); - return; - } catch (err: any) { - throw err; - } + await config.client.send< + DeleteObjectCommandInput, + DeleteObjectCommandOutput + >(command); + + return; } - async deleteItemsInBucket(pathWithFilename: string[]): Promise { - const keys: ObjectIdentifier[] = pathWithFilename.map( - (val: string) => ({ - Key: val, - }) - ); + async deleteItems(keys: string[], options?: IAwsS3Options): Promise { + if (keys.some(e => e.startsWith('/'))) { + throw new Error('Keys should not start with "/"'); + } + + const config = + options?.access === ENUM_AWS_S3_ACCESSIBILITY.PRIVATE + ? this.config.private + : this.config.public; + + const obj: ObjectIdentifier[] = keys.map((val: string) => ({ + Key: val, + })); const command: DeleteObjectsCommand = new DeleteObjectsCommand({ - Bucket: this.bucket, + Bucket: config.bucket, Delete: { - Objects: keys, + Objects: obj, }, }); - try { - await this.s3Client.send< - DeleteObjectsCommandInput, - DeleteObjectsCommandOutput - >(command); - return; - } catch (err: any) { - throw err; - } + await config.client.send< + DeleteObjectsCommandInput, + DeleteObjectsCommandOutput + >(command); + + return; } - async deleteFolder(dir: string): Promise { - const commandList: ListObjectsV2Command = new ListObjectsV2Command({ - Bucket: this.bucket, - Prefix: dir, - }); - const lists = await this.s3Client.send< - ListObjectsV2CommandInput, - ListObjectsV2CommandOutput - >(commandList); + async deleteDir( + path: string, + options?: IAwsS3DeleteDirOptions + ): Promise { + if (path.startsWith('/')) { + throw new Error('Path should not start with "/"'); + } - try { - const listItems = lists.Contents.map(val => ({ - Key: val.Key, - })); - const commandDeleteItems: DeleteObjectsCommand = - new DeleteObjectsCommand({ - Bucket: this.bucket, - Delete: { - Objects: listItems, - }, + const config = + options?.access === ENUM_AWS_S3_ACCESSIBILITY.PRIVATE + ? this.config.private + : this.config.public; + + const items: AwsS3Dto[] = await this.getItems(path, options); + + if (items.length > 0) { + const chunkItems = this.helperArrayService + .chunk(items, 1000) + .map((itms: AwsS3Dto[]) => { + const commandDeleteItems: DeleteObjectsCommand = + new DeleteObjectsCommand({ + Bucket: config.bucket, + Delete: { + Objects: itms.map(e => ({ + Key: e.pathWithFilename, + })), + }, + }); + + return config.client.send< + DeleteObjectsCommandInput, + DeleteObjectsCommandOutput + >(commandDeleteItems); }); - await this.s3Client.send< - DeleteObjectsCommandInput, - DeleteObjectsCommandOutput - >(commandDeleteItems); - - const commandDelete: DeleteObjectCommand = new DeleteObjectCommand({ - Bucket: this.bucket, - Key: dir, - }); - await this.s3Client.send< - DeleteObjectCommandInput, - DeleteObjectCommandOutput - >(commandDelete); - - return; - } catch (err: any) { - throw err; + await Promise.all(chunkItems); } + + const commandDelete: DeleteObjectCommand = new DeleteObjectCommand({ + Bucket: config.bucket, + Key: path, + }); + await config.client.send< + DeleteObjectCommandInput, + DeleteObjectCommandOutput + >(commandDelete); + + return; } async createMultiPart( file: IAwsS3PutItem, maxPartNumber: number, - options?: IAwsS3PutItemOptions + options?: IAwsS3Options ): Promise { - if (maxPartNumber > AWS_S3_MAX_PART_NUMBER) { + if (file.key.startsWith('/')) { + throw new Error('Key should not start with "/"'); + } else if (maxPartNumber > AWS_S3_MAX_PART_NUMBER) { throw new Error( `Max part number is greater than ${AWS_S3_MAX_PART_NUMBER}` ); } - const path: string = `/${options?.path?.replace(/^\/*|\/*$/g, '') ?? ''}`; - const mime: string = file.originalname.substring( - file.originalname.lastIndexOf('.') + 1, - file.originalname.length + const config = + options?.access === ENUM_AWS_S3_ACCESSIBILITY.PRIVATE + ? this.config.private + : this.config.public; + + const path: string = `/${file.key.substring(0, file.key.lastIndexOf('/'))}`; + const pathWithFilename: string = `/${file.key}`; + const filename: string = file.key.substring( + file.key.lastIndexOf('/') + 1, + file.key.length + ); + const mime: string = filename.substring( + filename.lastIndexOf('.') + 1, + filename.length ); - const filename = options?.customFilename - ? `${options?.customFilename.replace(/^\/*|\/*$/g, '')}.${mime}` - : file.originalname.replace(/^\/*|\/*$/g, ''); - const key: string = - path === '/' ? `${path}${filename}` : `${path}/${filename}`; const multiPartCommand: CreateMultipartUploadCommand = new CreateMultipartUploadCommand({ - Bucket: this.bucket, - Key: key, + Bucket: config.bucket, + Key: file.key, }); - try { - const response = await this.s3Client.send< - CreateMultipartUploadCommandInput, - CreateMultipartUploadCommandOutput - >(multiPartCommand); + const response = await config.client.send< + CreateMultipartUploadCommandInput, + CreateMultipartUploadCommandOutput + >(multiPartCommand); - return { - bucket: this.bucket, - uploadId: response.UploadId, - path, - pathWithFilename: key, - filename: filename, - completedUrl: `${this.baseUrl}${key}`, - baseUrl: this.baseUrl, - mime, - size: 0, - lastPartNumber: 0, - maxPartNumber: maxPartNumber, - parts: [], - }; - } catch (err: any) { - throw err; - } + return { + bucket: config.bucket, + uploadId: response.UploadId, + key: file.key, + path, + pathWithFilename, + filename: filename, + completedUrl: `${config.baseUrl}${pathWithFilename}`, + cdnUrl: config.cdnUrl + ? `${config.cdnUrl}${pathWithFilename}` + : undefined, + baseUrl: config.baseUrl, + mime, + duration: file.duration, + size: file.size, + lastPartNumber: 0, + exactSize: 0, + maxPartNumber: maxPartNumber, + parts: [], + }; } async createMultiPartWithAcl( @@ -414,174 +560,279 @@ export class AwsS3Service implements IAwsS3Service { maxPartNumber: number, options?: IAwsS3PutItemWithAclOptions ): Promise { - const path: string = `/${options?.path?.replace(/^\/*|\/*$/g, '') ?? ''}`; - const acl: ObjectCannedACL = options?.acl - ? (options.acl as ObjectCannedACL) - : ObjectCannedACL.public_read; - - const mime: string = file.originalname.substring( - file.originalname.lastIndexOf('.') + 1, - file.originalname.length + if (file.key.startsWith('/')) { + throw new Error('Key should not start with "/"'); + } else if (maxPartNumber > AWS_S3_MAX_PART_NUMBER) { + throw new Error( + `Max part number is greater than ${AWS_S3_MAX_PART_NUMBER}` + ); + } + + const config = + options?.access === ENUM_AWS_S3_ACCESSIBILITY.PRIVATE + ? this.config.private + : this.config.public; + + const path: string = `/${file.key.substring(0, file.key.lastIndexOf('/'))}`; + const pathWithFilename: string = `/${file.key}`; + const filename: string = file.key.substring( + file.key.lastIndexOf('/') + 1, + file.key.length + ); + const mime: string = filename.substring( + filename.lastIndexOf('.') + 1, + filename.length ); - const filename = options?.customFilename - ? `${options?.customFilename.replace(/^\/*|\/*$/g, '')}.${mime}` - : file.originalname.replace(/^\/*|\/*$/g, ''); - const key: string = - path === '/' ? `${path}${filename}` : `${path}/${filename}`; const multiPartCommand: CreateMultipartUploadCommand = new CreateMultipartUploadCommand({ - Bucket: this.bucket, - Key: key, - ACL: acl, + Bucket: config.bucket, + Key: file.key, + ACL: options?.acl ?? ObjectCannedACL.public_read, }); - try { - const response = await this.s3Client.send< - CreateMultipartUploadCommandInput, - CreateMultipartUploadCommandOutput - >(multiPartCommand); + const response = await config.client.send< + CreateMultipartUploadCommandInput, + CreateMultipartUploadCommandOutput + >(multiPartCommand); - return { - bucket: this.bucket, - uploadId: response.UploadId, - path, - pathWithFilename: key, - filename: filename, - completedUrl: `${this.baseUrl}${key}`, - baseUrl: this.baseUrl, - mime, - size: 0, - lastPartNumber: 0, - maxPartNumber: maxPartNumber, - parts: [], - }; - } catch (err: any) { - throw err; - } + return { + bucket: config.bucket, + uploadId: response.UploadId, + path, + key: file.key, + pathWithFilename, + filename: filename, + completedUrl: `${config.baseUrl}${pathWithFilename}`, + cdnUrl: config.cdnUrl + ? `${config.cdnUrl}${pathWithFilename}` + : undefined, + baseUrl: config.baseUrl, + mime, + size: file.size, + lastPartNumber: 0, + exactSize: 0, + maxPartNumber: maxPartNumber, + duration: file.duration, + parts: [], + }; } - async uploadPart( + async putItemMultiPart( multipart: AwsS3MultipartDto, partNumber: number, - content: string | Uint8Array | Buffer - ): Promise { + file: Buffer, + options?: IAwsS3Options + ): Promise { + const config = + options?.access === ENUM_AWS_S3_ACCESSIBILITY.PRIVATE + ? this.config.private + : this.config.public; + const uploadPartCommand: UploadPartCommand = new UploadPartCommand({ - Bucket: this.bucket, + Bucket: config.bucket, Key: multipart.path, - Body: content, + Body: file, PartNumber: partNumber, UploadId: multipart.uploadId, }); - try { - const { ETag } = await this.s3Client.send< - UploadPartCommandInput, - UploadPartCommandOutput - >(uploadPartCommand); + const { ETag } = await config.client.send< + UploadPartCommandInput, + UploadPartCommandOutput + >(uploadPartCommand); - return { - eTag: ETag, - partNumber: partNumber, - size: content.length, - }; - } catch (err: any) { - throw err; - } + const part: AwsS3MultipartPartDto = { + eTag: ETag, + partNumber: partNumber, + size: file.length, + }; + + return this.updateMultiPart(multipart, part); } - async updateMultiPart( - { size, parts, ...others }: AwsS3MultipartDto, + updateMultiPart( + { exactSize, parts, ...others }: AwsS3MultipartDto, part: AwsS3MultipartPartDto - ): Promise { + ): AwsS3MultipartDto { parts.push(part); + return { ...others, - size: size + part.size, + exactSize: exactSize + part.size, lastPartNumber: part.partNumber, parts, }; } - async completeMultipart(multipart: AwsS3MultipartDto): Promise { + async completeMultipart( + multipart: AwsS3MultipartDto, + options?: IAwsS3Options + ): Promise { + const config = + options?.access === ENUM_AWS_S3_ACCESSIBILITY.PRIVATE + ? this.config.private + : this.config.public; + const completeMultipartCommand: CompleteMultipartUploadCommand = new CompleteMultipartUploadCommand({ - Bucket: this.bucket, + Bucket: config.bucket, Key: multipart.path, UploadId: multipart.uploadId, MultipartUpload: { - Parts: multipart.parts as CompletedPart[], + Parts: multipart.parts.map(el => ({ + ETag: el.eTag, + PartNumber: el.partNumber, + })), }, }); - try { - await this.s3Client.send< - CompleteMultipartUploadCommandInput, - CompleteMultipartUploadCommandOutput - >(completeMultipartCommand); - - return; - } catch (err: any) { - throw err; - } + await config.client.send< + CompleteMultipartUploadCommandInput, + CompleteMultipartUploadCommandOutput + >(completeMultipartCommand); + + return; } - async abortMultipart(multipart: AwsS3MultipartDto): Promise { + async abortMultipart( + multipart: AwsS3MultipartDto, + options?: IAwsS3Options + ): Promise { + const config = + options?.access === ENUM_AWS_S3_ACCESSIBILITY.PRIVATE + ? this.config.private + : this.config.public; + const abortMultipartCommand: AbortMultipartUploadCommand = new AbortMultipartUploadCommand({ - Bucket: this.bucket, + Bucket: config.bucket, Key: multipart.path, UploadId: multipart.uploadId, }); - try { - await this.s3Client.send< - AbortMultipartUploadCommandInput, - AbortMultipartUploadCommandOutput - >(abortMultipartCommand); - - return; - } catch (err: any) { - throw err; - } + await config.client.send< + AbortMultipartUploadCommandInput, + AbortMultipartUploadCommandOutput + >(abortMultipartCommand); + + return; } - async setPresignUrl( - { filename, size, duration }: IAwsS3PutPresignUrlFile, - options?: IAwsS3PutPresignUrlOptions - ): Promise { + async presign( + key: string, + options?: IAwsS3PresignOptions + ): Promise { + if (key.startsWith('/')) { + throw new Error('Key should not start with "/"'); + } + + const config = + options?.access === ENUM_AWS_S3_ACCESSIBILITY.PRIVATE + ? this.config.private + : this.config.public; + + const headCommand = new HeadObjectCommand({ + Bucket: config.bucket, + Key: key, + }); + try { - const path: string = `/${options?.path?.replace(/^\/*|\/*$/g, '') ?? ''}`; - const key: string = - path === '/' ? `${path}${filename}` : `${path}/${filename}`; - const mime: string = filename.substring( - filename.lastIndexOf('.') + 1, - filename.length - ); + await config.client.send< + HeadObjectCommandInput, + HeadObjectCommandOutput + >(headCommand); + + throw new Error(`Key ${key} is already exist`); + } catch (error: unknown) { + if (!(error instanceof NoSuchKey)) { + throw error; + } + } - const command = new PutObjectCommand({ - Bucket: this.bucket, - Key: key, - ContentType: mime, - }); - const presignUrl = await getSignedUrl(this.s3Client, command, { - expiresIn: this.presignUrlExpired, - }); + const filename: string = key.substring( + key.lastIndexOf('/') + 1, + key.length + ); + const mime: string = filename.substring( + filename.lastIndexOf('.') + 1, + filename.length + ); - return { - bucket: this.bucket, - pathWithFilename: key, - path, - completedUrl: presignUrl, - expiredIn: this.presignUrlExpired, - size, - mime, - filename, - baseUrl: this.baseUrl, - duration, - }; - } catch (err) { - throw err; + const size = options?.allowedSize ?? FILE_SIZE_IN_BYTES; + const command = new PutObjectCommand({ + Bucket: config.bucket, + Key: key, + ContentType: mime, + ContentLength: size, + }); + const presignUrl = await getSignedUrl(config.client, command, { + expiresIn: this.presignExpired, + }); + + return { expiredIn: this.presignExpired, presignUrl: presignUrl, key }; + } + + mapPresign( + { key, size, duration }: AwsS3PresignRequestDto, + options?: IAwsS3Options + ): AwsS3Dto { + if (key.startsWith('/')) { + throw new Error('Key should not start with "/"'); } + + const config = + options?.access === ENUM_AWS_S3_ACCESSIBILITY.PRIVATE + ? this.config.private + : this.config.public; + + const path: string = `/${key.substring(0, key.lastIndexOf('/'))}`; + const pathWithFilename: string = `/${key}`; + const filename: string = key.substring( + key.lastIndexOf('/') + 1, + key.length + ); + const mime: string = filename.substring( + filename.lastIndexOf('.') + 1, + filename.length + ); + + return { + bucket: config.bucket, + path, + key, + pathWithFilename, + filename: filename, + completedUrl: `${config.baseUrl}${pathWithFilename}`, + cdnUrl: config.cdnUrl + ? `${config.cdnUrl}${pathWithFilename}` + : undefined, + baseUrl: config.baseUrl, + mime, + size, + duration, + }; + } + + getBucket(options?: IAwsS3Options): string { + const config = + options?.access === ENUM_AWS_S3_ACCESSIBILITY.PRIVATE + ? this.config.private + : this.config.public; + + return config.bucket; + } + + getRegion(options?: IAwsS3Options): string { + const config = + options?.access === ENUM_AWS_S3_ACCESSIBILITY.PRIVATE + ? this.config.private + : this.config.public; + + return config.region; + } + + mapResponse(dto: AwsS3Dto): AwsS3ResponseDto { + return plainToInstance(AwsS3ResponseDto, dto); } } diff --git a/src/modules/aws/services/aws.ses.service.ts b/src/modules/aws/services/aws.ses.service.ts index 2dbfcaaee..f8b56a65a 100644 --- a/src/modules/aws/services/aws.ses.service.ts +++ b/src/modules/aws/services/aws.ses.service.ts @@ -59,16 +59,12 @@ export class AwsSESService implements IAwsSESService { NextToken: nextToken, }); - try { - const listTemplate: ListTemplatesCommandOutput = - await this.sesClient.send< - ListTemplatesCommandInput, - ListTemplatesCommandOutput - >(command); - return listTemplate; - } catch (err: any) { - throw err; - } + const listTemplate: ListTemplatesCommandOutput = + await this.sesClient.send< + ListTemplatesCommandInput, + ListTemplatesCommandOutput + >(command); + return listTemplate; } async getTemplate({ @@ -78,17 +74,12 @@ export class AwsSESService implements IAwsSESService { TemplateName: name, }); - try { - const getTemplate: GetTemplateCommandOutput = - await this.sesClient.send< - GetTemplateCommandInput, - GetTemplateCommandOutput - >(command); + const getTemplate: GetTemplateCommandOutput = await this.sesClient.send< + GetTemplateCommandInput, + GetTemplateCommandOutput + >(command); - return getTemplate; - } catch (err: any) { - throw err; - } + return getTemplate; } async createTemplate({ @@ -110,17 +101,12 @@ export class AwsSESService implements IAwsSESService { }, }); - try { - const create: CreateTemplateCommandOutput = - await this.sesClient.send< - CreateTemplateCommandInput, - CreateTemplateCommandOutput - >(command); + const create: CreateTemplateCommandOutput = await this.sesClient.send< + CreateTemplateCommandInput, + CreateTemplateCommandOutput + >(command); - return create; - } catch (err: any) { - throw err; - } + return create; } async updateTemplate({ @@ -142,17 +128,12 @@ export class AwsSESService implements IAwsSESService { }, }); - try { - const update: UpdateTemplateCommandOutput = - await this.sesClient.send< - UpdateTemplateCommandInput, - UpdateTemplateCommandOutput - >(command); + const update: UpdateTemplateCommandOutput = await this.sesClient.send< + UpdateTemplateCommandInput, + UpdateTemplateCommandOutput + >(command); - return update; - } catch (err: any) { - throw err; - } + return update; } async deleteTemplate({ @@ -162,16 +143,12 @@ export class AwsSESService implements IAwsSESService { TemplateName: name, }); - try { - const del: DeleteTemplateCommandOutput = await this.sesClient.send< - DeleteTemplateCommandInput, - DeleteTemplateCommandOutput - >(command); + const del: DeleteTemplateCommandOutput = await this.sesClient.send< + DeleteTemplateCommandInput, + DeleteTemplateCommandOutput + >(command); - return del; - } catch (err: any) { - throw err; - } + return del; } async send({ @@ -196,17 +173,13 @@ export class AwsSESService implements IAwsSESService { ReplyToAddresses: [replyTo ?? sender], }); - try { - const sendWithTemplate: SendTemplatedEmailCommandOutput = - await this.sesClient.send< - SendTemplatedEmailCommandInput, - SendTemplatedEmailCommandOutput - >(command); + const sendWithTemplate: SendTemplatedEmailCommandOutput = + await this.sesClient.send< + SendTemplatedEmailCommandInput, + SendTemplatedEmailCommandOutput + >(command); - return sendWithTemplate; - } catch (err: any) { - throw err; - } + return sendWithTemplate; } async sendBulk({ @@ -219,6 +192,7 @@ export class AwsSESService implements IAwsSESService { }: AwsSESSendBulkDto): Promise { const command: SendBulkTemplatedEmailCommand = new SendBulkTemplatedEmailCommand({ + DefaultTemplateData: '', Template: templateName, Destinations: recipients.map(e => ({ Destination: { @@ -234,16 +208,12 @@ export class AwsSESService implements IAwsSESService { ReplyToAddresses: [replyTo ?? sender], }); - try { - const sendWithTemplate: SendBulkTemplatedEmailCommandOutput = - await this.sesClient.send< - SendBulkTemplatedEmailCommandInput, - SendBulkTemplatedEmailCommandOutput - >(command); + const sendWithTemplate: SendBulkTemplatedEmailCommandOutput = + await this.sesClient.send< + SendBulkTemplatedEmailCommandInput, + SendBulkTemplatedEmailCommandOutput + >(command); - return sendWithTemplate; - } catch (err: any) { - throw err; - } + return sendWithTemplate; } } diff --git a/src/modules/country/constants/country.doc.constant.ts b/src/modules/country/constants/country.doc.constant.ts deleted file mode 100644 index 436f6290b..000000000 --- a/src/modules/country/constants/country.doc.constant.ts +++ /dev/null @@ -1,9 +0,0 @@ -export const CountryDocParamsId = [ - { - name: 'country', - allowEmptyValue: false, - required: true, - type: 'string', - description: 'country id', - }, -]; diff --git a/src/modules/country/constants/country.list.constant.ts b/src/modules/country/constants/country.list.constant.ts index 64e69cb57..ee0411fc1 100644 --- a/src/modules/country/constants/country.list.constant.ts +++ b/src/modules/country/constants/country.list.constant.ts @@ -3,5 +3,3 @@ export const COUNTRY_DEFAULT_AVAILABLE_SEARCH = [ 'alpha2Code', 'phoneCode', ]; - -export const COUNTRY_DEFAULT_IS_ACTIVE = [true, false]; diff --git a/src/modules/country/controllers/country.admin.controller.ts b/src/modules/country/controllers/country.admin.controller.ts deleted file mode 100644 index 1580ce9ae..000000000 --- a/src/modules/country/controllers/country.admin.controller.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { Controller, Get, Param } from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; -import { ApiKeyProtected } from 'src/modules/api-key/decorators/api-key.decorator'; -import { AuthJwtAccessProtected } from 'src/modules/auth/decorators/auth.jwt.decorator'; -import { - PaginationQuery, - PaginationQueryFilterInBoolean, -} from 'src/common/pagination/decorators/pagination.decorator'; -import { PaginationListDto } from 'src/common/pagination/dtos/pagination.list.dto'; -import { PaginationService } from 'src/common/pagination/services/pagination.service'; -import { - ENUM_POLICY_ACTION, - ENUM_POLICY_ROLE_TYPE, - ENUM_POLICY_SUBJECT, -} from 'src/modules/policy/enums/policy.enum'; -import { - PolicyAbilityProtected, - PolicyRoleProtected, -} from 'src/modules/policy/decorators/policy.decorator'; -import { RequestRequiredPipe } from 'src/common/request/pipes/request.required.pipe'; -import { - Response, - ResponsePaging, -} from 'src/common/response/decorators/response.decorator'; -import { - IResponse, - IResponsePaging, -} from 'src/common/response/interfaces/response.interface'; -import { - COUNTRY_DEFAULT_AVAILABLE_SEARCH, - COUNTRY_DEFAULT_IS_ACTIVE, -} from 'src/modules/country/constants/country.list.constant'; -import { - CountryAdminGetDoc, - CountryAdminListDoc, -} from 'src/modules/country/docs/country.admin.doc'; -import { CountryGetResponseDto } from 'src/modules/country/dtos/response/country.get.response.dto'; -import { CountryListResponseDto } from 'src/modules/country/dtos/response/country.list.response.dto'; -import { CountryParsePipe } from 'src/modules/country/pipes/country.parse.pipe'; -import { CountryDoc } from 'src/modules/country/repository/entities/country.entity'; -import { CountryService } from 'src/modules/country/services/country.service'; - -@ApiTags('modules.admin.country') -@Controller({ - version: '1', - path: '/country', -}) -export class CountryAdminController { - constructor( - private readonly countryService: CountryService, - private readonly paginationService: PaginationService - ) {} - - @CountryAdminListDoc() - @ResponsePaging('country.list') - @PolicyAbilityProtected({ - subject: ENUM_POLICY_SUBJECT.COUNTRY, - action: [ENUM_POLICY_ACTION.READ], - }) - @PolicyRoleProtected(ENUM_POLICY_ROLE_TYPE.ADMIN) - @AuthJwtAccessProtected() - @ApiKeyProtected() - @Get('/list') - async list( - @PaginationQuery({ - availableSearch: COUNTRY_DEFAULT_AVAILABLE_SEARCH, - }) - { _search, _limit, _offset, _order }: PaginationListDto, - @PaginationQueryFilterInBoolean('isActive', COUNTRY_DEFAULT_IS_ACTIVE) - isActive: Record - ): Promise> { - const find: Record = { - ..._search, - ...isActive, - }; - - const countries: CountryDoc[] = await this.countryService.findAll( - find, - { - paging: { - limit: _limit, - offset: _offset, - }, - order: _order, - } - ); - const total: number = await this.countryService.getTotal(find); - const totalPage: number = this.paginationService.totalPage( - total, - _limit - ); - - const mapped: CountryListResponseDto[] = - await this.countryService.mapList(countries); - - return { - _pagination: { total, totalPage }, - data: mapped, - }; - } - - @CountryAdminGetDoc() - @Response('country.get') - @PolicyAbilityProtected({ - subject: ENUM_POLICY_SUBJECT.COUNTRY, - action: [ENUM_POLICY_ACTION.READ], - }) - @PolicyRoleProtected(ENUM_POLICY_ROLE_TYPE.ADMIN) - @AuthJwtAccessProtected() - @Get('/get/:country') - async get( - @Param('country', RequestRequiredPipe, CountryParsePipe) - country: CountryDoc - ): Promise> { - const mapped: CountryGetResponseDto = - await this.countryService.mapGet(country); - return { data: mapped }; - } -} diff --git a/src/modules/country/controllers/country.system.controller.ts b/src/modules/country/controllers/country.system.controller.ts index 8bbfec5cb..96252e056 100644 --- a/src/modules/country/controllers/country.system.controller.ts +++ b/src/modules/country/controllers/country.system.controller.ts @@ -7,10 +7,10 @@ import { PaginationService } from 'src/common/pagination/services/pagination.ser import { ResponsePaging } from 'src/common/response/decorators/response.decorator'; import { IResponsePaging } from 'src/common/response/interfaces/response.interface'; import { COUNTRY_DEFAULT_AVAILABLE_SEARCH } from 'src/modules/country/constants/country.list.constant'; -import { CountryListResponseDto } from 'src/modules/country/dtos/response/country.list.response.dto'; import { CountryDoc } from 'src/modules/country/repository/entities/country.entity'; import { CountryService } from 'src/modules/country/services/country.service'; import { CountrySystemListDoc } from 'src/modules/country/docs/country.system.doc'; +import { CountryListResponseDto } from 'src/modules/country/dtos/response/country.list.response.dto'; @ApiTags('modules.system.country') @Controller({ @@ -35,7 +35,6 @@ export class CountrySystemController { ): Promise> { const find: Record = { ..._search, - isActive: true, }; const countries: CountryDoc[] = await this.countryService.findAll( diff --git a/src/modules/country/docs/country.admin.doc.ts b/src/modules/country/docs/country.admin.doc.ts deleted file mode 100644 index 38963ed7a..000000000 --- a/src/modules/country/docs/country.admin.doc.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { applyDecorators } from '@nestjs/common'; -import { - Doc, - DocAuth, - DocGuard, - DocRequest, - DocResponse, - DocResponsePaging, -} from 'src/common/doc/decorators/doc.decorator'; -import { CountryDocParamsId } from 'src/modules/country/constants/country.doc.constant'; -import { CountryGetResponseDto } from 'src/modules/country/dtos/response/country.get.response.dto'; -import { CountryListResponseDto } from 'src/modules/country/dtos/response/country.list.response.dto'; - -export function CountryAdminListDoc(): MethodDecorator { - return applyDecorators( - Doc({ - summary: 'get all of countries', - }), - DocAuth({ - xApiKey: true, - jwtAccessToken: true, - }), - DocGuard({ role: true, policy: true }), - DocResponsePaging('country.list', { - dto: CountryListResponseDto, - }) - ); -} - -export function CountryAdminGetDoc(): MethodDecorator { - return applyDecorators( - Doc({ - summary: 'get detail a country', - }), - DocRequest({ - params: CountryDocParamsId, - }), - DocAuth({ - xApiKey: true, - jwtAccessToken: true, - }), - DocGuard({ role: true, policy: true }), - DocResponse('country.get', { - dto: CountryGetResponseDto, - }) - ); -} diff --git a/src/modules/country/dtos/request/country.create.request.dto.ts b/src/modules/country/dtos/request/country.create.request.dto.ts index 03ccc2fc4..3add7f882 100644 --- a/src/modules/country/dtos/request/country.create.request.dto.ts +++ b/src/modules/country/dtos/request/country.create.request.dto.ts @@ -14,7 +14,6 @@ import { export class CountryCreateRequestDto { @ApiProperty({ required: true, - type: String, description: 'Country name', example: faker.location.country(), maxLength: 100, @@ -28,7 +27,6 @@ export class CountryCreateRequestDto { @ApiProperty({ required: true, - type: String, description: 'Country code, Alpha 2 code version', example: faker.location.countryCode('alpha-2'), maxLength: 2, @@ -43,7 +41,6 @@ export class CountryCreateRequestDto { @ApiProperty({ required: true, - type: String, description: 'Country code, Alpha 3 code version', example: faker.location.countryCode('alpha-3'), maxLength: 3, @@ -58,7 +55,6 @@ export class CountryCreateRequestDto { @ApiProperty({ required: true, - type: String, description: 'Country code, Numeric code version', example: faker.location.countryCode('numeric'), maxLength: 3, @@ -72,7 +68,6 @@ export class CountryCreateRequestDto { @ApiProperty({ required: true, - type: String, description: 'Country code, FIPS version', example: faker.location.countryCode('alpha-2'), maxLength: 2, @@ -86,12 +81,10 @@ export class CountryCreateRequestDto { @ApiProperty({ required: true, - type: String, description: 'Country phone code', example: [faker.helpers.arrayElement(['62', '65'])], maxLength: 4, isArray: true, - default: [], }) @IsArray() @ArrayNotEmpty() @@ -116,6 +109,14 @@ export class CountryCreateRequestDto { @IsString() timeZone: string; + @ApiProperty({ + required: true, + example: faker.finance.currency(), + }) + @IsNotEmpty() + @IsString() + currency: string; + @ApiProperty({ required: false, description: 'Top level domain', diff --git a/src/modules/country/dtos/response/country.get.response.dto.ts b/src/modules/country/dtos/response/country.get.response.dto.ts deleted file mode 100644 index 4f52056bd..000000000 --- a/src/modules/country/dtos/response/country.get.response.dto.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { faker } from '@faker-js/faker'; -import { ApiHideProperty, ApiProperty } from '@nestjs/swagger'; -import { Exclude, Type } from 'class-transformer'; -import { DatabaseDto } from 'src/common/database/dtos/database.dto'; -import { AwsS3Dto } from 'src/modules/aws/dtos/aws.s3.dto'; - -export class CountryGetResponseDto extends DatabaseDto { - @ApiProperty({ - required: true, - type: String, - description: 'Country name', - example: faker.location.country(), - maxLength: 100, - minLength: 1, - }) - name: string; - - @ApiProperty({ - required: true, - type: String, - description: 'Country code, Alpha 2 code version', - example: faker.location.countryCode('alpha-2'), - maxLength: 2, - minLength: 2, - }) - alpha2Code: string; - - @ApiProperty({ - required: true, - type: String, - description: 'Country code, Alpha 3 code version', - example: faker.location.countryCode('alpha-3'), - maxLength: 3, - minLength: 3, - }) - alpha3Code: string; - - @ApiProperty({ - required: true, - type: String, - description: 'Country code, Numeric code version', - example: faker.location.countryCode('numeric'), - maxLength: 3, - minLength: 3, - }) - numericCode: string; - - @ApiProperty({ - required: true, - type: String, - description: 'Country code, FIPS version', - example: faker.location.countryCode('alpha-2'), - maxLength: 2, - minLength: 2, - }) - fipsCode: string; - - @ApiProperty({ - required: true, - type: String, - description: 'Country phone code', - example: [faker.helpers.arrayElement(['62', '65'])], - maxLength: 4, - minLength: 4, - isArray: true, - default: [], - }) - phoneCode: string[]; - - @ApiProperty({ - required: true, - example: faker.location.country(), - }) - continent: string; - - @ApiProperty({ - required: true, - example: faker.location.timeZone(), - }) - timeZone: string; - - @ApiProperty({ - required: false, - description: 'Top level domain', - example: faker.internet.domainSuffix(), - }) - domain?: string; - - @ApiProperty({ - required: false, - type: AwsS3Dto, - }) - @Type(() => AwsS3Dto) - image?: AwsS3Dto; - - @ApiProperty({ - description: 'Date created at', - example: faker.date.recent(), - required: true, - nullable: false, - }) - createdAt: Date; - - @ApiProperty({ - description: 'Date updated at', - example: faker.date.recent(), - required: true, - nullable: false, - }) - updatedAt: Date; - - @ApiHideProperty() - @Exclude() - deletedAt?: Date; -} diff --git a/src/modules/country/dtos/response/country.list.response.dto.ts b/src/modules/country/dtos/response/country.list.response.dto.ts index 105017e61..11319b368 100644 --- a/src/modules/country/dtos/response/country.list.response.dto.ts +++ b/src/modules/country/dtos/response/country.list.response.dto.ts @@ -1,36 +1,106 @@ -import { ApiHideProperty, OmitType } from '@nestjs/swagger'; +import { faker } from '@faker-js/faker'; +import { ApiHideProperty, ApiProperty } from '@nestjs/swagger'; import { Exclude } from 'class-transformer'; -import { CountryGetResponseDto } from 'src/modules/country/dtos/response/country.get.response.dto'; - -export class CountryListResponseDto extends OmitType(CountryGetResponseDto, [ - 'alpha3Code', - 'fipsCode', - 'continent', - 'domain', - 'timeZone', - 'numericCode', -] as const) { - @ApiHideProperty() - @Exclude() +import { DatabaseDto } from 'src/common/database/dtos/database.dto'; + +export class CountryListResponseDto extends DatabaseDto { + @ApiProperty({ + required: true, + description: 'Country name', + example: faker.location.country(), + maxLength: 100, + minLength: 1, + }) + name: string; + + @ApiProperty({ + required: true, + description: 'Country code, Alpha 2 code version', + example: faker.location.countryCode('alpha-2'), + maxLength: 2, + minLength: 2, + }) + alpha2Code: string; + + @ApiProperty({ + required: true, + description: 'Country code, Alpha 3 code version', + example: faker.location.countryCode('alpha-3'), + maxLength: 3, + minLength: 3, + }) alpha3Code: string; - @ApiHideProperty() - @Exclude() + @ApiProperty({ + required: true, + description: 'Country code, Numeric code version', + example: faker.location.countryCode('numeric'), + maxLength: 3, + minLength: 3, + }) + numericCode: string; + + @ApiProperty({ + required: true, + description: 'Country code, FIPS version', + example: faker.location.countryCode('alpha-2'), + maxLength: 2, + minLength: 2, + }) fipsCode: string; - @ApiHideProperty() - @Exclude() + @ApiProperty({ + required: true, + description: 'Country phone code', + example: [faker.helpers.arrayElement(['62', '65'])], + maxLength: 4, + minLength: 4, + isArray: true, + }) + phoneCode: string[]; + + @ApiProperty({ + required: true, + example: faker.location.country(), + }) continent: string; - @ApiHideProperty() - @Exclude() + @ApiProperty({ + required: true, + example: faker.location.timeZone(), + }) + timeZone: string; + + @ApiProperty({ + required: true, + example: faker.finance.currencyCode(), + }) + currency: string; + + @ApiProperty({ + required: false, + description: 'Top level domain', + example: faker.internet.domainSuffix(), + }) domain?: string; - @ApiHideProperty() - @Exclude() - timeZone: string; + @ApiProperty({ + description: 'Date created at', + example: faker.date.recent(), + required: true, + nullable: false, + }) + createdAt: Date; + + @ApiProperty({ + description: 'Date updated at', + example: faker.date.recent(), + required: true, + nullable: false, + }) + updatedAt: Date; @ApiHideProperty() @Exclude() - numericCode: string; + deletedAt?: Date; } diff --git a/src/modules/country/dtos/response/country.short.response.dto.ts b/src/modules/country/dtos/response/country.short.response.dto.ts index ec47ddd03..48c185b03 100644 --- a/src/modules/country/dtos/response/country.short.response.dto.ts +++ b/src/modules/country/dtos/response/country.short.response.dto.ts @@ -5,7 +5,32 @@ import { CountryListResponseDto } from 'src/modules/country/dtos/response/countr export class CountryShortResponseDto extends OmitType(CountryListResponseDto, [ 'createdAt', 'updatedAt', + 'alpha3Code', + 'numericCode', + 'fipsCode', + 'continent', + 'domain', ]) { + @ApiHideProperty() + @Exclude() + alpha3Code: string; + + @ApiHideProperty() + @Exclude() + numericCode: string; + + @ApiHideProperty() + @Exclude() + fipsCode: string; + + @ApiHideProperty() + @Exclude() + continent: string; + + @ApiHideProperty() + @Exclude() + domain?: string; + @ApiHideProperty() @Exclude() createdAt: Date; diff --git a/src/modules/country/interfaces/country.service.interface.ts b/src/modules/country/interfaces/country.service.interface.ts index 847a9e15f..0533a0f08 100644 --- a/src/modules/country/interfaces/country.service.interface.ts +++ b/src/modules/country/interfaces/country.service.interface.ts @@ -6,7 +6,6 @@ import { IDatabaseOptions, } from 'src/common/database/interfaces/database.interface'; import { CountryCreateRequestDto } from 'src/modules/country/dtos/request/country.create.request.dto'; -import { CountryGetResponseDto } from 'src/modules/country/dtos/response/country.get.response.dto'; import { CountryListResponseDto } from 'src/modules/country/dtos/response/country.list.response.dto'; import { CountryShortResponseDto } from 'src/modules/country/dtos/response/country.short.response.dto'; import { @@ -31,15 +30,11 @@ export interface ICountryService { alpha2: string, options?: IDatabaseOptions ): Promise; - findOneActiveByPhoneCode( + findOneByPhoneCode( phoneCode: string, options?: IDatabaseOptions ): Promise; findOneById(_id: string, options?: IDatabaseOptions): Promise; - findOneActiveById( - _id: string, - options?: IDatabaseOptions - ): Promise; getTotal( find?: Record, options?: IDatabaseGetTotalOptions @@ -55,7 +50,6 @@ export interface ICountryService { mapList( countries: CountryDoc[] | CountryEntity[] ): Promise; - mapGet(country: CountryDoc | CountryEntity): Promise; mapShort( countries: CountryDoc[] | CountryEntity[] ): Promise; diff --git a/src/modules/country/pipes/country.parse.pipe.ts b/src/modules/country/pipes/country.parse.pipe.ts deleted file mode 100644 index 55447975a..000000000 --- a/src/modules/country/pipes/country.parse.pipe.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Injectable, NotFoundException, PipeTransform } from '@nestjs/common'; -import { ENUM_COUNTRY_STATUS_CODE_ERROR } from 'src/modules/country/enums/country.status-code.enum'; -import { CountryDoc } from 'src/modules/country/repository/entities/country.entity'; -import { CountryService } from 'src/modules/country/services/country.service'; - -@Injectable() -export class CountryParsePipe implements PipeTransform { - constructor(private readonly countryService: CountryService) {} - - async transform(value: any): Promise { - const country: CountryDoc = - await this.countryService.findOneById(value); - if (!country) { - throw new NotFoundException({ - statusCode: ENUM_COUNTRY_STATUS_CODE_ERROR.NOT_FOUND, - message: 'country.error.notFound', - }); - } - - return country; - } -} diff --git a/src/modules/country/repository/entities/country.entity.ts b/src/modules/country/repository/entities/country.entity.ts index 1cafd6c4d..5f33ee680 100644 --- a/src/modules/country/repository/entities/country.entity.ts +++ b/src/modules/country/repository/entities/country.entity.ts @@ -1,17 +1,15 @@ -import { DatabaseEntityAbstract } from 'src/common/database/abstracts/database.entity.abstract'; +import { DatabaseEntityBase } from 'src/common/database/bases/database.entity'; import { DatabaseEntity, DatabaseProp, DatabaseSchema, } from 'src/common/database/decorators/database.decorator'; import { IDatabaseDocument } from 'src/common/database/interfaces/database.interface'; -import { AwsS3Dto } from 'src/modules/aws/dtos/aws.s3.dto'; -import { AwsS3Schema } from 'src/modules/aws/repository/entities/aws.s3.entity'; export const CountryTableName = 'Countries'; @DatabaseEntity({ collection: CountryTableName }) -export class CountryEntity extends DatabaseEntityAbstract { +export class CountryEntity extends DatabaseEntityBase { @DatabaseProp({ required: true, index: true, @@ -80,15 +78,14 @@ export class CountryEntity extends DatabaseEntityAbstract { timeZone: string; @DatabaseProp({ - required: false, + required: true, }) - domain?: string; + currency: string; @DatabaseProp({ required: false, - schema: AwsS3Schema, }) - image?: AwsS3Dto; + domain?: string; } export const CountrySchema = DatabaseSchema(CountryEntity); diff --git a/src/modules/country/repository/repositories/country.repository.ts b/src/modules/country/repository/repositories/country.repository.ts index dcda00c49..bd042387e 100644 --- a/src/modules/country/repository/repositories/country.repository.ts +++ b/src/modules/country/repository/repositories/country.repository.ts @@ -1,19 +1,19 @@ import { Injectable } from '@nestjs/common'; import { Model } from 'mongoose'; -import { DatabaseRepositoryAbstract } from 'src/common/database/abstracts/database.repository.abstract'; -import { DatabaseModel } from 'src/common/database/decorators/database.decorator'; +import { DatabaseRepositoryBase } from 'src/common/database/bases/database.repository'; +import { InjectDatabaseModel } from 'src/common/database/decorators/database.decorator'; import { CountryDoc, CountryEntity, } from 'src/modules/country/repository/entities/country.entity'; @Injectable() -export class CountryRepository extends DatabaseRepositoryAbstract< +export class CountryRepository extends DatabaseRepositoryBase< CountryEntity, CountryDoc > { constructor( - @DatabaseModel(CountryEntity.name) + @InjectDatabaseModel(CountryEntity.name) private readonly countryModel: Model ) { super(countryModel); diff --git a/src/modules/country/services/country.service.ts b/src/modules/country/services/country.service.ts index ec61c698a..427603e02 100644 --- a/src/modules/country/services/country.service.ts +++ b/src/modules/country/services/country.service.ts @@ -1,16 +1,15 @@ import { Injectable } from '@nestjs/common'; import { plainToInstance } from 'class-transformer'; import { Document } from 'mongoose'; -import { DatabaseQueryContain } from 'src/common/database/decorators/database.decorator'; +import { DatabaseHelperQueryContain } from 'src/common/database/decorators/database.decorator'; import { IDatabaseCreateManyOptions, IDatabaseDeleteManyOptions, IDatabaseFindAllOptions, + IDatabaseFindOneOptions, IDatabaseGetTotalOptions, - IDatabaseOptions, } from 'src/common/database/interfaces/database.interface'; import { CountryCreateRequestDto } from 'src/modules/country/dtos/request/country.create.request.dto'; -import { CountryGetResponseDto } from 'src/modules/country/dtos/response/country.get.response.dto'; import { CountryListResponseDto } from 'src/modules/country/dtos/response/country.list.response.dto'; import { CountryShortResponseDto } from 'src/modules/country/dtos/response/country.short.response.dto'; import { ICountryService } from 'src/modules/country/interfaces/country.service.interface'; @@ -33,39 +32,38 @@ export class CountryService implements ICountryService { async findOne( find: Record, - options?: IDatabaseOptions + options?: IDatabaseFindOneOptions ): Promise { return this.countryRepository.findOne(find, options); } async findOneByName( name: string, - options?: IDatabaseOptions + options?: IDatabaseFindOneOptions ): Promise { return this.countryRepository.findOne( - DatabaseQueryContain('name', name), + DatabaseHelperQueryContain('name', name), options ); } async findOneByAlpha2( alpha2: string, - options?: IDatabaseOptions + options?: IDatabaseFindOneOptions ): Promise { return this.countryRepository.findOne( - DatabaseQueryContain('alpha2Code', alpha2), + DatabaseHelperQueryContain('alpha2Code', alpha2), options ); } - async findOneActiveByPhoneCode( + async findOneByPhoneCode( phoneCode: string, - options?: IDatabaseOptions + options?: IDatabaseFindOneOptions ): Promise { return this.countryRepository.findOne( { phoneCode, - isActive: true, }, options ); @@ -73,18 +71,11 @@ export class CountryService implements ICountryService { async findOneById( _id: string, - options?: IDatabaseOptions + options?: IDatabaseFindOneOptions ): Promise { return this.countryRepository.findOneById(_id, options); } - async findOneActiveById( - _id: string, - options?: IDatabaseOptions - ): Promise { - return this.countryRepository.findOne({ _id, isActive: true }, options); - } - async getTotal( find?: Record, options?: IDatabaseGetTotalOptions @@ -96,53 +87,47 @@ export class CountryService implements ICountryService { find: Record, options?: IDatabaseDeleteManyOptions ): Promise { - try { - await this.countryRepository.deleteMany(find, options); + await this.countryRepository.deleteMany(find, options); - return true; - } catch (error: unknown) { - throw error; - } + return true; } async createMany( data: CountryCreateRequestDto[], options?: IDatabaseCreateManyOptions ): Promise { - try { - const entities: CountryEntity[] = data.map( - ({ - name, - alpha2Code, - alpha3Code, - numericCode, - continent, - fipsCode, - phoneCode, - timeZone, - domain, - }): CountryCreateRequestDto => { - const create: CountryEntity = new CountryEntity(); - create.name = name; - create.alpha2Code = alpha2Code; - create.alpha3Code = alpha3Code; - create.numericCode = numericCode; - create.continent = continent; - create.fipsCode = fipsCode; - create.phoneCode = phoneCode; - create.timeZone = timeZone; - create.domain = domain; - - return create; - } - ) as CountryEntity[]; - - await this.countryRepository.createMany(entities, options); - - return true; - } catch (error: unknown) { - throw error; - } + const entities: CountryEntity[] = data.map( + ({ + name, + alpha2Code, + alpha3Code, + numericCode, + continent, + fipsCode, + phoneCode, + timeZone, + domain, + currency, + }): CountryCreateRequestDto => { + const create: CountryEntity = new CountryEntity(); + create.name = name; + create.alpha2Code = alpha2Code; + create.alpha3Code = alpha3Code; + create.numericCode = numericCode; + create.continent = continent; + create.fipsCode = fipsCode; + create.phoneCode = phoneCode; + create.timeZone = timeZone; + create.domain = domain; + create.currency = currency; + + return create; + } + ) as CountryEntity[]; + + await this.countryRepository.createMany(entities, options); + + return true; } async mapList( @@ -156,15 +141,6 @@ export class CountryService implements ICountryService { ); } - async mapGet( - country: CountryDoc | CountryEntity - ): Promise { - return plainToInstance( - CountryGetResponseDto, - country instanceof Document ? country.toObject() : country - ); - } - async mapShort( countries: CountryDoc[] | CountryEntity[] ): Promise { diff --git a/src/modules/device/device.module.ts b/src/modules/device/device.module.ts deleted file mode 100644 index 9ee5f2fa4..000000000 --- a/src/modules/device/device.module.ts +++ /dev/null @@ -1 +0,0 @@ -// TODO: DEVICE MODULE diff --git a/src/modules/email/dtos/email.send.dto.ts b/src/modules/email/dtos/email.send.dto.ts index a826633f7..befaec958 100644 --- a/src/modules/email/dtos/email.send.dto.ts +++ b/src/modules/email/dtos/email.send.dto.ts @@ -2,14 +2,11 @@ import { faker } from '@faker-js/faker'; import { ApiProperty } from '@nestjs/swagger'; export class EmailSendDto { - @ApiProperty({ - type: 'string', - example: faker.person.fullName(), - }) + @ApiProperty({ required: true, example: faker.person.fullName() }) name: string; @ApiProperty({ - type: 'string', + required: true, example: faker.internet.email(), }) email: string; diff --git a/src/modules/email/dtos/email.temp-password.dto.ts b/src/modules/email/dtos/email.temp-password.dto.ts index eeba66551..1e019a4e7 100644 --- a/src/modules/email/dtos/email.temp-password.dto.ts +++ b/src/modules/email/dtos/email.temp-password.dto.ts @@ -12,7 +12,6 @@ export class EmailTempPasswordDto { @ApiProperty({ required: true, example: faker.date.future(), - type: 'date', description: 'Expired at by date', }) passwordExpiredAt: Date; diff --git a/src/worker/dtos/worker.email.dto.ts b/src/modules/email/dtos/email.worker.dto.ts similarity index 77% rename from src/worker/dtos/worker.email.dto.ts rename to src/modules/email/dtos/email.worker.dto.ts index a7cbb2cf3..491b539b4 100644 --- a/src/worker/dtos/worker.email.dto.ts +++ b/src/modules/email/dtos/email.worker.dto.ts @@ -1,4 +1,4 @@ -import { ApiProperty } from '@nestjs/swagger'; +import { ApiProperty, getSchemaPath } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { IsNotEmpty, @@ -9,10 +9,11 @@ import { } from 'class-validator'; import { EmailSendDto } from 'src/modules/email/dtos/email.send.dto'; -export class WorkerEmailDto { +export class EmailWorkerDto { @ApiProperty({ required: true, - type: () => EmailSendDto, + type: EmailSendDto, + oneOf: [{ $ref: getSchemaPath(EmailSendDto) }], }) @IsObject() @IsNotEmpty() diff --git a/src/modules/email/enums/email.enum.ts b/src/modules/email/enums/email.enum.ts index db2d0b1ac..d4dbbe680 100644 --- a/src/modules/email/enums/email.enum.ts +++ b/src/modules/email/enums/email.enum.ts @@ -1,6 +1,6 @@ -export enum ENUM_EMAIL { +export enum ENUM_SEND_EMAIL_PROCESS { CHANGE_PASSWORD = 'CHANGE_PASSWORD', - TEMP_PASSWORD = 'TEMP_PASSWORD', + TEMPORARY_PASSWORD = 'TEMPORARY_PASSWORD', WELCOME = 'WELCOME', WELCOME_ADMIN = 'WELCOME_ADMIN', } diff --git a/src/modules/email/interfaces/email.service.interface.ts b/src/modules/email/interfaces/email.service.interface.ts index 1017e7d5a..46ceec148 100644 --- a/src/modules/email/interfaces/email.service.interface.ts +++ b/src/modules/email/interfaces/email.service.interface.ts @@ -4,22 +4,22 @@ import { EmailTempPasswordDto } from 'src/modules/email/dtos/email.temp-password import { EmailWelcomeAdminDto } from 'src/modules/email/dtos/email.welcome-admin.dto'; export interface IEmailService { - createChangePassword(): Promise; + importChangePassword(): Promise; getChangePassword(): Promise; deleteChangePassword(): Promise; sendChangePassword({ name, email }: EmailSendDto): Promise; - createWelcome(): Promise; + importWelcome(): Promise; getWelcome(): Promise; deleteWelcome(): Promise; sendWelcome({ name, email }: EmailSendDto): Promise; - createWelcomeAdmin(): Promise; + importWelcomeAdmin(): Promise; getWelcomeAdmin(): Promise; deleteWelcomeAdmin(): Promise; sendWelcomeAdmin( { name, email }: EmailSendDto, { password, passwordExpiredAt }: EmailWelcomeAdminDto ): Promise; - createTempPassword(): Promise; + importTempPassword(): Promise; getTempPassword(): Promise; deleteTempPassword(): Promise; sendTempPassword( diff --git a/src/modules/email/processors/email.processor.ts b/src/modules/email/processors/email.processor.ts index 6f4afcb73..aa48c342b 100644 --- a/src/modules/email/processors/email.processor.ts +++ b/src/modules/email/processors/email.processor.ts @@ -5,10 +5,10 @@ import { Job } from 'bullmq'; import { EmailSendDto } from 'src/modules/email/dtos/email.send.dto'; import { EmailTempPasswordDto } from 'src/modules/email/dtos/email.temp-password.dto'; import { EmailWelcomeAdminDto } from 'src/modules/email/dtos/email.welcome-admin.dto'; -import { ENUM_EMAIL } from 'src/modules/email/enums/email.enum'; +import { EmailWorkerDto } from 'src/modules/email/dtos/email.worker.dto'; +import { ENUM_SEND_EMAIL_PROCESS } from 'src/modules/email/enums/email.enum'; import { IEmailProcessor } from 'src/modules/email/interfaces/email.processor.interface'; import { EmailService } from 'src/modules/email/services/email.service'; -import { WorkerEmailDto } from 'src/worker/dtos/worker.email.dto'; import { ENUM_WORKER_QUEUES } from 'src/worker/enums/worker.enum'; @Processor(ENUM_WORKER_QUEUES.EMAIL_QUEUE) @@ -22,32 +22,32 @@ export class EmailProcessor extends WorkerHost implements IEmailProcessor { ) { super(); - this.debug = this.configService.get('app.debug'); + this.debug = this.configService.get('debug.enable'); } - async process(job: Job): Promise { + async process(job: Job): Promise { try { const jobName = job.name; switch (jobName) { - case ENUM_EMAIL.TEMP_PASSWORD: + case ENUM_SEND_EMAIL_PROCESS.TEMPORARY_PASSWORD: await this.processTempPassword( job.data.send, job.data.data as EmailTempPasswordDto ); break; - case ENUM_EMAIL.CHANGE_PASSWORD: + case ENUM_SEND_EMAIL_PROCESS.CHANGE_PASSWORD: await this.processChangePassword(job.data.send); break; - case ENUM_EMAIL.WELCOME_ADMIN: + case ENUM_SEND_EMAIL_PROCESS.WELCOME_ADMIN: await this.processWelcomeAdmin( job.data.send, job.data.data as EmailWelcomeAdminDto ); break; - case ENUM_EMAIL.WELCOME: + case ENUM_SEND_EMAIL_PROCESS.WELCOME: default: await this.processWelcome(job.data.send); diff --git a/src/modules/email/services/email.service.ts b/src/modules/email/services/email.service.ts index a77231742..26bf6c1a1 100644 --- a/src/modules/email/services/email.service.ts +++ b/src/modules/email/services/email.service.ts @@ -1,5 +1,5 @@ import { Injectable, Logger } from '@nestjs/common'; -import { ENUM_EMAIL } from 'src/modules/email/enums/email.enum'; +import { ENUM_SEND_EMAIL_PROCESS } from 'src/modules/email/enums/email.enum'; import { title } from 'case'; import { ConfigService } from '@nestjs/config'; import { IEmailService } from 'src/modules/email/interfaces/email.service.interface'; @@ -9,12 +9,10 @@ import { EmailSendDto } from 'src/modules/email/dtos/email.send.dto'; import { HelperDateService } from 'src/common/helper/services/helper.date.service'; import { EmailTempPasswordDto } from 'src/modules/email/dtos/email.temp-password.dto'; import { EmailWelcomeAdminDto } from 'src/modules/email/dtos/email.welcome-admin.dto'; -import { ENUM_HELPER_DATE_FORMAT } from 'src/common/helper/enums/helper.enum'; import { AwsSESService } from 'src/modules/aws/services/aws.ses.service'; @Injectable() export class EmailService implements IEmailService { - private readonly debug: boolean; private readonly logger = new Logger(EmailService.name); private readonly fromEmail: string; @@ -29,33 +27,29 @@ export class EmailService implements IEmailService { private readonly helperDateService: HelperDateService, private readonly configService: ConfigService ) { - this.debug = this.configService.get('app.debug'); - this.fromEmail = this.configService.get('email.fromEmail'); this.supportEmail = this.configService.get('email.supportEmail'); - this.appName = this.configService.get('app.name'); + this.appName = this.configService.get('email.name'); this.clientUrl = this.configService.get('email.clientUrl'); } - async createChangePassword(): Promise { + async importChangePassword(): Promise { try { await this.awsSESService.createTemplate({ - name: ENUM_EMAIL.CHANGE_PASSWORD, + name: ENUM_SEND_EMAIL_PROCESS.CHANGE_PASSWORD, subject: `Change Password`, htmlBody: readFileSync( - './templates/email.change-password.template.html', + '/templates/email/change-password.template.html', 'utf8' ), }); return true; } catch (err: unknown) { - if (this.debug) { - this.logger.error(err); - } + this.logger.error(err); return false; } @@ -63,21 +57,19 @@ export class EmailService implements IEmailService { async getChangePassword(): Promise { return this.awsSESService.getTemplate({ - name: ENUM_EMAIL.CHANGE_PASSWORD, + name: ENUM_SEND_EMAIL_PROCESS.CHANGE_PASSWORD, }); } async deleteChangePassword(): Promise { try { await this.awsSESService.deleteTemplate({ - name: ENUM_EMAIL.CHANGE_PASSWORD, + name: ENUM_SEND_EMAIL_PROCESS.CHANGE_PASSWORD, }); return true; } catch (err: unknown) { - if (this.debug) { - this.logger.error(err); - } + this.logger.error(err); return false; } @@ -86,7 +78,7 @@ export class EmailService implements IEmailService { async sendChangePassword({ name, email }: EmailSendDto): Promise { try { await this.awsSESService.send({ - templateName: ENUM_EMAIL.CHANGE_PASSWORD, + templateName: ENUM_SEND_EMAIL_PROCESS.CHANGE_PASSWORD, recipients: [email], sender: this.fromEmail, templateData: { @@ -99,30 +91,26 @@ export class EmailService implements IEmailService { return true; } catch (err: unknown) { - if (this.debug) { - this.logger.error(err); - } + this.logger.error(err); return false; } } - async createWelcome(): Promise { + async importWelcome(): Promise { try { await this.awsSESService.createTemplate({ - name: ENUM_EMAIL.WELCOME, + name: ENUM_SEND_EMAIL_PROCESS.WELCOME, subject: `Welcome`, htmlBody: readFileSync( - './templates/email.welcome.template.html', + '/templates/email/welcome.template.html', 'utf8' ), }); return true; } catch (err: unknown) { - if (this.debug) { - this.logger.error(err); - } + this.logger.error(err); return false; } @@ -130,21 +118,19 @@ export class EmailService implements IEmailService { async getWelcome(): Promise { return this.awsSESService.getTemplate({ - name: ENUM_EMAIL.WELCOME, + name: ENUM_SEND_EMAIL_PROCESS.WELCOME, }); } async deleteWelcome(): Promise { try { await this.awsSESService.deleteTemplate({ - name: ENUM_EMAIL.WELCOME, + name: ENUM_SEND_EMAIL_PROCESS.WELCOME, }); return true; } catch (err: unknown) { - if (this.debug) { - this.logger.error(err); - } + this.logger.error(err); return false; } @@ -153,7 +139,7 @@ export class EmailService implements IEmailService { async sendWelcome({ name, email }: EmailSendDto): Promise { try { await this.awsSESService.send({ - templateName: ENUM_EMAIL.WELCOME, + templateName: ENUM_SEND_EMAIL_PROCESS.WELCOME, recipients: [email], sender: this.fromEmail, templateData: { @@ -167,30 +153,26 @@ export class EmailService implements IEmailService { return true; } catch (err: unknown) { - if (this.debug) { - this.logger.error(err); - } + this.logger.error(err); return false; } } - async createWelcomeAdmin(): Promise { + async importWelcomeAdmin(): Promise { try { await this.awsSESService.createTemplate({ - name: ENUM_EMAIL.WELCOME_ADMIN, + name: ENUM_SEND_EMAIL_PROCESS.WELCOME_ADMIN, subject: `Welcome`, htmlBody: readFileSync( - './templates/email.welcome-admin.template.html', + '/templates/email/welcome-admin.template.html', 'utf8' ), }); return true; } catch (err: unknown) { - if (this.debug) { - this.logger.error(err); - } + this.logger.error(err); return false; } @@ -198,21 +180,19 @@ export class EmailService implements IEmailService { async getWelcomeAdmin(): Promise { return this.awsSESService.getTemplate({ - name: ENUM_EMAIL.WELCOME_ADMIN, + name: ENUM_SEND_EMAIL_PROCESS.WELCOME_ADMIN, }); } async deleteWelcomeAdmin(): Promise { try { await this.awsSESService.deleteTemplate({ - name: ENUM_EMAIL.WELCOME_ADMIN, + name: ENUM_SEND_EMAIL_PROCESS.WELCOME_ADMIN, }); return true; } catch (err: unknown) { - if (this.debug) { - this.logger.error(err); - } + this.logger.error(err); return false; } @@ -224,7 +204,7 @@ export class EmailService implements IEmailService { ): Promise { try { await this.awsSESService.send({ - templateName: ENUM_EMAIL.WELCOME, + templateName: ENUM_SEND_EMAIL_PROCESS.WELCOME_ADMIN, recipients: [email], sender: this.fromEmail, templateData: { @@ -234,41 +214,35 @@ export class EmailService implements IEmailService { password: passwordString, supportEmail: this.supportEmail, clientUrl: this.clientUrl, - passwordExpiredAt: this.helperDateService.format( - passwordExpiredAt, - { - format: ENUM_HELPER_DATE_FORMAT.FRIENDLY_DATE_TIME, - } - ), + passwordExpiredAt: + this.helperDateService.formatToRFC2822( + passwordExpiredAt + ), }, }); return true; } catch (err: unknown) { - if (this.debug) { - this.logger.error(err); - } + this.logger.error(err); return false; } } - async createTempPassword(): Promise { + async importTempPassword(): Promise { try { await this.awsSESService.createTemplate({ - name: ENUM_EMAIL.TEMP_PASSWORD, + name: ENUM_SEND_EMAIL_PROCESS.TEMPORARY_PASSWORD, subject: `Temporary Password`, htmlBody: readFileSync( - './templates/email.temp-password.template.html', + '/templates/email/temp-password.template.html', 'utf8' ), }); return true; } catch (err: unknown) { - if (this.debug) { - this.logger.error(err); - } + this.logger.error(err); return false; } @@ -277,14 +251,12 @@ export class EmailService implements IEmailService { async getTempPassword(): Promise { try { const template = await this.awsSESService.getTemplate({ - name: ENUM_EMAIL.TEMP_PASSWORD, + name: ENUM_SEND_EMAIL_PROCESS.TEMPORARY_PASSWORD, }); return template; } catch (err: unknown) { - if (this.debug) { - this.logger.error(err); - } + this.logger.error(err); return; } @@ -293,14 +265,12 @@ export class EmailService implements IEmailService { async deleteTempPassword(): Promise { try { await this.awsSESService.deleteTemplate({ - name: ENUM_EMAIL.TEMP_PASSWORD, + name: ENUM_SEND_EMAIL_PROCESS.TEMPORARY_PASSWORD, }); return true; } catch (err: unknown) { - if (this.debug) { - this.logger.error(err); - } + this.logger.error(err); return false; } @@ -312,7 +282,7 @@ export class EmailService implements IEmailService { ): Promise { try { await this.awsSESService.send({ - templateName: ENUM_EMAIL.TEMP_PASSWORD, + templateName: ENUM_SEND_EMAIL_PROCESS.TEMPORARY_PASSWORD, recipients: [email], sender: this.fromEmail, templateData: { @@ -321,20 +291,16 @@ export class EmailService implements IEmailService { password: passwordString, supportEmail: this.supportEmail, clientUrl: this.clientUrl, - passwordExpiredAt: this.helperDateService.format( - passwordExpiredAt, - { - format: ENUM_HELPER_DATE_FORMAT.FRIENDLY_DATE_TIME, - } - ), + passwordExpiredAt: + this.helperDateService.formatToRFC2822( + passwordExpiredAt + ), }, }); return true; } catch (err: unknown) { - if (this.debug) { - this.logger.error(err); - } + this.logger.error(err); return false; } diff --git a/src/modules/health/controllers/health.system.controller.ts b/src/modules/health/controllers/health.system.controller.ts index 3dd928728..460cfe000 100644 --- a/src/modules/health/controllers/health.system.controller.ts +++ b/src/modules/health/controllers/health.system.controller.ts @@ -9,13 +9,20 @@ import { } from '@nestjs/terminus'; import { Connection } from 'mongoose'; import { ApiKeySystemProtected } from 'src/modules/api-key/decorators/api-key.decorator'; -import { DatabaseConnection } from 'src/common/database/decorators/database.decorator'; +import { InjectDatabaseConnection } from 'src/common/database/decorators/database.decorator'; import { Response } from 'src/common/response/decorators/response.decorator'; import { IResponse } from 'src/common/response/interfaces/response.interface'; import { HealthResponseDto } from 'src/modules/health/dtos/response/health.response.dto'; import { HealthAwsS3Indicator } from 'src/modules/health/indicators/health.aws-s3.indicator'; import { HealthSystemCheckDoc } from 'src/modules/health/docs/health.system.doc'; +// TODO: MORE HEALTH CHECK +// - aws ses +// - aws s3 +// - google +// - apple +// - sentry +// - redis @ApiTags('modules.system.health') @Controller({ version: VERSION_NEUTRAL, @@ -23,7 +30,8 @@ import { HealthSystemCheckDoc } from 'src/modules/health/docs/health.system.doc' }) export class HealthSystemController { constructor( - @DatabaseConnection() private readonly databaseConnection: Connection, + @InjectDatabaseConnection() + private readonly databaseConnection: Connection, private readonly health: HealthCheckService, private readonly memoryHealthIndicator: MemoryHealthIndicator, private readonly diskHealthIndicator: DiskHealthIndicator, diff --git a/src/modules/health/indicators/health.aws-s3.indicator.ts b/src/modules/health/indicators/health.aws-s3.indicator.ts index ddd2d92cd..b62c08245 100644 --- a/src/modules/health/indicators/health.aws-s3.indicator.ts +++ b/src/modules/health/indicators/health.aws-s3.indicator.ts @@ -14,7 +14,7 @@ export class HealthAwsS3Indicator extends HealthIndicator { async isHealthy(key: string): Promise { try { - await this.awsS3Service.checkBucketExistence(); + await this.awsS3Service.checkBucket(); return this.getStatus(key, true); } catch (err: any) { throw new HealthCheckError( diff --git a/src/modules/hello/controllers/hello.public.controller.ts b/src/modules/hello/controllers/hello.public.controller.ts index 533dba60e..145acac32 100644 --- a/src/modules/hello/controllers/hello.public.controller.ts +++ b/src/modules/hello/controllers/hello.public.controller.ts @@ -22,13 +22,13 @@ export class HelloPublicController { @ApiKeyProtected() @Get('/') async hello(): Promise> { - const newDate = this.helperDateService.create(); + const today = this.helperDateService.create(); return { data: { - date: newDate, - format: this.helperDateService.format(newDate), - timestamp: this.helperDateService.createTimestamp(newDate), + date: today, + format: this.helperDateService.formatToIso(today), + timestamp: this.helperDateService.getTimestamp(today), }, }; } diff --git a/src/modules/hello/dtos/response/hello.response.dto.ts b/src/modules/hello/dtos/response/hello.response.dto.ts index 00f4a3e75..1661aaf42 100644 --- a/src/modules/hello/dtos/response/hello.response.dto.ts +++ b/src/modules/hello/dtos/response/hello.response.dto.ts @@ -1,6 +1,5 @@ import { faker } from '@faker-js/faker'; import { ApiProperty } from '@nestjs/swagger'; -import { Type } from 'class-transformer'; export class HelloResponseDto { @ApiProperty({ @@ -8,7 +7,6 @@ export class HelloResponseDto { nullable: false, example: faker.date.recent(), }) - @Type(() => String) date: Date; @ApiProperty({ diff --git a/src/modules/password-history/controllers/password-history.admin.controller.ts b/src/modules/password-history/controllers/password-history.admin.controller.ts new file mode 100644 index 000000000..ae5c3cb9b --- /dev/null +++ b/src/modules/password-history/controllers/password-history.admin.controller.ts @@ -0,0 +1,82 @@ +import { Controller, Get, Param } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; +import { PaginationQuery } from 'src/common/pagination/decorators/pagination.decorator'; +import { PaginationListDto } from 'src/common/pagination/dtos/pagination.list.dto'; +import { PaginationService } from 'src/common/pagination/services/pagination.service'; +import { RequestRequiredPipe } from 'src/common/request/pipes/request.required.pipe'; +import { ResponsePaging } from 'src/common/response/decorators/response.decorator'; +import { IResponsePaging } from 'src/common/response/interfaces/response.interface'; +import { ApiKeyProtected } from 'src/modules/api-key/decorators/api-key.decorator'; +import { AuthJwtAccessProtected } from 'src/modules/auth/decorators/auth.jwt.decorator'; +import { PasswordHistoryAdminListDoc } from 'src/modules/password-history/docs/password-history.admin.doc'; +import { PasswordHistoryListResponseDto } from 'src/modules/password-history/dtos/response/password-history.list.response.dto'; +import { IPasswordHistoryDoc } from 'src/modules/password-history/interfaces/password-history.interface'; +import { PasswordHistoryService } from 'src/modules/password-history/services/password-history.service'; +import { + PolicyAbilityProtected, + PolicyRoleProtected, +} from 'src/modules/policy/decorators/policy.decorator'; +import { + ENUM_POLICY_ACTION, + ENUM_POLICY_ROLE_TYPE, + ENUM_POLICY_SUBJECT, +} from 'src/modules/policy/enums/policy.enum'; +import { UserParsePipe } from 'src/modules/user/pipes/user.parse.pipe'; +import { UserDoc } from 'src/modules/user/repository/entities/user.entity'; + +@ApiTags('modules.admin.passwordHistory') +@Controller({ + version: '1', + path: '/password-history/:user', +}) +export class PasswordHistoryAdminController { + constructor( + private readonly paginationService: PaginationService, + private readonly passwordHistoryService: PasswordHistoryService + ) {} + + @PasswordHistoryAdminListDoc() + @ResponsePaging('passwordHistory.list') + @PolicyAbilityProtected({ + subject: ENUM_POLICY_SUBJECT.SESSION, + action: [ENUM_POLICY_ACTION.READ], + }) + @PolicyRoleProtected(ENUM_POLICY_ROLE_TYPE.ADMIN) + @AuthJwtAccessProtected() + @ApiKeyProtected() + @Get('/list') + async list( + @Param('user', RequestRequiredPipe, UserParsePipe) user: UserDoc, + @PaginationQuery() + { _search, _limit, _offset, _order }: PaginationListDto + ): Promise> { + const find: Record = { + ..._search, + }; + + const passwordHistories: IPasswordHistoryDoc[] = + await this.passwordHistoryService.findAllByUser(user._id, find, { + paging: { + limit: _limit, + offset: _offset, + }, + order: _order, + }); + const total: number = await this.passwordHistoryService.getTotalByUser( + user._id, + find + ); + const totalPage: number = this.paginationService.totalPage( + total, + _limit + ); + + const mapped = + await this.passwordHistoryService.mapList(passwordHistories); + + return { + _pagination: { total, totalPage }, + data: mapped, + }; + } +} diff --git a/src/modules/password-history/controllers/password-history.shared.controller.ts b/src/modules/password-history/controllers/password-history.shared.controller.ts new file mode 100644 index 000000000..5e63d4b77 --- /dev/null +++ b/src/modules/password-history/controllers/password-history.shared.controller.ts @@ -0,0 +1,68 @@ +import { Controller, Get } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; +import { PaginationQuery } from 'src/common/pagination/decorators/pagination.decorator'; +import { PaginationListDto } from 'src/common/pagination/dtos/pagination.list.dto'; +import { PaginationService } from 'src/common/pagination/services/pagination.service'; +import { ResponsePaging } from 'src/common/response/decorators/response.decorator'; +import { IResponsePaging } from 'src/common/response/interfaces/response.interface'; +import { ApiKeyProtected } from 'src/modules/api-key/decorators/api-key.decorator'; +import { + AuthJwtAccessProtected, + AuthJwtPayload, +} from 'src/modules/auth/decorators/auth.jwt.decorator'; +import { PasswordHistorySharedListDoc } from 'src/modules/password-history/docs/password-history.shared.doc'; +import { PasswordHistoryListResponseDto } from 'src/modules/password-history/dtos/response/password-history.list.response.dto'; +import { IPasswordHistoryDoc } from 'src/modules/password-history/interfaces/password-history.interface'; +import { PasswordHistoryService } from 'src/modules/password-history/services/password-history.service'; + +@ApiTags('modules.shared.passwordHistory') +@Controller({ + version: '1', + path: '/password-history', +}) +export class PasswordHistorySharedController { + constructor( + private readonly paginationService: PaginationService, + private readonly passwordHistoryService: PasswordHistoryService + ) {} + + @PasswordHistorySharedListDoc() + @ResponsePaging('passwordHistory.list') + @AuthJwtAccessProtected() + @ApiKeyProtected() + @Get('/list') + async list( + @AuthJwtPayload('_id') user: string, + @PaginationQuery() + { _search, _limit, _offset, _order }: PaginationListDto + ): Promise> { + const find: Record = { + ..._search, + }; + + const passwordHistories: IPasswordHistoryDoc[] = + await this.passwordHistoryService.findAllByUser(user, find, { + paging: { + limit: _limit, + offset: _offset, + }, + order: _order, + }); + const total: number = await this.passwordHistoryService.getTotalByUser( + user, + find + ); + const totalPage: number = this.paginationService.totalPage( + total, + _limit + ); + + const mapped = + await this.passwordHistoryService.mapList(passwordHistories); + + return { + _pagination: { total, totalPage }, + data: mapped, + }; + } +} diff --git a/src/modules/password-history/docs/password-history.admin.doc.ts b/src/modules/password-history/docs/password-history.admin.doc.ts new file mode 100644 index 000000000..6f455b415 --- /dev/null +++ b/src/modules/password-history/docs/password-history.admin.doc.ts @@ -0,0 +1,32 @@ +import { applyDecorators } from '@nestjs/common'; +import { + Doc, + DocAuth, + DocGuard, + DocRequest, + DocResponsePaging, +} from 'src/common/doc/decorators/doc.decorator'; +import { PasswordHistoryListResponseDto } from 'src/modules/password-history/dtos/response/password-history.list.response.dto'; +import { UserDocParamsId } from 'src/modules/user/constants/user.doc.constant'; + +export function PasswordHistoryAdminListDoc(): MethodDecorator { + return applyDecorators( + Doc({ + summary: 'get all user password histories', + }), + DocRequest({ + params: UserDocParamsId, + }), + DocAuth({ + xApiKey: true, + jwtAccessToken: true, + }), + DocGuard({ role: true, policy: true }), + DocResponsePaging( + 'passwordHistory.list', + { + dto: PasswordHistoryListResponseDto, + } + ) + ); +} diff --git a/src/modules/password-history/docs/password-history.shared.doc.ts b/src/modules/password-history/docs/password-history.shared.doc.ts new file mode 100644 index 000000000..974b055d7 --- /dev/null +++ b/src/modules/password-history/docs/password-history.shared.doc.ts @@ -0,0 +1,25 @@ +import { applyDecorators } from '@nestjs/common'; +import { + Doc, + DocAuth, + DocResponsePaging, +} from 'src/common/doc/decorators/doc.decorator'; +import { PasswordHistoryListResponseDto } from 'src/modules/password-history/dtos/response/password-history.list.response.dto'; + +export function PasswordHistorySharedListDoc(): MethodDecorator { + return applyDecorators( + Doc({ + summary: 'get all user password Histories', + }), + DocAuth({ + xApiKey: true, + jwtAccessToken: true, + }), + DocResponsePaging( + 'passwordHistory.list', + { + dto: PasswordHistoryListResponseDto, + } + ) + ); +} diff --git a/src/modules/password-history/dtos/request/password-history.create-by-admin.request.dto.ts b/src/modules/password-history/dtos/request/password-history.create-by-admin.request.dto.ts new file mode 100644 index 000000000..56abb6a0d --- /dev/null +++ b/src/modules/password-history/dtos/request/password-history.create-by-admin.request.dto.ts @@ -0,0 +1,14 @@ +import { faker } from '@faker-js/faker'; +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsUUID } from 'class-validator'; +import { PasswordHistoryCreateRequestDto } from 'src/modules/password-history/dtos/request/password-history.create.request.dto'; + +export class PasswordHistoryCreateByAdminRequestDto extends PasswordHistoryCreateRequestDto { + @ApiProperty({ + example: faker.string.uuid(), + required: true, + }) + @IsNotEmpty() + @IsUUID() + by: string; +} diff --git a/src/modules/password-history/dtos/request/password-history.create.request.dto.ts b/src/modules/password-history/dtos/request/password-history.create.request.dto.ts new file mode 100644 index 000000000..126221f7b --- /dev/null +++ b/src/modules/password-history/dtos/request/password-history.create.request.dto.ts @@ -0,0 +1,15 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsEmpty, IsEnum, IsString } from 'class-validator'; +import { ENUM_PASSWORD_HISTORY_TYPE } from 'src/modules/password-history/enums/password-history.enum'; + +export class PasswordHistoryCreateRequestDto { + @ApiProperty({ + required: true, + enum: ENUM_PASSWORD_HISTORY_TYPE, + example: ENUM_PASSWORD_HISTORY_TYPE.TEMPORARY, + }) + @IsEnum(ENUM_PASSWORD_HISTORY_TYPE) + @IsString() + @IsEmpty() + type: ENUM_PASSWORD_HISTORY_TYPE; +} diff --git a/src/modules/password-history/dtos/response/password-history.list.response.dto.ts b/src/modules/password-history/dtos/response/password-history.list.response.dto.ts new file mode 100644 index 000000000..dc1b31b12 --- /dev/null +++ b/src/modules/password-history/dtos/response/password-history.list.response.dto.ts @@ -0,0 +1,43 @@ +import { faker } from '@faker-js/faker'; +import { + ApiHideProperty, + ApiProperty, + getSchemaPath, + IntersectionType, + PickType, +} from '@nestjs/swagger'; +import { Exclude, Type } from 'class-transformer'; +import { DatabaseDto } from 'src/common/database/dtos/database.dto'; +import { ENUM_PASSWORD_HISTORY_TYPE } from 'src/modules/password-history/enums/password-history.enum'; +import { SessionListResponseDto } from 'src/modules/session/dtos/response/session.list.response.dto'; +import { UserShortResponseDto } from 'src/modules/user/dtos/response/user.short.response.dto'; + +export class PasswordHistoryListResponseDto extends IntersectionType( + DatabaseDto, + PickType(SessionListResponseDto, ['expiredAt'] as const) +) { + @ApiProperty({ + required: true, + example: faker.string.uuid(), + }) + user: string; + + @ApiProperty({ + required: true, + type: UserShortResponseDto, + oneOf: [{ $ref: getSchemaPath(UserShortResponseDto) }], + }) + @Type(() => UserShortResponseDto) + by: UserShortResponseDto; + + @ApiProperty({ + required: true, + enum: ENUM_PASSWORD_HISTORY_TYPE, + example: ENUM_PASSWORD_HISTORY_TYPE.TEMPORARY, + }) + type: ENUM_PASSWORD_HISTORY_TYPE; + + @ApiHideProperty() + @Exclude() + password: string; +} diff --git a/src/modules/password-history/enums/password-history.enum.ts b/src/modules/password-history/enums/password-history.enum.ts new file mode 100644 index 000000000..b6ce60cff --- /dev/null +++ b/src/modules/password-history/enums/password-history.enum.ts @@ -0,0 +1,6 @@ +export enum ENUM_PASSWORD_HISTORY_TYPE { + SIGN_UP = 'SIGN_UP', + FORGOT = 'FORGOT', + TEMPORARY = 'TEMPORARY', + CHANGE = 'CHANGE', +} diff --git a/src/modules/password-history/interfaces/password-history.interface.ts b/src/modules/password-history/interfaces/password-history.interface.ts new file mode 100644 index 000000000..37c8e56ff --- /dev/null +++ b/src/modules/password-history/interfaces/password-history.interface.ts @@ -0,0 +1,17 @@ +import { + PasswordHistoryDoc, + PasswordHistoryEntity, +} from 'src/modules/password-history/repository/entities/password-history.entity'; +import { + UserDoc, + UserEntity, +} from 'src/modules/user/repository/entities/user.entity'; + +export interface IPasswordHistoryEntity + extends Omit { + by: UserEntity; +} + +export interface IPasswordHistoryDoc extends Omit { + by: UserDoc; +} diff --git a/src/modules/password-history/interfaces/password-history.service.interface.ts b/src/modules/password-history/interfaces/password-history.service.interface.ts new file mode 100644 index 000000000..7c5fe4407 --- /dev/null +++ b/src/modules/password-history/interfaces/password-history.service.interface.ts @@ -0,0 +1,72 @@ +import { + IDatabaseCreateOptions, + IDatabaseDeleteManyOptions, + IDatabaseFindAllOptions, + IDatabaseGetTotalOptions, + IDatabaseOptions, +} from 'src/common/database/interfaces/database.interface'; +import { PasswordHistoryCreateByAdminRequestDto } from 'src/modules/password-history/dtos/request/password-history.create-by-admin.request.dto'; +import { PasswordHistoryCreateRequestDto } from 'src/modules/password-history/dtos/request/password-history.create.request.dto'; +import { PasswordHistoryListResponseDto } from 'src/modules/password-history/dtos/response/password-history.list.response.dto'; +import { + IPasswordHistoryDoc, + IPasswordHistoryEntity, +} from 'src/modules/password-history/interfaces/password-history.interface'; +import { PasswordHistoryDoc } from 'src/modules/password-history/repository/entities/password-history.entity'; +import { UserDoc } from 'src/modules/user/repository/entities/user.entity'; + +export interface IPasswordHistoryService { + findAll( + find?: Record, + options?: IDatabaseFindAllOptions + ): Promise; + findAllByUser( + user: string, + find?: Record, + options?: IDatabaseFindAllOptions + ): Promise; + findOneById( + _id: string, + options?: IDatabaseOptions + ): Promise; + findOne( + find: Record, + options?: IDatabaseOptions + ): Promise; + findOneByUser( + user: string, + password: string, + options?: IDatabaseOptions + ): Promise; + findOneUsedByUser( + user: string, + password: string, + options?: IDatabaseOptions + ): Promise; + getTotal( + find?: Record, + options?: IDatabaseGetTotalOptions + ): Promise; + getTotalByUser( + user: string, + find?: Record, + options?: IDatabaseGetTotalOptions + ): Promise; + createByUser( + user: UserDoc, + { type }: PasswordHistoryCreateRequestDto, + options?: IDatabaseCreateOptions + ): Promise; + createByAdmin( + user: UserDoc, + { by, type }: PasswordHistoryCreateByAdminRequestDto, + options?: IDatabaseCreateOptions + ): Promise; + deleteMany( + find: Record, + options?: IDatabaseDeleteManyOptions + ): Promise; + mapList( + userHistories: IPasswordHistoryDoc[] | IPasswordHistoryEntity[] + ): Promise; +} diff --git a/src/modules/password-history/password-history.module.ts b/src/modules/password-history/password-history.module.ts new file mode 100644 index 000000000..43fad2c72 --- /dev/null +++ b/src/modules/password-history/password-history.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { PasswordHistoryRepositoryModule } from 'src/modules/password-history/repository/password-history.repository.module'; +import { PasswordHistoryService } from 'src/modules/password-history/services/password-history.service'; + +@Module({ + imports: [PasswordHistoryRepositoryModule], + exports: [PasswordHistoryService], + providers: [PasswordHistoryService], + controllers: [], +}) +export class PasswordHistoryModule {} diff --git a/src/modules/password-history/repository/entities/password-history.entity.ts b/src/modules/password-history/repository/entities/password-history.entity.ts new file mode 100644 index 000000000..69e93a6b2 --- /dev/null +++ b/src/modules/password-history/repository/entities/password-history.entity.ts @@ -0,0 +1,53 @@ +import { DatabaseEntityBase } from 'src/common/database/bases/database.entity'; +import { + DatabaseEntity, + DatabaseProp, + DatabaseSchema, +} from 'src/common/database/decorators/database.decorator'; +import { IDatabaseDocument } from 'src/common/database/interfaces/database.interface'; +import { ENUM_PASSWORD_HISTORY_TYPE } from 'src/modules/password-history/enums/password-history.enum'; +import { UserEntity } from 'src/modules/user/repository/entities/user.entity'; + +export const PasswordHistoryTableName = 'PasswordHistories'; + +@DatabaseEntity({ collection: PasswordHistoryTableName }) +export class PasswordHistoryEntity extends DatabaseEntityBase { + @DatabaseProp({ + required: true, + index: true, + trim: true, + type: String, + ref: UserEntity.name, + }) + user: string; + + @DatabaseProp({ + required: true, + type: String, + }) + password: string; + + @DatabaseProp({ + required: true, + type: String, + enum: ENUM_PASSWORD_HISTORY_TYPE, + }) + type: ENUM_PASSWORD_HISTORY_TYPE; + + @DatabaseProp({ + required: true, + type: Date, + }) + expiredAt: Date; + + @DatabaseProp({ + required: true, + trim: true, + type: String, + ref: UserEntity.name, + }) + by: string; +} + +export const PasswordHistorySchema = DatabaseSchema(PasswordHistoryEntity); +export type PasswordHistoryDoc = IDatabaseDocument; diff --git a/src/modules/password-history/repository/password-history.repository.module.ts b/src/modules/password-history/repository/password-history.repository.module.ts new file mode 100644 index 000000000..ee2558119 --- /dev/null +++ b/src/modules/password-history/repository/password-history.repository.module.ts @@ -0,0 +1,26 @@ +import { Module } from '@nestjs/common'; +import { MongooseModule } from '@nestjs/mongoose'; +import { DATABASE_CONNECTION_NAME } from 'src/common/database/constants/database.constant'; +import { + PasswordHistoryEntity, + PasswordHistorySchema, +} from 'src/modules/password-history/repository/entities/password-history.entity'; +import { PasswordHistoryRepository } from 'src/modules/password-history/repository/repositories/password-history.repository'; + +@Module({ + providers: [PasswordHistoryRepository], + exports: [PasswordHistoryRepository], + controllers: [], + imports: [ + MongooseModule.forFeature( + [ + { + name: PasswordHistoryEntity.name, + schema: PasswordHistorySchema, + }, + ], + DATABASE_CONNECTION_NAME + ), + ], +}) +export class PasswordHistoryRepositoryModule {} diff --git a/src/modules/password-history/repository/repositories/password-history.repository.ts b/src/modules/password-history/repository/repositories/password-history.repository.ts new file mode 100644 index 000000000..fa66149c0 --- /dev/null +++ b/src/modules/password-history/repository/repositories/password-history.repository.ts @@ -0,0 +1,53 @@ +import { Injectable } from '@nestjs/common'; +import { Model } from 'mongoose'; +import { DatabaseRepositoryBase } from 'src/common/database/bases/database.repository'; +import { InjectDatabaseModel } from 'src/common/database/decorators/database.decorator'; +import { CountryEntity } from 'src/modules/country/repository/entities/country.entity'; +import { + PasswordHistoryDoc, + PasswordHistoryEntity, +} from 'src/modules/password-history/repository/entities/password-history.entity'; +import { RoleEntity } from 'src/modules/role/repository/entities/role.entity'; +import { UserEntity } from 'src/modules/user/repository/entities/user.entity'; + +@Injectable() +export class PasswordHistoryRepository extends DatabaseRepositoryBase< + PasswordHistoryEntity, + PasswordHistoryDoc +> { + constructor( + @InjectDatabaseModel(PasswordHistoryEntity.name) + private readonly passwordHistoryModel: Model + ) { + super(passwordHistoryModel, { + path: 'by', + localField: 'by', + foreignField: '_id', + model: UserEntity.name, + justOne: true, + populate: [ + { + path: 'role', + localField: 'role', + foreignField: '_id', + model: RoleEntity.name, + justOne: true, + }, + { + path: 'country', + localField: 'country', + foreignField: '_id', + model: CountryEntity.name, + justOne: true, + }, + { + path: 'mobileNumber.country', + localField: 'mobileNumber.country', + foreignField: '_id', + model: CountryEntity.name, + justOne: true, + }, + ], + }); + } +} diff --git a/src/modules/password-history/services/password-history.service.ts b/src/modules/password-history/services/password-history.service.ts new file mode 100644 index 000000000..abae76215 --- /dev/null +++ b/src/modules/password-history/services/password-history.service.ts @@ -0,0 +1,211 @@ +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { plainToInstance } from 'class-transformer'; +import { Duration } from 'luxon'; +import { Document } from 'mongoose'; +import { + IDatabaseCreateOptions, + IDatabaseDeleteManyOptions, + IDatabaseFindAllOptions, + IDatabaseFindOneOptions, + IDatabaseGetTotalOptions, +} from 'src/common/database/interfaces/database.interface'; +import { HelperDateService } from 'src/common/helper/services/helper.date.service'; +import { PasswordHistoryCreateByAdminRequestDto } from 'src/modules/password-history/dtos/request/password-history.create-by-admin.request.dto'; +import { PasswordHistoryCreateRequestDto } from 'src/modules/password-history/dtos/request/password-history.create.request.dto'; +import { PasswordHistoryListResponseDto } from 'src/modules/password-history/dtos/response/password-history.list.response.dto'; +import { + IPasswordHistoryDoc, + IPasswordHistoryEntity, +} from 'src/modules/password-history/interfaces/password-history.interface'; +import { IPasswordHistoryService } from 'src/modules/password-history/interfaces/password-history.service.interface'; +import { + PasswordHistoryDoc, + PasswordHistoryEntity, +} from 'src/modules/password-history/repository/entities/password-history.entity'; +import { PasswordHistoryRepository } from 'src/modules/password-history/repository/repositories/password-history.repository'; +import { UserDoc } from 'src/modules/user/repository/entities/user.entity'; + +@Injectable() +export class PasswordHistoryService implements IPasswordHistoryService { + private readonly passwordPeriod: number; + + constructor( + private readonly configService: ConfigService, + private readonly helperDateService: HelperDateService, + private readonly passwordHistoryRepository: PasswordHistoryRepository + ) { + this.passwordPeriod = this.configService.get( + 'auth.password.period' + ); + } + + async findAll( + find?: Record, + options?: IDatabaseFindAllOptions + ): Promise { + return this.passwordHistoryRepository.findAll( + find, + { ...options, join: true } + ); + } + + async findAllByUser( + user: string, + find?: Record, + options?: IDatabaseFindAllOptions + ): Promise { + return this.passwordHistoryRepository.findAll( + { ...find, user }, + { ...options, join: true } + ); + } + + async findOneById( + _id: string, + options?: IDatabaseFindOneOptions + ): Promise { + return this.passwordHistoryRepository.findOneById( + _id, + options + ); + } + + async findOne( + find: Record, + options?: IDatabaseFindOneOptions + ): Promise { + return this.passwordHistoryRepository.findOne( + find, + options + ); + } + + async findOneByUser( + user: string, + password: string, + options?: IDatabaseFindOneOptions + ): Promise { + return this.passwordHistoryRepository.findOne( + { + user, + password, + }, + options + ); + } + + async findOneUsedByUser( + user: string, + password: string, + options?: IDatabaseFindOneOptions + ): Promise { + const today = this.helperDateService.create(); + + return this.passwordHistoryRepository.findOne( + { + user, + password, + expiredAt: { + $gte: today, + }, + }, + options + ); + } + + async getTotal( + find?: Record, + options?: IDatabaseGetTotalOptions + ): Promise { + return this.passwordHistoryRepository.getTotal(find, options); + } + + async getTotalByUser( + user: string, + find?: Record, + options?: IDatabaseGetTotalOptions + ): Promise { + return this.passwordHistoryRepository.getTotal( + { ...find, user }, + options + ); + } + + async createByUser( + user: UserDoc, + { type }: PasswordHistoryCreateRequestDto, + options?: IDatabaseCreateOptions + ): Promise { + const today = this.helperDateService.create(); + const expiredAt: Date = this.helperDateService.forward( + today, + Duration.fromObject({ + seconds: this.passwordPeriod, + }) + ); + + const create: PasswordHistoryEntity = new PasswordHistoryEntity(); + create.user = user._id; + create.by = user._id; + create.type = type; + create.password = user.password; + create.expiredAt = expiredAt; + + return this.passwordHistoryRepository.create( + create, + options + ); + } + + async createByAdmin( + user: UserDoc, + { by, type }: PasswordHistoryCreateByAdminRequestDto, + options?: IDatabaseCreateOptions + ): Promise { + const today = this.helperDateService.create(); + const expiredAt: Date = this.helperDateService.forward( + today, + Duration.fromObject({ + seconds: this.passwordPeriod, + }) + ); + + const create: PasswordHistoryEntity = new PasswordHistoryEntity(); + create.user = user._id; + create.by = by; + create.type = type; + create.password = user.password; + create.expiredAt = expiredAt; + + return this.passwordHistoryRepository.create( + create, + options + ); + } + + async deleteMany( + find: Record, + options?: IDatabaseDeleteManyOptions + ): Promise { + await this.passwordHistoryRepository.deleteMany(find, options); + + return true; + } + + async getPasswordPeriod(): Promise { + return this.passwordPeriod; + } + + async mapList( + userHistories: IPasswordHistoryDoc[] | IPasswordHistoryEntity[] + ): Promise { + return plainToInstance( + PasswordHistoryListResponseDto, + userHistories.map( + (e: IPasswordHistoryDoc | IPasswordHistoryEntity) => + e instanceof Document ? e.toObject() : e + ) + ); + } +} diff --git a/src/modules/policy/constants/policy.constant.ts b/src/modules/policy/constants/policy.constant.ts index d87b44eb3..84bd23cd0 100644 --- a/src/modules/policy/constants/policy.constant.ts +++ b/src/modules/policy/constants/policy.constant.ts @@ -1,2 +1,9 @@ +import { ENUM_POLICY_ROLE_TYPE } from 'src/modules/policy/enums/policy.enum'; + export const POLICY_ABILITY_META_KEY = 'PolicyAbilityMetaKey'; export const POLICY_ROLE_META_KEY = 'PolicyRoleMetaKey'; + +export const POLICY_ROLE_TYPE_USER_GROUP = [ + ENUM_POLICY_ROLE_TYPE.MEMBER, + ENUM_POLICY_ROLE_TYPE.USER, +]; diff --git a/src/modules/policy/enums/policy.enum.ts b/src/modules/policy/enums/policy.enum.ts index d24fecb64..3c63b93f9 100644 --- a/src/modules/policy/enums/policy.enum.ts +++ b/src/modules/policy/enums/policy.enum.ts @@ -26,10 +26,13 @@ export enum ENUM_POLICY_SUBJECT { COUNTRY = 'COUNTRY', ROLE = 'ROLE', USER = 'USER', + SESSION = 'SESSION', + ACTIVITY = 'ACTIVITY', } export enum ENUM_POLICY_ROLE_TYPE { SUPER_ADMIN = 'SUPER_ADMIN', ADMIN = 'ADMIN', USER = 'USER', + MEMBER = 'MEMBER', } diff --git a/src/modules/reset-password/reset-password.module.ts b/src/modules/reset-password/reset-password.module.ts index 94f9664fa..3d4dd5c3c 100644 --- a/src/modules/reset-password/reset-password.module.ts +++ b/src/modules/reset-password/reset-password.module.ts @@ -1 +1,2 @@ // TODO: RESET PASSWORD MODULE +// 5090 diff --git a/src/modules/role/controllers/role.admin.controller.ts b/src/modules/role/controllers/role.admin.controller.ts index 711c84567..a04a8c6f5 100644 --- a/src/modules/role/controllers/role.admin.controller.ts +++ b/src/modules/role/controllers/role.admin.controller.ts @@ -58,7 +58,6 @@ import { RoleIsActivePipe } from 'src/modules/role/pipes/role.is-active.pipe'; import { RoleParsePipe } from 'src/modules/role/pipes/role.parse.pipe'; import { RoleDoc } from 'src/modules/role/repository/entities/role.entity'; import { RoleService } from 'src/modules/role/services/role.service'; -import { UserService } from 'src/modules/user/services/user.service'; import { DatabaseIdResponseDto } from 'src/common/database/dtos/response/database.id.response.dto'; @ApiTags('modules.admin.role') @@ -69,8 +68,7 @@ import { DatabaseIdResponseDto } from 'src/common/database/dtos/response/databas export class RoleAdminController { constructor( private readonly paginationService: PaginationService, - private readonly roleService: RoleService, - private readonly userService: UserService + private readonly roleService: RoleService ) {} @RoleAdminListDoc() diff --git a/src/modules/role/dtos/request/role.create.request.dto.ts b/src/modules/role/dtos/request/role.create.request.dto.ts index 520fcde15..ba7e9f8c9 100644 --- a/src/modules/role/dtos/request/role.create.request.dto.ts +++ b/src/modules/role/dtos/request/role.create.request.dto.ts @@ -5,7 +5,6 @@ import { OmitType, PickType, } from '@nestjs/swagger'; -import { Type } from 'class-transformer'; import { IsString, IsNotEmpty, MaxLength, MinLength } from 'class-validator'; import { RoleUpdateRequestDto } from 'src/modules/role/dtos/request/role.update.request.dto'; @@ -22,6 +21,5 @@ export class RoleCreateRequestDto extends IntersectionType( @IsNotEmpty() @MinLength(3) @MaxLength(30) - @Type(() => String) name: string; } diff --git a/src/modules/role/dtos/request/role.update.request.dto.ts b/src/modules/role/dtos/request/role.update.request.dto.ts index 06f2dd5c0..ce34a8729 100644 --- a/src/modules/role/dtos/request/role.update.request.dto.ts +++ b/src/modules/role/dtos/request/role.update.request.dto.ts @@ -1,4 +1,4 @@ -import { ApiProperty } from '@nestjs/swagger'; +import { ApiProperty, getSchemaPath } from '@nestjs/swagger'; import { faker } from '@faker-js/faker'; import { IsArray, @@ -6,16 +6,11 @@ import { IsNotEmpty, IsOptional, IsString, - ValidateIf, ValidateNested, } from 'class-validator'; -import { Transform, Type } from 'class-transformer'; +import { Type } from 'class-transformer'; import { RolePermissionDto } from 'src/modules/role/dtos/role.permission.dto'; -import { - ENUM_POLICY_ACTION, - ENUM_POLICY_ROLE_TYPE, - ENUM_POLICY_SUBJECT, -} from 'src/modules/policy/enums/policy.enum'; +import { ENUM_POLICY_ROLE_TYPE } from 'src/modules/policy/enums/policy.enum'; export class RoleUpdateRequestDto { @ApiProperty({ @@ -26,13 +21,13 @@ export class RoleUpdateRequestDto { }) @IsString() @IsOptional() - @Type(() => String) description?: string; @ApiProperty({ description: 'Representative for role type', - example: 'ADMIN', + example: ENUM_POLICY_ROLE_TYPE.ADMIN, required: true, + enum: ENUM_POLICY_ROLE_TYPE, }) @IsEnum(ENUM_POLICY_ROLE_TYPE) @IsNotEmpty() @@ -42,22 +37,12 @@ export class RoleUpdateRequestDto { required: true, description: 'Permission list of role', isArray: true, - default: [], - example: [ - { - subject: ENUM_POLICY_SUBJECT.API_KEY, - action: [ENUM_POLICY_ACTION.MANAGE], - }, - ], type: RolePermissionDto, + oneOf: [{ $ref: getSchemaPath(RolePermissionDto) }], }) @Type(() => RolePermissionDto) @IsNotEmpty() - @IsArray() @ValidateNested() - @ValidateIf(e => e.type === ENUM_POLICY_ROLE_TYPE.ADMIN) - @Transform(({ value, obj }) => - obj.type !== ENUM_POLICY_ROLE_TYPE.ADMIN ? [] : value - ) + @IsArray() permissions: RolePermissionDto[]; } diff --git a/src/modules/role/dtos/response/role.get.response.dto.ts b/src/modules/role/dtos/response/role.get.response.dto.ts index a230b363b..cc3321958 100644 --- a/src/modules/role/dtos/response/role.get.response.dto.ts +++ b/src/modules/role/dtos/response/role.get.response.dto.ts @@ -1,5 +1,5 @@ import { faker } from '@faker-js/faker'; -import { ApiProperty } from '@nestjs/swagger'; +import { ApiProperty, getSchemaPath } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { DatabaseDto } from 'src/common/database/dtos/database.dto'; import { ENUM_POLICY_ROLE_TYPE } from 'src/modules/policy/enums/policy.enum'; @@ -35,15 +35,18 @@ export class RoleGetResponseDto extends DatabaseDto { example: ENUM_POLICY_ROLE_TYPE.ADMIN, required: true, nullable: false, + enum: ENUM_POLICY_ROLE_TYPE, }) type: ENUM_POLICY_ROLE_TYPE; @ApiProperty({ type: RolePermissionDto, + oneOf: [{ $ref: getSchemaPath(RolePermissionDto) }], required: true, nullable: false, + isArray: true, default: [], }) @Type(() => RolePermissionDto) - permissions: RolePermissionDto; + permissions: RolePermissionDto[]; } diff --git a/src/modules/role/interfaces/role.service.interface.ts b/src/modules/role/interfaces/role.service.interface.ts index 6f7a2badd..9fdb01e1c 100644 --- a/src/modules/role/interfaces/role.service.interface.ts +++ b/src/modules/role/interfaces/role.service.interface.ts @@ -2,12 +2,12 @@ import { IDatabaseCreateManyOptions, IDatabaseCreateOptions, IDatabaseDeleteManyOptions, - IDatabaseExistOptions, IDatabaseFindAllOptions, IDatabaseGetTotalOptions, IDatabaseOptions, IDatabaseSaveOptions, } from 'src/common/database/interfaces/database.interface'; +import { ENUM_POLICY_ROLE_TYPE } from 'src/modules/policy/enums/policy.enum'; import { RoleCreateRequestDto } from 'src/modules/role/dtos/request/role.create.request.dto'; import { RoleUpdateRequestDto } from 'src/modules/role/dtos/request/role.update.request.dto'; import { RoleGetResponseDto } from 'src/modules/role/dtos/response/role.get.response.dto'; @@ -35,6 +35,14 @@ export interface IRoleService { find?: Record, options?: IDatabaseGetTotalOptions ): Promise; + findAllActiveByType( + type: ENUM_POLICY_ROLE_TYPE, + options?: IDatabaseFindAllOptions + ): Promise; + findAllByTypes( + types: ENUM_POLICY_ROLE_TYPE[], + options?: IDatabaseFindAllOptions + ): Promise; findOneById(_id: string, options?: IDatabaseOptions): Promise; findOne( find: Record, @@ -45,10 +53,7 @@ export interface IRoleService { _id: string, options?: IDatabaseOptions ): Promise; - existByName( - name: string, - options?: IDatabaseExistOptions - ): Promise; + existByName(name: string, options?: IDatabaseOptions): Promise; create( { name, description, type, permissions }: RoleCreateRequestDto, options?: IDatabaseCreateOptions diff --git a/src/modules/role/repository/entities/role.entity.ts b/src/modules/role/repository/entities/role.entity.ts index ca52dbe2b..8872e065d 100644 --- a/src/modules/role/repository/entities/role.entity.ts +++ b/src/modules/role/repository/entities/role.entity.ts @@ -1,4 +1,4 @@ -import { DatabaseEntityAbstract } from 'src/common/database/abstracts/database.entity.abstract'; +import { DatabaseEntityBase } from 'src/common/database/bases/database.entity'; import { DatabaseEntity, DatabaseProp, @@ -14,7 +14,7 @@ import { export const RoleTableName = 'Roles'; @DatabaseEntity({ collection: RoleTableName }) -export class RoleEntity extends DatabaseEntityAbstract { +export class RoleEntity extends DatabaseEntityBase { @DatabaseProp({ required: true, index: true, diff --git a/src/modules/role/repository/entities/role.permission.entity.ts b/src/modules/role/repository/entities/role.permission.entity.ts index ef722a8de..fed1c0826 100644 --- a/src/modules/role/repository/entities/role.permission.entity.ts +++ b/src/modules/role/repository/entities/role.permission.entity.ts @@ -22,6 +22,7 @@ export class RolePermissionEntity { type: [String], enum: ENUM_POLICY_ACTION, default: [], + isArray: true, }) action: ENUM_POLICY_ACTION[]; } diff --git a/src/modules/role/repository/repositories/role.repository.ts b/src/modules/role/repository/repositories/role.repository.ts index 1fa2b1eb4..a00c27ed4 100644 --- a/src/modules/role/repository/repositories/role.repository.ts +++ b/src/modules/role/repository/repositories/role.repository.ts @@ -1,19 +1,19 @@ import { Injectable } from '@nestjs/common'; import { Model } from 'mongoose'; -import { DatabaseRepositoryAbstract } from 'src/common/database/abstracts/database.repository.abstract'; -import { DatabaseModel } from 'src/common/database/decorators/database.decorator'; +import { DatabaseRepositoryBase } from 'src/common/database/bases/database.repository'; +import { InjectDatabaseModel } from 'src/common/database/decorators/database.decorator'; import { RoleDoc, RoleEntity, } from 'src/modules/role/repository/entities/role.entity'; @Injectable() -export class RoleRepository extends DatabaseRepositoryAbstract< +export class RoleRepository extends DatabaseRepositoryBase< RoleEntity, RoleDoc > { constructor( - @DatabaseModel(RoleEntity.name) + @InjectDatabaseModel(RoleEntity.name) private readonly roleModel: Model ) { super(roleModel); diff --git a/src/modules/role/services/role.service.ts b/src/modules/role/services/role.service.ts index fa90196dd..4a2a00d6b 100644 --- a/src/modules/role/services/role.service.ts +++ b/src/modules/role/services/role.service.ts @@ -3,14 +3,15 @@ import { plainToInstance } from 'class-transformer'; import { Document } from 'mongoose'; import { IDatabaseCreateOptions, - IDatabaseExistOptions, IDatabaseFindAllOptions, IDatabaseGetTotalOptions, IDatabaseCreateManyOptions, IDatabaseSaveOptions, - IDatabaseOptions, IDatabaseDeleteManyOptions, + IDatabaseFindOneOptions, + IDatabaseOptions, } from 'src/common/database/interfaces/database.interface'; +import { ENUM_POLICY_ROLE_TYPE } from 'src/modules/policy/enums/policy.enum'; import { RoleCreateRequestDto } from 'src/modules/role/dtos/request/role.create.request.dto'; import { RoleUpdateRequestDto } from 'src/modules/role/dtos/request/role.update.request.dto'; import { RoleGetResponseDto } from 'src/modules/role/dtos/response/role.get.response.dto'; @@ -61,37 +62,58 @@ export class RoleService implements IRoleService { ); } + async findAllActiveByType( + type: ENUM_POLICY_ROLE_TYPE, + options?: IDatabaseFindAllOptions + ): Promise { + return this.roleRepository.findAll({ type, isActive: true }, options); + } + + async findAllByTypes( + types: ENUM_POLICY_ROLE_TYPE[], + options?: IDatabaseFindAllOptions + ): Promise { + return this.roleRepository.findAll( + { + type: { + $in: types, + }, + }, + options + ); + } + async findOneById( _id: string, - options?: IDatabaseOptions + options?: IDatabaseFindOneOptions ): Promise { return this.roleRepository.findOneById(_id, options); } async findOne( find: Record, - options?: IDatabaseOptions + options?: IDatabaseFindOneOptions ): Promise { return this.roleRepository.findOne(find, options); } async findOneByName( name: string, - options?: IDatabaseOptions + options?: IDatabaseFindOneOptions ): Promise { return this.roleRepository.findOne({ name }, options); } async findOneActiveById( _id: string, - options?: IDatabaseOptions + options?: IDatabaseFindOneOptions ): Promise { return this.roleRepository.findOne({ _id, isActive: true }, options); } async existByName( name: string, - options?: IDatabaseExistOptions + options?: IDatabaseOptions ): Promise { return this.roleRepository.exists( { @@ -149,38 +171,28 @@ export class RoleService implements IRoleService { find: Record, options?: IDatabaseDeleteManyOptions ): Promise { - try { - await this.roleRepository.deleteMany(find, options); + await this.roleRepository.deleteMany(find, options); - return true; - } catch (error: unknown) { - throw error; - } + return true; } async createMany( data: RoleCreateRequestDto[], options?: IDatabaseCreateManyOptions ): Promise { - try { - const create: RoleEntity[] = data.map( - ({ type, name, permissions }) => { - const entity: RoleEntity = new RoleEntity(); - entity.type = type; - entity.isActive = true; - entity.name = name; - entity.permissions = permissions; - - return entity; - } - ) as RoleEntity[]; - - await this.roleRepository.createMany(create, options); - - return true; - } catch (error: unknown) { - throw error; - } + const create: RoleEntity[] = data.map(({ type, name, permissions }) => { + const entity: RoleEntity = new RoleEntity(); + entity.type = type; + entity.isActive = true; + entity.name = name; + entity.permissions = permissions; + + return entity; + }) as RoleEntity[]; + + await this.roleRepository.createMany(create, options); + + return true; } async mapList( diff --git a/src/modules/session/constants/session.constant.ts b/src/modules/session/constants/session.constant.ts new file mode 100644 index 000000000..3d4a70720 --- /dev/null +++ b/src/modules/session/constants/session.constant.ts @@ -0,0 +1 @@ +export const SessionLoginPrefix = 'UserLogin'; diff --git a/src/modules/session/constants/session.doc.constant.ts b/src/modules/session/constants/session.doc.constant.ts new file mode 100644 index 000000000..65ee07d6f --- /dev/null +++ b/src/modules/session/constants/session.doc.constant.ts @@ -0,0 +1,11 @@ +import { faker } from '@faker-js/faker'; + +export const SessionDocParamsId = [ + { + name: 'session', + allowEmptyValue: false, + required: true, + type: 'string', + example: faker.string.uuid(), + }, +]; diff --git a/src/modules/session/controllers/session.admin.controller.ts b/src/modules/session/controllers/session.admin.controller.ts new file mode 100644 index 000000000..a5e2b830f --- /dev/null +++ b/src/modules/session/controllers/session.admin.controller.ts @@ -0,0 +1,120 @@ +import { + Controller, + Delete, + Get, + NotFoundException, + Param, +} from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; +import { PaginationQuery } from 'src/common/pagination/decorators/pagination.decorator'; +import { PaginationListDto } from 'src/common/pagination/dtos/pagination.list.dto'; +import { PaginationService } from 'src/common/pagination/services/pagination.service'; +import { RequestRequiredPipe } from 'src/common/request/pipes/request.required.pipe'; +import { + Response, + ResponsePaging, +} from 'src/common/response/decorators/response.decorator'; +import { IResponsePaging } from 'src/common/response/interfaces/response.interface'; +import { ApiKeyProtected } from 'src/modules/api-key/decorators/api-key.decorator'; +import { AuthJwtAccessProtected } from 'src/modules/auth/decorators/auth.jwt.decorator'; +import { + PolicyAbilityProtected, + PolicyRoleProtected, +} from 'src/modules/policy/decorators/policy.decorator'; +import { + ENUM_POLICY_ACTION, + ENUM_POLICY_ROLE_TYPE, + ENUM_POLICY_SUBJECT, +} from 'src/modules/policy/enums/policy.enum'; +import { + SessionAdminListDoc, + SessionAdminRevokeDoc, +} from 'src/modules/session/docs/session.admin.doc'; +import { SessionListResponseDto } from 'src/modules/session/dtos/response/session.list.response.dto'; +import { ENUM_SESSION_STATUS_CODE_ERROR } from 'src/modules/session/enums/session.status-code.enum'; +import { SessionActiveParsePipe } from 'src/modules/session/pipes/session.parse.pipe'; +import { SessionDoc } from 'src/modules/session/repository/entities/session.entity'; +import { SessionService } from 'src/modules/session/services/session.service'; +import { UserNotSelfPipe } from 'src/modules/user/pipes/user.not-self.pipe'; +import { UserParsePipe } from 'src/modules/user/pipes/user.parse.pipe'; +import { UserDoc } from 'src/modules/user/repository/entities/user.entity'; + +@ApiTags('modules.admin.session') +@Controller({ + version: '1', + path: '/session/:user', +}) +export class SessionAdminController { + constructor( + private readonly paginationService: PaginationService, + private readonly sessionService: SessionService + ) {} + + @SessionAdminListDoc() + @ResponsePaging('session.list') + @PolicyAbilityProtected({ + subject: ENUM_POLICY_SUBJECT.SESSION, + action: [ENUM_POLICY_ACTION.READ], + }) + @PolicyRoleProtected(ENUM_POLICY_ROLE_TYPE.ADMIN) + @AuthJwtAccessProtected() + @ApiKeyProtected() + @Get('/list') + async list( + @Param('user', RequestRequiredPipe, UserParsePipe) user: UserDoc, + @PaginationQuery() + { _search, _limit, _offset, _order }: PaginationListDto + ): Promise> { + const find: Record = { + ..._search, + }; + + const sessions: SessionDoc[] = await this.sessionService.findAllByUser( + user._id, + find, + { + paging: { + limit: _limit, + offset: _offset, + }, + order: _order, + } + ); + const total: number = await this.sessionService.getTotalByUser( + user._id, + find + ); + const totalPage: number = this.paginationService.totalPage( + total, + _limit + ); + + const mapped = await this.sessionService.mapList(sessions); + + return { + _pagination: { total, totalPage }, + data: mapped, + }; + } + + @SessionAdminRevokeDoc() + @Response('session.revoke') + @AuthJwtAccessProtected() + @ApiKeyProtected() + @Delete('/revoke/:session') + async revoke( + @Param('user', RequestRequiredPipe, UserParsePipe, UserNotSelfPipe) + user: UserDoc, + @Param('session', RequestRequiredPipe, SessionActiveParsePipe) + session: SessionDoc + ): Promise { + if (user._id !== session.user) { + throw new NotFoundException({ + statusCode: ENUM_SESSION_STATUS_CODE_ERROR.NOT_FOUND, + message: 'session.error.notFound', + }); + } + + await this.sessionService.updateRevoke(session); + } +} diff --git a/src/modules/session/controllers/session.shared.controller.ts b/src/modules/session/controllers/session.shared.controller.ts new file mode 100644 index 000000000..911c09203 --- /dev/null +++ b/src/modules/session/controllers/session.shared.controller.ts @@ -0,0 +1,105 @@ +import { + Controller, + Delete, + ForbiddenException, + Get, + Param, +} from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; +import { PaginationQuery } from 'src/common/pagination/decorators/pagination.decorator'; +import { PaginationListDto } from 'src/common/pagination/dtos/pagination.list.dto'; +import { PaginationService } from 'src/common/pagination/services/pagination.service'; +import { RequestRequiredPipe } from 'src/common/request/pipes/request.required.pipe'; +import { + Response, + ResponsePaging, +} from 'src/common/response/decorators/response.decorator'; +import { IResponsePaging } from 'src/common/response/interfaces/response.interface'; +import { ApiKeyProtected } from 'src/modules/api-key/decorators/api-key.decorator'; +import { + AuthJwtAccessProtected, + AuthJwtPayload, +} from 'src/modules/auth/decorators/auth.jwt.decorator'; +import { + SessionSharedListDoc, + SessionSharedRevokeDoc, +} from 'src/modules/session/docs/session.shared.doc'; +import { SessionListResponseDto } from 'src/modules/session/dtos/response/session.list.response.dto'; +import { ENUM_SESSION_STATUS_CODE_ERROR } from 'src/modules/session/enums/session.status-code.enum'; +import { SessionActiveByUserParsePipe } from 'src/modules/session/pipes/session.parse.pipe'; +import { SessionDoc } from 'src/modules/session/repository/entities/session.entity'; +import { SessionService } from 'src/modules/session/services/session.service'; + +@ApiTags('modules.shared.session') +@Controller({ + version: '1', + path: '/session', +}) +export class SessionSharedController { + constructor( + private readonly paginationService: PaginationService, + private readonly sessionService: SessionService + ) {} + + @SessionSharedListDoc() + @ResponsePaging('session.list') + @AuthJwtAccessProtected() + @ApiKeyProtected() + @Get('/list') + async list( + @AuthJwtPayload('_id') user: string, + @PaginationQuery() + { _search, _limit, _offset, _order }: PaginationListDto + ): Promise> { + const find: Record = { + ..._search, + }; + + const sessions: SessionDoc[] = await this.sessionService.findAllByUser( + user, + find, + { + paging: { + limit: _limit, + offset: _offset, + }, + order: _order, + } + ); + const total: number = await this.sessionService.getTotalByUser( + user, + find + ); + const totalPage: number = this.paginationService.totalPage( + total, + _limit + ); + + const mapped = await this.sessionService.mapList(sessions); + + return { + _pagination: { total, totalPage }, + data: mapped, + }; + } + + @SessionSharedRevokeDoc() + @Response('session.revoke') + @AuthJwtAccessProtected() + @ApiKeyProtected() + @Delete('/revoke/:session') + async revoke( + @Param('session', RequestRequiredPipe, SessionActiveByUserParsePipe) + session: SessionDoc, + @AuthJwtPayload('session') sessionFromRequest: string + ): Promise { + if (session._id === sessionFromRequest) { + throw new ForbiddenException({ + statusCode: ENUM_SESSION_STATUS_CODE_ERROR.FORBIDDEN_REVOKE, + message: 'session.error.forbiddenRevoke', + }); + } + + await this.sessionService.updateRevoke(session); + } +} diff --git a/src/modules/session/docs/session.admin.doc.ts b/src/modules/session/docs/session.admin.doc.ts new file mode 100644 index 000000000..bbde41be2 --- /dev/null +++ b/src/modules/session/docs/session.admin.doc.ts @@ -0,0 +1,47 @@ +import { applyDecorators } from '@nestjs/common'; +import { + Doc, + DocAuth, + DocGuard, + DocRequest, + DocResponse, + DocResponsePaging, +} from 'src/common/doc/decorators/doc.decorator'; +import { SessionDocParamsId } from 'src/modules/session/constants/session.doc.constant'; +import { SessionListResponseDto } from 'src/modules/session/dtos/response/session.list.response.dto'; +import { UserDocParamsId } from 'src/modules/user/constants/user.doc.constant'; + +export function SessionAdminListDoc(): MethodDecorator { + return applyDecorators( + Doc({ + summary: 'get all user sessions', + }), + DocRequest({ + params: UserDocParamsId, + }), + DocAuth({ + xApiKey: true, + jwtAccessToken: true, + }), + DocGuard({ role: true, policy: true }), + DocResponsePaging('session.list', { + dto: SessionListResponseDto, + }) + ); +} + +export function SessionAdminRevokeDoc(): MethodDecorator { + return applyDecorators( + Doc({ + summary: 'revoke user session', + }), + DocRequest({ + params: [...SessionDocParamsId, ...UserDocParamsId], + }), + DocAuth({ + xApiKey: true, + jwtAccessToken: true, + }), + DocResponse('session.revoke') + ); +} diff --git a/src/modules/session/docs/session.shared.doc.ts b/src/modules/session/docs/session.shared.doc.ts new file mode 100644 index 000000000..025ddbce5 --- /dev/null +++ b/src/modules/session/docs/session.shared.doc.ts @@ -0,0 +1,41 @@ +import { applyDecorators } from '@nestjs/common'; +import { + Doc, + DocAuth, + DocRequest, + DocResponse, + DocResponsePaging, +} from 'src/common/doc/decorators/doc.decorator'; +import { SessionDocParamsId } from 'src/modules/session/constants/session.doc.constant'; +import { SessionListResponseDto } from 'src/modules/session/dtos/response/session.list.response.dto'; + +export function SessionSharedListDoc(): MethodDecorator { + return applyDecorators( + Doc({ + summary: 'get all user sessions', + }), + DocAuth({ + xApiKey: true, + jwtAccessToken: true, + }), + DocResponsePaging('session.list', { + dto: SessionListResponseDto, + }) + ); +} + +export function SessionSharedRevokeDoc(): MethodDecorator { + return applyDecorators( + Doc({ + summary: 'revoke user session', + }), + DocRequest({ + params: SessionDocParamsId, + }), + DocAuth({ + xApiKey: true, + jwtAccessToken: true, + }), + DocResponse('session.revoke') + ); +} diff --git a/src/modules/session/dtos/request/session.create.request.dto.ts b/src/modules/session/dtos/request/session.create.request.dto.ts new file mode 100644 index 000000000..d8e9978a9 --- /dev/null +++ b/src/modules/session/dtos/request/session.create.request.dto.ts @@ -0,0 +1,13 @@ +import { faker } from '@faker-js/faker'; +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsUUID } from 'class-validator'; + +export class SessionCreateRequestDto { + @ApiProperty({ + example: faker.string.uuid(), + required: true, + }) + @IsNotEmpty() + @IsUUID() + user: string; +} diff --git a/src/modules/session/dtos/response/session.list.response.dto.ts b/src/modules/session/dtos/response/session.list.response.dto.ts new file mode 100644 index 000000000..ec1010a1f --- /dev/null +++ b/src/modules/session/dtos/response/session.list.response.dto.ts @@ -0,0 +1,89 @@ +import { faker } from '@faker-js/faker'; +import { ApiProperty } from '@nestjs/swagger'; +import { DatabaseDto } from 'src/common/database/dtos/database.dto'; +import { ENUM_SESSION_STATUS } from 'src/modules/session/enums/session.enum'; + +export class SessionListResponseDto extends DatabaseDto { + @ApiProperty({ + required: true, + example: faker.string.uuid(), + }) + user: string; + + @ApiProperty({ + description: 'Date expired at', + example: faker.date.recent(), + required: true, + nullable: false, + }) + expiredAt: Date; + + @ApiProperty({ + description: 'Date expired at', + example: faker.date.recent(), + required: false, + nullable: true, + }) + revokeAt?: Date; + + @ApiProperty({ + required: true, + enum: ENUM_SESSION_STATUS, + default: ENUM_SESSION_STATUS.ACTIVE, + }) + status: ENUM_SESSION_STATUS; + + @ApiProperty({ + required: true, + example: faker.internet.ipv4(), + }) + ip: string; + + @ApiProperty({ + required: true, + example: faker.internet.domainName(), + }) + hostname: string; + + @ApiProperty({ + required: true, + example: faker.internet.protocol(), + }) + protocol: string; + + @ApiProperty({ + required: true, + example: faker.internet.url(), + }) + originalUrl: string; + + @ApiProperty({ + required: true, + example: faker.internet.httpMethod(), + }) + method: string; + + @ApiProperty({ + required: false, + example: faker.internet.userAgent(), + }) + userAgent?: string; + + @ApiProperty({ + required: false, + example: faker.internet.ipv4(), + }) + xForwardedFor?: string; + + @ApiProperty({ + required: false, + example: faker.internet.ipv4(), + }) + xForwardedHost?: string; + + @ApiProperty({ + required: false, + example: faker.internet.protocol(), + }) + xForwardedPorto?: string; +} diff --git a/src/modules/session/dtos/session.worker.dto.ts b/src/modules/session/dtos/session.worker.dto.ts new file mode 100644 index 000000000..c5217676b --- /dev/null +++ b/src/modules/session/dtos/session.worker.dto.ts @@ -0,0 +1,14 @@ +import { faker } from '@faker-js/faker'; +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsString, IsUUID } from 'class-validator'; + +export class SessionWorkerDto { + @ApiProperty({ + required: true, + example: faker.string.uuid(), + }) + @IsString() + @IsNotEmpty() + @IsUUID() + session: string; +} diff --git a/src/modules/session/enums/session.enum.ts b/src/modules/session/enums/session.enum.ts new file mode 100644 index 000000000..53e0bbe13 --- /dev/null +++ b/src/modules/session/enums/session.enum.ts @@ -0,0 +1,8 @@ +export enum ENUM_SESSION_STATUS { + ACTIVE = 'ACTIVE', + REVOKED = 'REVOKED', +} + +export enum ENUM_SESSION_PROCESS { + REVOKE = 'REVOKE', +} diff --git a/src/modules/session/enums/session.status-code.enum.ts b/src/modules/session/enums/session.status-code.enum.ts new file mode 100644 index 000000000..021de8b05 --- /dev/null +++ b/src/modules/session/enums/session.status-code.enum.ts @@ -0,0 +1,5 @@ +export enum ENUM_SESSION_STATUS_CODE_ERROR { + NOT_FOUND = 5070, + EXPIRED = 5071, + FORBIDDEN_REVOKE = 5072, +} diff --git a/src/modules/session/interfaces/session.processor.interface.ts b/src/modules/session/interfaces/session.processor.interface.ts new file mode 100644 index 000000000..c731843a8 --- /dev/null +++ b/src/modules/session/interfaces/session.processor.interface.ts @@ -0,0 +1,3 @@ +export interface ISessionProcessor { + processDeleteLoginSession(session: string): Promise; +} diff --git a/src/modules/session/interfaces/session.service.interface.ts b/src/modules/session/interfaces/session.service.interface.ts new file mode 100644 index 000000000..6d7c4540b --- /dev/null +++ b/src/modules/session/interfaces/session.service.interface.ts @@ -0,0 +1,74 @@ +import { Request } from 'express'; +import { + IDatabaseCreateOptions, + IDatabaseDeleteManyOptions, + IDatabaseFindAllOptions, + IDatabaseGetTotalOptions, + IDatabaseOptions, + IDatabaseUpdateManyOptions, +} from 'src/common/database/interfaces/database.interface'; +import { SessionCreateRequestDto } from 'src/modules/session/dtos/request/session.create.request.dto'; +import { SessionListResponseDto } from 'src/modules/session/dtos/response/session.list.response.dto'; +import { + SessionDoc, + SessionEntity, +} from 'src/modules/session/repository/entities/session.entity'; +import { IUserDoc } from 'src/modules/user/interfaces/user.interface'; + +export interface ISessionService { + findAll( + find?: Record, + options?: IDatabaseFindAllOptions + ): Promise; + findAllByUser( + user: string, + find?: Record, + options?: IDatabaseFindAllOptions + ): Promise; + findOneById(_id: string, options?: IDatabaseOptions): Promise; + findOne( + find: Record, + options?: IDatabaseOptions + ): Promise; + findOneActiveById( + _id: string, + options?: IDatabaseOptions + ): Promise; + findOneActiveByIdAndUser( + _id: string, + user: string, + options?: IDatabaseOptions + ): Promise; + getTotal( + find?: Record, + options?: IDatabaseGetTotalOptions + ): Promise; + getTotalByUser( + user: string, + options?: IDatabaseGetTotalOptions + ): Promise; + create( + request: Request, + { user }: SessionCreateRequestDto, + options?: IDatabaseCreateOptions + ): Promise; + mapList( + userLogins: SessionDoc[] | SessionEntity[] + ): Promise; + findLoginSession(_id: string): Promise; + setLoginSession(user: IUserDoc, session: SessionDoc): Promise; + deleteLoginSession(_id: string): Promise; + resetLoginSession(): Promise; + updateRevoke( + repository: SessionDoc, + options?: IDatabaseOptions + ): Promise; + updateManyRevokeByUser( + user: string, + options?: IDatabaseUpdateManyOptions + ): Promise; + deleteMany( + find: Record, + options?: IDatabaseDeleteManyOptions + ): Promise; +} diff --git a/src/modules/session/pipes/session.parse.pipe.ts b/src/modules/session/pipes/session.parse.pipe.ts new file mode 100644 index 000000000..3775c17b3 --- /dev/null +++ b/src/modules/session/pipes/session.parse.pipe.ts @@ -0,0 +1,57 @@ +import { + Inject, + Injectable, + NotFoundException, + PipeTransform, + Scope, +} from '@nestjs/common'; +import { REQUEST } from '@nestjs/core'; +import { IRequestApp } from 'src/common/request/interfaces/request.interface'; +import { ENUM_SESSION_STATUS_CODE_ERROR } from 'src/modules/session/enums/session.status-code.enum'; +import { SessionDoc } from 'src/modules/session/repository/entities/session.entity'; +import { SessionService } from 'src/modules/session/services/session.service'; + +@Injectable() +export class SessionActiveParsePipe implements PipeTransform { + constructor( + @Inject(REQUEST) protected readonly request: IRequestApp, + private readonly sessionService: SessionService + ) {} + + async transform(value: string): Promise { + const session = await this.sessionService.findOneActiveById(value); + if (!session) { + throw new NotFoundException({ + statusCode: ENUM_SESSION_STATUS_CODE_ERROR.NOT_FOUND, + message: 'session.error.notFound', + }); + } + + return session; + } +} + +@Injectable({ scope: Scope.REQUEST }) +export class SessionActiveByUserParsePipe implements PipeTransform { + constructor( + @Inject(REQUEST) protected readonly request: IRequestApp, + private readonly sessionService: SessionService + ) {} + + async transform(value: string): Promise { + const { user } = this.request; + + const session = await this.sessionService.findOneActiveByIdAndUser( + value, + user._id + ); + if (!session) { + throw new NotFoundException({ + statusCode: ENUM_SESSION_STATUS_CODE_ERROR.NOT_FOUND, + message: 'session.error.notFound', + }); + } + + return session; + } +} diff --git a/src/modules/session/processors/session.processor.ts b/src/modules/session/processors/session.processor.ts new file mode 100644 index 000000000..2eb68114e --- /dev/null +++ b/src/modules/session/processors/session.processor.ts @@ -0,0 +1,52 @@ +import { Processor, WorkerHost } from '@nestjs/bullmq'; +import { Logger } from '@nestjs/common'; +import { Job } from 'bullmq'; +import { MessageService } from 'src/common/message/services/message.service'; +import { SessionWorkerDto } from 'src/modules/session/dtos/session.worker.dto'; +import { ENUM_SESSION_PROCESS } from 'src/modules/session/enums/session.enum'; +import { ISessionProcessor } from 'src/modules/session/interfaces/session.processor.interface'; +import { SessionService } from 'src/modules/session/services/session.service'; +import { ENUM_WORKER_QUEUES } from 'src/worker/enums/worker.enum'; + +@Processor(ENUM_WORKER_QUEUES.SESSION_QUEUE) +export class SessionProcessor extends WorkerHost implements ISessionProcessor { + private readonly logger = new Logger(SessionProcessor.name); + + constructor( + private readonly sessionService: SessionService, + private readonly messageService: MessageService + ) { + super(); + } + + async process(job: Job): Promise { + try { + const jobName = job.name; + switch (jobName) { + case ENUM_SESSION_PROCESS.REVOKE: + default: + await this.processDeleteLoginSession(job.data.session); + + break; + } + } catch (error: any) { + this.logger.error(error); + } + + return; + } + + async processDeleteLoginSession(session: string): Promise { + const checkSession = await this.sessionService.findOneById(session); + + if (!checkSession) { + throw new Error( + this.messageService.setMessage('session.error.notFound') + ); + } + + await this.sessionService.updateRevoke(checkSession); + + return; + } +} diff --git a/src/modules/session/repository/entities/session.entity.ts b/src/modules/session/repository/entities/session.entity.ts new file mode 100644 index 000000000..9aa57cf34 --- /dev/null +++ b/src/modules/session/repository/entities/session.entity.ts @@ -0,0 +1,110 @@ +import { DatabaseEntityBase } from 'src/common/database/bases/database.entity'; +import { + DatabaseEntity, + DatabaseProp, + DatabaseSchema, +} from 'src/common/database/decorators/database.decorator'; +import { IDatabaseDocument } from 'src/common/database/interfaces/database.interface'; +import { ENUM_SESSION_STATUS } from 'src/modules/session/enums/session.enum'; +import { UserEntity } from 'src/modules/user/repository/entities/user.entity'; + +export const SessionTableName = 'Sessions'; + +@DatabaseEntity({ collection: SessionTableName }) +export class SessionEntity extends DatabaseEntityBase { + @DatabaseProp({ + required: true, + index: true, + type: String, + enum: ENUM_SESSION_STATUS, + default: ENUM_SESSION_STATUS.ACTIVE, + }) + status: ENUM_SESSION_STATUS; + + @DatabaseProp({ + required: false, + type: Date, + }) + revokeAt?: Date; + + @DatabaseProp({ + required: true, + index: true, + trim: true, + type: String, + ref: UserEntity.name, + }) + user: string; + + @DatabaseProp({ + required: true, + trim: true, + type: String, + }) + ip: string; + + @DatabaseProp({ + required: true, + trim: true, + type: String, + }) + hostname: string; + + @DatabaseProp({ + required: true, + trim: true, + type: String, + }) + protocol: string; + + @DatabaseProp({ + required: true, + trim: true, + type: String, + }) + originalUrl: string; + + @DatabaseProp({ + required: true, + trim: true, + type: String, + }) + method: string; + + @DatabaseProp({ + required: false, + trim: true, + type: String, + }) + userAgent?: string; + + @DatabaseProp({ + required: false, + trim: true, + type: String, + }) + xForwardedFor?: string; + + @DatabaseProp({ + required: false, + trim: true, + type: String, + }) + xForwardedHost?: string; + + @DatabaseProp({ + required: false, + trim: true, + type: String, + }) + xForwardedPorto?: string; + + @DatabaseProp({ + required: true, + type: Date, + }) + expiredAt: Date; +} + +export const SessionSchema = DatabaseSchema(SessionEntity); +export type SessionDoc = IDatabaseDocument; diff --git a/src/modules/session/repository/repositories/session.repository.ts b/src/modules/session/repository/repositories/session.repository.ts new file mode 100644 index 000000000..12b9148aa --- /dev/null +++ b/src/modules/session/repository/repositories/session.repository.ts @@ -0,0 +1,21 @@ +import { Injectable } from '@nestjs/common'; +import { Model } from 'mongoose'; +import { DatabaseRepositoryBase } from 'src/common/database/bases/database.repository'; +import { InjectDatabaseModel } from 'src/common/database/decorators/database.decorator'; +import { + SessionDoc, + SessionEntity, +} from 'src/modules/session/repository/entities/session.entity'; + +@Injectable() +export class SessionRepository extends DatabaseRepositoryBase< + SessionEntity, + SessionDoc +> { + constructor( + @InjectDatabaseModel(SessionEntity.name) + private readonly sessionModel: Model + ) { + super(sessionModel); + } +} diff --git a/src/modules/session/repository/session.repository.module.ts b/src/modules/session/repository/session.repository.module.ts new file mode 100644 index 000000000..53ba7f3d5 --- /dev/null +++ b/src/modules/session/repository/session.repository.module.ts @@ -0,0 +1,26 @@ +import { Module } from '@nestjs/common'; +import { MongooseModule } from '@nestjs/mongoose'; +import { DATABASE_CONNECTION_NAME } from 'src/common/database/constants/database.constant'; +import { + SessionEntity, + SessionSchema, +} from 'src/modules/session/repository/entities/session.entity'; +import { SessionRepository } from 'src/modules/session/repository/repositories/session.repository'; + +@Module({ + providers: [SessionRepository], + exports: [SessionRepository], + controllers: [], + imports: [ + MongooseModule.forFeature( + [ + { + name: SessionEntity.name, + schema: SessionSchema, + }, + ], + DATABASE_CONNECTION_NAME + ), + ], +}) +export class SessionRepositoryModule {} diff --git a/src/modules/session/services/session.service.ts b/src/modules/session/services/session.service.ts new file mode 100644 index 000000000..5f50d16d2 --- /dev/null +++ b/src/modules/session/services/session.service.ts @@ -0,0 +1,268 @@ +import { InjectQueue } from '@nestjs/bullmq'; +import { CACHE_MANAGER } from '@nestjs/cache-manager'; +import { Inject, Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { Queue } from 'bullmq'; +import { Cache } from 'cache-manager'; +import { plainToInstance } from 'class-transformer'; +import { Request } from 'express'; +import { Duration } from 'luxon'; +import { Document } from 'mongoose'; +import { + IDatabaseCreateOptions, + IDatabaseDeleteManyOptions, + IDatabaseFindAllOptions, + IDatabaseFindOneOptions, + IDatabaseGetTotalOptions, + IDatabaseOptions, + IDatabaseUpdateManyOptions, +} from 'src/common/database/interfaces/database.interface'; +import { HelperDateService } from 'src/common/helper/services/helper.date.service'; +import { SessionLoginPrefix } from 'src/modules/session/constants/session.constant'; +import { SessionCreateRequestDto } from 'src/modules/session/dtos/request/session.create.request.dto'; +import { SessionListResponseDto } from 'src/modules/session/dtos/response/session.list.response.dto'; +import { + ENUM_SESSION_PROCESS, + ENUM_SESSION_STATUS, +} from 'src/modules/session/enums/session.enum'; +import { ISessionService } from 'src/modules/session/interfaces/session.service.interface'; +import { + SessionDoc, + SessionEntity, +} from 'src/modules/session/repository/entities/session.entity'; +import { SessionRepository } from 'src/modules/session/repository/repositories/session.repository'; +import { IUserDoc } from 'src/modules/user/interfaces/user.interface'; +import { ENUM_WORKER_QUEUES } from 'src/worker/enums/worker.enum'; + +@Injectable() +export class SessionService implements ISessionService { + private readonly refreshTokenExpiration: number; + private readonly appName: string; + + constructor( + @InjectQueue(ENUM_WORKER_QUEUES.SESSION_QUEUE) + private readonly sessionQueue: Queue, + @Inject(CACHE_MANAGER) private cacheManager: Cache, + private readonly configService: ConfigService, + private readonly helperDateService: HelperDateService, + private readonly sessionRepository: SessionRepository + ) { + this.refreshTokenExpiration = this.configService.get( + 'auth.jwt.refreshToken.expirationTime' + ); + this.appName = this.configService.get('app.name'); + } + + async findAll( + find?: Record, + options?: IDatabaseFindAllOptions + ): Promise { + return this.sessionRepository.findAll(find, options); + } + + async findAllByUser( + user: string, + find?: Record, + options?: IDatabaseFindAllOptions + ): Promise { + return this.sessionRepository.findAll( + { user, ...find }, + options + ); + } + + async findOneById( + _id: string, + options?: IDatabaseFindOneOptions + ): Promise { + return this.sessionRepository.findOneById(_id, options); + } + + async findOne( + find: Record, + options?: IDatabaseFindOneOptions + ): Promise { + return this.sessionRepository.findOne(find, options); + } + + async findOneActiveById( + _id: string, + options?: IDatabaseFindOneOptions + ): Promise { + const today = this.helperDateService.create(); + + return this.sessionRepository.findOne( + { + _id, + expiredAt: { + $gte: today, + }, + }, + options + ); + } + + async findOneActiveByIdAndUser( + _id: string, + user: string, + options?: IDatabaseFindOneOptions + ): Promise { + const today = this.helperDateService.create(); + + return this.sessionRepository.findOne( + { + _id, + user, + expiredAt: { + $gte: today, + }, + }, + options + ); + } + + async getTotal( + find?: Record, + options?: IDatabaseGetTotalOptions + ): Promise { + return this.sessionRepository.getTotal(find, options); + } + + async getTotalByUser( + user: string, + options?: IDatabaseGetTotalOptions + ): Promise { + return this.sessionRepository.getTotal({ user }, options); + } + + async create( + request: Request, + { user }: SessionCreateRequestDto, + options?: IDatabaseCreateOptions + ): Promise { + const today = this.helperDateService.create(); + const expiredAt: Date = this.helperDateService.forward( + today, + Duration.fromObject({ + seconds: this.refreshTokenExpiration, + }) + ); + + const create = new SessionEntity(); + create.user = user; + create.hostname = request.hostname; + create.ip = request.ip; + create.protocol = request.protocol; + create.originalUrl = request.originalUrl; + create.method = request.method; + + create.userAgent = request.headers['user-agent'] as string; + create.xForwardedFor = request.headers['x-forwarded-for'] as string; + create.xForwardedHost = request.headers['x-forwarded-host'] as string; + create.xForwardedPorto = request.headers['x-forwarded-porto'] as string; + + create.status = ENUM_SESSION_STATUS.ACTIVE; + create.expiredAt = expiredAt; + + return this.sessionRepository.create(create, options); + } + + async mapList( + userLogins: SessionDoc[] | SessionEntity[] + ): Promise { + return plainToInstance( + SessionListResponseDto, + userLogins.map((e: SessionDoc | SessionEntity) => + e instanceof Document ? e.toObject() : e + ) + ); + } + + async findLoginSession(_id: string): Promise { + return this.cacheManager.get( + `${this.appName}:${SessionLoginPrefix}:${_id}` + ); + } + + async setLoginSession(user: IUserDoc, session: SessionDoc): Promise { + const key = `${this.appName}:${SessionLoginPrefix}:${session._id}`; + + await this.cacheManager.set( + key, + { user: user._id }, + { ttl: this.refreshTokenExpiration } + ); + + await this.sessionQueue.add( + ENUM_SESSION_PROCESS.REVOKE, + { + session: session._id, + }, + { + jobId: key, + timestamp: session.createdAt.valueOf(), + delay: this.refreshTokenExpiration * 1000, + } + ); + + return; + } + + async deleteLoginSession(_id: string): Promise { + const key = `${this.appName}:${SessionLoginPrefix}:${_id}`; + await this.cacheManager.del(key); + + await this.sessionQueue.remove(key); + + return; + } + + async resetLoginSession(): Promise { + return this.cacheManager.reset(); + } + + async updateRevoke( + repository: SessionDoc, + options?: IDatabaseOptions + ): Promise { + await this.deleteLoginSession(repository._id); + + repository.status = ENUM_SESSION_STATUS.REVOKED; + repository.revokeAt = this.helperDateService.create(); + + return this.sessionRepository.save(repository, options); + } + + async updateManyRevokeByUser( + user: string, + options?: IDatabaseUpdateManyOptions + ): Promise { + const today = this.helperDateService.create(); + const sessions = await this.findAllByUser(user, undefined, options); + const promises = sessions.map(e => this.deleteLoginSession(e._id)); + + await Promise.all(promises); + + await this.sessionRepository.updateMany( + { + user, + }, + { + status: ENUM_SESSION_STATUS.REVOKED, + revokeAt: today, + }, + options + ); + + return true; + } + + async deleteMany( + find: Record, + options?: IDatabaseDeleteManyOptions + ): Promise { + await this.sessionRepository.deleteMany(find, options); + + return true; + } +} diff --git a/src/modules/session/session.module.ts b/src/modules/session/session.module.ts index 435c3cd9b..9d65ccef0 100644 --- a/src/modules/session/session.module.ts +++ b/src/modules/session/session.module.ts @@ -1 +1,18 @@ -// TODO: SESSION MODULE +import { BullModule } from '@nestjs/bullmq'; +import { Module } from '@nestjs/common'; +import { SessionRepositoryModule } from 'src/modules/session/repository/session.repository.module'; +import { SessionService } from 'src/modules/session/services/session.service'; +import { ENUM_WORKER_QUEUES } from 'src/worker/enums/worker.enum'; + +@Module({ + imports: [ + SessionRepositoryModule, + BullModule.registerQueueAsync({ + name: ENUM_WORKER_QUEUES.SESSION_QUEUE, + }), + ], + exports: [SessionService], + providers: [SessionService], + controllers: [], +}) +export class SessionModule {} diff --git a/src/modules/setting/constants/setting.doc.constant.ts b/src/modules/setting/constants/setting.doc.constant.ts deleted file mode 100644 index f5bab47d8..000000000 --- a/src/modules/setting/constants/setting.doc.constant.ts +++ /dev/null @@ -1,9 +0,0 @@ -export const SettingDocParamsId = [ - { - name: 'setting', - allowEmptyValue: false, - required: true, - type: 'string', - description: 'setting id', - }, -]; diff --git a/src/modules/setting/constants/setting.list.constant.ts b/src/modules/setting/constants/setting.list.constant.ts deleted file mode 100644 index 0421cb7d6..000000000 --- a/src/modules/setting/constants/setting.list.constant.ts +++ /dev/null @@ -1 +0,0 @@ -export const SETTING_DEFAULT_AVAILABLE_SEARCH = ['name']; diff --git a/src/modules/setting/controllers/setting.admin.controller.ts b/src/modules/setting/controllers/setting.admin.controller.ts deleted file mode 100644 index 6ac56a8df..000000000 --- a/src/modules/setting/controllers/setting.admin.controller.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { - BadRequestException, - Body, - Controller, - Get, - Param, - Put, -} from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; -import { PaginationQuery } from 'src/common/pagination/decorators/pagination.decorator'; -import { PaginationListDto } from 'src/common/pagination/dtos/pagination.list.dto'; -import { PaginationService } from 'src/common/pagination/services/pagination.service'; -import { RequestRequiredPipe } from 'src/common/request/pipes/request.required.pipe'; -import { - Response, - ResponsePaging, -} from 'src/common/response/decorators/response.decorator'; -import { - IResponse, - IResponsePaging, -} from 'src/common/response/interfaces/response.interface'; -import { ApiKeyProtected } from 'src/modules/api-key/decorators/api-key.decorator'; -import { AuthJwtAccessProtected } from 'src/modules/auth/decorators/auth.jwt.decorator'; -import { - ENUM_POLICY_ACTION, - ENUM_POLICY_ROLE_TYPE, - ENUM_POLICY_SUBJECT, -} from 'src/modules/policy/enums/policy.enum'; -import { - PolicyAbilityProtected, - PolicyRoleProtected, -} from 'src/modules/policy/decorators/policy.decorator'; -import { SETTING_DEFAULT_AVAILABLE_SEARCH } from 'src/modules/setting/constants/setting.list.constant'; -import { ENUM_SETTING_STATUS_CODE_ERROR } from 'src/modules/setting/enums/setting.status-code.enum'; -import { - SettingAdminGetDoc, - SettingAdminListDoc, - SettingAdminUpdateDoc, -} from 'src/modules/setting/docs/setting.admin.doc'; -import { SettingUpdateRequestDto } from 'src/modules/setting/dtos/request/setting.update.request.dto'; -import { SettingGetResponseDto } from 'src/modules/setting/dtos/response/setting.get.response.dto'; -import { SettingListResponseDto } from 'src/modules/setting/dtos/response/setting.list.response.dto'; -import { SettingParsePipe } from 'src/modules/setting/pipes/setting.parse.pipe'; -import { SettingDoc } from 'src/modules/setting/repository/entities/setting.entity'; -import { SettingService } from 'src/modules/setting/services/setting.service'; -import { DatabaseIdResponseDto } from 'src/common/database/dtos/response/database.id.response.dto'; - -@ApiTags('modules.admin.setting') -@Controller({ - version: '1', - path: '/setting', -}) -export class SettingAdminController { - constructor( - private readonly settingService: SettingService, - private readonly paginationService: PaginationService - ) {} - - @SettingAdminListDoc() - @ResponsePaging('setting.list') - @PolicyAbilityProtected({ - subject: ENUM_POLICY_SUBJECT.SETTING, - action: [ENUM_POLICY_ACTION.READ], - }) - @PolicyRoleProtected(ENUM_POLICY_ROLE_TYPE.ADMIN) - @AuthJwtAccessProtected() - @ApiKeyProtected() - @Get('/list') - async list( - @PaginationQuery({ - availableSearch: SETTING_DEFAULT_AVAILABLE_SEARCH, - }) - { _search, _limit, _offset, _order }: PaginationListDto - ): Promise> { - const find: Record = { - ..._search, - }; - - const settings: SettingDoc[] = await this.settingService.findAll(find, { - paging: { - limit: _limit, - offset: _offset, - }, - order: _order, - }); - const mapSettings: SettingListResponseDto[] = - await this.settingService.mapList(settings); - const total: number = await this.settingService.getTotal(find); - const totalPage: number = this.paginationService.totalPage( - total, - _limit - ); - - return { - _pagination: { total, totalPage }, - data: mapSettings, - }; - } - - @SettingAdminGetDoc() - @Response('setting.get') - @PolicyAbilityProtected({ - subject: ENUM_POLICY_SUBJECT.SETTING, - action: [ENUM_POLICY_ACTION.READ], - }) - @PolicyRoleProtected(ENUM_POLICY_ROLE_TYPE.ADMIN) - @AuthJwtAccessProtected() - @Get('/get/:setting') - async get( - @Param('setting', RequestRequiredPipe, SettingParsePipe) - setting: SettingDoc - ): Promise> { - const mapSetting = await this.settingService.mapGet(setting); - return { data: mapSetting }; - } - - @SettingAdminUpdateDoc() - @Response('setting.update') - @PolicyAbilityProtected({ - subject: ENUM_POLICY_SUBJECT.SETTING, - action: [ENUM_POLICY_ACTION.READ], - }) - @PolicyRoleProtected(ENUM_POLICY_ROLE_TYPE.ADMIN) - @AuthJwtAccessProtected() - @Put('/update/:setting') - async update( - @Param('setting', RequestRequiredPipe, SettingParsePipe) - setting: SettingDoc, - @Body() - body: SettingUpdateRequestDto - ): Promise> { - const check = this.settingService.checkValue(setting.type, body.value); - if (!check) { - throw new BadRequestException({ - statusCode: ENUM_SETTING_STATUS_CODE_ERROR.VALUE_NOT_ALLOWED, - message: 'setting.error.valueNotAllowed', - }); - } - - await this.settingService.update(setting, body); - - return { - data: { _id: setting._id }, - }; - } -} diff --git a/src/modules/setting/controllers/setting.system.controller.ts b/src/modules/setting/controllers/setting.system.controller.ts index 985eccec8..80270bf6e 100644 --- a/src/modules/setting/controllers/setting.system.controller.ts +++ b/src/modules/setting/controllers/setting.system.controller.ts @@ -1,17 +1,11 @@ import { Controller, Get } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { ApiKeySystemProtected } from 'src/modules/api-key/decorators/api-key.decorator'; -import { FILE_SIZE_IN_BYTES } from 'src/common/file/constants/file.constant'; -import { MessageService } from 'src/common/message/services/message.service'; import { Response } from 'src/common/response/decorators/response.decorator'; import { IResponse } from 'src/common/response/interfaces/response.interface'; import { SettingCoreResponseDto } from 'src/modules/setting/dtos/response/setting.core.response.dto'; -import { SettingFileResponseDto } from 'src/modules/setting/dtos/response/setting.file.response.dto'; -import { SettingLanguageResponseDto } from 'src/modules/setting/dtos/response/setting.language.response.dto'; -import { SettingTimezoneResponseDto } from 'src/modules/setting/dtos/response/setting.timezone.response.dto'; import { SettingService } from 'src/modules/setting/services/setting.service'; import { SettingSystemCoreDoc } from 'src/modules/setting/docs/setting.system.doc'; -import { ENUM_MESSAGE_LANGUAGE } from 'src/common/message/enums/message.enum'; @ApiTags('modules.system.setting') @Controller({ @@ -19,45 +13,17 @@ import { ENUM_MESSAGE_LANGUAGE } from 'src/common/message/enums/message.enum'; path: '/setting', }) export class SettingSystemController { - constructor( - private readonly messageService: MessageService, - private readonly settingService: SettingService - ) {} + constructor(private readonly settingService: SettingService) {} @SettingSystemCoreDoc() @Response('setting.core') @ApiKeySystemProtected() @Get('/core') async getUserMaxCertificate(): Promise> { - const availableLanguage: ENUM_MESSAGE_LANGUAGE[] = - this.messageService.getAvailableLanguages(); - const currentLanguage: ENUM_MESSAGE_LANGUAGE = - this.messageService.getLanguage(); - - const language: SettingLanguageResponseDto = { - language: currentLanguage, - availableLanguage, - }; - - const tz: string = await this.settingService.getTimezone(); - const timezoneOffset: string = - await this.settingService.getTimezoneOffset(); - - const timezone: SettingTimezoneResponseDto = { - timezone: tz, - timezoneOffset: timezoneOffset, - }; - - const file: SettingFileResponseDto = { - sizeInBytes: FILE_SIZE_IN_BYTES, - }; + const coreSetting = await this.settingService.core(); return { - data: { - timezone, - language, - file, - }, + data: coreSetting, }; } } diff --git a/src/modules/setting/docs/setting.admin.doc.ts b/src/modules/setting/docs/setting.admin.doc.ts deleted file mode 100644 index c2ca8f65b..000000000 --- a/src/modules/setting/docs/setting.admin.doc.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { HttpStatus, applyDecorators } from '@nestjs/common'; -import { - Doc, - DocAuth, - DocDefault, - DocErrorGroup, - DocRequest, - DocGuard, - DocResponse, - DocResponsePaging, -} from 'src/common/doc/decorators/doc.decorator'; -import { ENUM_DOC_REQUEST_BODY_TYPE } from 'src/common/doc/enums/doc.enum'; -import { SettingDocParamsId } from 'src/modules/setting/constants/setting.doc.constant'; -import { ENUM_SETTING_STATUS_CODE_ERROR } from 'src/modules/setting/enums/setting.status-code.enum'; -import { SettingUpdateRequestDto } from 'src/modules/setting/dtos/request/setting.update.request.dto'; -import { SettingGetResponseDto } from 'src/modules/setting/dtos/response/setting.get.response.dto'; -import { SettingListResponseDto } from 'src/modules/setting/dtos/response/setting.list.response.dto'; -import { DatabaseIdResponseDto } from 'src/common/database/dtos/response/database.id.response.dto'; - -export function SettingAdminListDoc(): MethodDecorator { - return applyDecorators( - Doc({ - summary: 'get list of settings', - }), - DocAuth({ xApiKey: true, jwtAccessToken: true }), - DocGuard({ role: true, policy: true }), - DocResponsePaging('setting.list', { - dto: SettingListResponseDto, - }) - ); -} - -export function SettingAdminGetDoc(): MethodDecorator { - return applyDecorators( - Doc({ summary: 'get detail a setting' }), - DocRequest({ - params: SettingDocParamsId, - }), - DocResponse('setting.get', { - dto: SettingGetResponseDto, - }), - DocAuth({ xApiKey: true, jwtAccessToken: true }), - DocGuard({ role: true, policy: true }), - DocErrorGroup([ - DocDefault({ - httpStatus: HttpStatus.NOT_FOUND, - statusCode: ENUM_SETTING_STATUS_CODE_ERROR.NOT_FOUND, - messagePath: 'setting.error.notFound', - }), - ]) - ); -} - -export function SettingAdminUpdateDoc(): MethodDecorator { - return applyDecorators( - Doc({ summary: 'update a setting' }), - DocRequest({ - params: SettingDocParamsId, - dto: SettingUpdateRequestDto, - bodyType: ENUM_DOC_REQUEST_BODY_TYPE.JSON, - }), - DocAuth({ - jwtAccessToken: true, - xApiKey: true, - }), - DocGuard({ role: true, policy: true }), - DocResponse('setting.update', { - dto: DatabaseIdResponseDto, - }), - DocErrorGroup([ - DocDefault({ - httpStatus: HttpStatus.NOT_FOUND, - statusCode: ENUM_SETTING_STATUS_CODE_ERROR.NOT_FOUND, - messagePath: 'setting.error.notFound', - }), - DocDefault({ - httpStatus: HttpStatus.BAD_REQUEST, - statusCode: ENUM_SETTING_STATUS_CODE_ERROR.VALUE_NOT_ALLOWED, - messagePath: 'setting.error.valueNotAllowed', - }), - ]) - ); -} diff --git a/src/modules/setting/dtos/request/setting.create.request.dto.ts b/src/modules/setting/dtos/request/setting.create.request.dto.ts deleted file mode 100644 index 83ec9e6e2..000000000 --- a/src/modules/setting/dtos/request/setting.create.request.dto.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { Type } from 'class-transformer'; -import { IsString, IsNotEmpty, IsOptional } from 'class-validator'; -import { SafeString } from 'src/common/request/validations/request.safe-string.validation'; -import { ENUM_SETTING_DATA_TYPE } from 'src/modules/setting/enums/setting.enum'; - -export class SettingCreateRequestDto { - @IsString() - @IsNotEmpty() - @SafeString() - @Type(() => String) - name: string; - - @IsString() - @IsOptional() - @Type(() => String) - @ApiProperty({ - name: 'description', - examples: ['Maintenance Mode', 'Max Part Number Aws Chunk File'], - description: 'The description about setting', - nullable: true, - }) - description?: string; - - @IsString() - @IsNotEmpty() - @ApiProperty({ - description: 'Data type of setting', - example: 'BOOLEAN', - required: true, - enum: ENUM_SETTING_DATA_TYPE, - }) - type: ENUM_SETTING_DATA_TYPE; - - @IsNotEmpty() - @Type(() => String) - @ApiProperty({ - name: 'value', - description: 'The value of setting', - nullable: false, - oneOf: [ - { type: 'string', readOnly: true, examples: ['on', 'off'] }, - { type: 'number', readOnly: true, examples: [100, 200] }, - { type: 'boolean', readOnly: true, examples: [true, false] }, - ], - }) - value: string; -} diff --git a/src/modules/setting/dtos/request/setting.update.request.dto.ts b/src/modules/setting/dtos/request/setting.update.request.dto.ts deleted file mode 100644 index 2bd44d7f2..000000000 --- a/src/modules/setting/dtos/request/setting.update.request.dto.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { PickType } from '@nestjs/swagger'; -import { SettingCreateRequestDto } from 'src/modules/setting/dtos/request/setting.create.request.dto'; - -export class SettingUpdateRequestDto extends PickType(SettingCreateRequestDto, [ - 'value', - 'description', -] as const) {} diff --git a/src/modules/setting/dtos/response/setting.auth.response.dto.ts b/src/modules/setting/dtos/response/setting.auth.response.dto.ts new file mode 100644 index 000000000..4df9df393 --- /dev/null +++ b/src/modules/setting/dtos/response/setting.auth.response.dto.ts @@ -0,0 +1,45 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { ENUM_SETTING_UNIT } from 'src/modules/setting/enums/setting.enum'; + +export class SettingAuthResponseDto { + @ApiProperty({ + required: true, + }) + passwordMaxAttempt: number; + + @ApiProperty({ + required: true, + }) + passwordExpiredIn: number; + + @ApiProperty({ + required: true, + example: ENUM_SETTING_UNIT.MILLISECOND, + enum: ENUM_SETTING_UNIT, + }) + passwordExpiredInUnit: ENUM_SETTING_UNIT; + + @ApiProperty({ + required: true, + }) + passwordExpiredInTemporary: number; + + @ApiProperty({ + required: true, + example: ENUM_SETTING_UNIT.MILLISECOND, + enum: ENUM_SETTING_UNIT, + }) + passwordExpiredInTemporaryUnit: ENUM_SETTING_UNIT; + + @ApiProperty({ + required: true, + }) + passwordPeriod: number; + + @ApiProperty({ + required: true, + example: ENUM_SETTING_UNIT.MILLISECOND, + enum: ENUM_SETTING_UNIT, + }) + passwordPeriodUnit: ENUM_SETTING_UNIT; +} diff --git a/src/modules/setting/dtos/response/setting.core.response.dto.ts b/src/modules/setting/dtos/response/setting.core.response.dto.ts index 86678266c..7c6f02a33 100644 --- a/src/modules/setting/dtos/response/setting.core.response.dto.ts +++ b/src/modules/setting/dtos/response/setting.core.response.dto.ts @@ -1,10 +1,39 @@ import { ApiProperty, getSchemaPath } from '@nestjs/swagger'; import { Type } from 'class-transformer'; +import { ENUM_APP_ENVIRONMENT } from 'src/app/enums/app.enum'; +import { SettingAuthResponseDto } from 'src/modules/setting/dtos/response/setting.auth.response.dto'; import { SettingFileResponseDto } from 'src/modules/setting/dtos/response/setting.file.response.dto'; import { SettingLanguageResponseDto } from 'src/modules/setting/dtos/response/setting.language.response.dto'; +import { SettingMiddlewareResponseDto } from 'src/modules/setting/dtos/response/setting.middleware.response.dto'; import { SettingTimezoneResponseDto } from 'src/modules/setting/dtos/response/setting.timezone.response.dto'; +import { SettingUserResponseDto } from 'src/modules/setting/dtos/response/setting.user.response.dto'; +import { ENUM_SETTING_UNIT } from 'src/modules/setting/enums/setting.enum'; export class SettingCoreResponseDto { + @ApiProperty({ + required: true, + }) + name: string; + + @ApiProperty({ + required: true, + enum: ENUM_APP_ENVIRONMENT, + example: ENUM_APP_ENVIRONMENT.DEVELOPMENT, + }) + env: ENUM_APP_ENVIRONMENT; + + @ApiProperty({ + required: true, + }) + timeout: number; + + @ApiProperty({ + required: true, + example: ENUM_SETTING_UNIT.MILLISECOND, + enum: ENUM_SETTING_UNIT, + }) + timeoutUnit: ENUM_SETTING_UNIT; + @ApiProperty({ required: true, type: SettingFileResponseDto, @@ -28,4 +57,28 @@ export class SettingCoreResponseDto { }) @Type(() => SettingTimezoneResponseDto) timezone: SettingTimezoneResponseDto; + + @ApiProperty({ + required: true, + type: SettingMiddlewareResponseDto, + oneOf: [{ $ref: getSchemaPath(SettingMiddlewareResponseDto) }], + }) + @Type(() => SettingMiddlewareResponseDto) + middleware: SettingMiddlewareResponseDto; + + @ApiProperty({ + required: true, + type: SettingAuthResponseDto, + oneOf: [{ $ref: getSchemaPath(SettingAuthResponseDto) }], + }) + @Type(() => SettingAuthResponseDto) + auth: SettingAuthResponseDto; + + @ApiProperty({ + required: true, + type: SettingUserResponseDto, + oneOf: [{ $ref: getSchemaPath(SettingUserResponseDto) }], + }) + @Type(() => SettingUserResponseDto) + user: SettingUserResponseDto; } diff --git a/src/modules/setting/dtos/response/setting.file.response.dto.ts b/src/modules/setting/dtos/response/setting.file.response.dto.ts index 8dc465738..164389c68 100644 --- a/src/modules/setting/dtos/response/setting.file.response.dto.ts +++ b/src/modules/setting/dtos/response/setting.file.response.dto.ts @@ -1,8 +1,18 @@ +import { faker } from '@faker-js/faker'; import { ApiProperty } from '@nestjs/swagger'; +import { ENUM_SETTING_UNIT } from 'src/modules/setting/enums/setting.enum'; export class SettingFileResponseDto { @ApiProperty({ required: true, + example: faker.number.int(), }) - sizeInBytes: number; + size: number; + + @ApiProperty({ + required: true, + example: ENUM_SETTING_UNIT.BYTE, + enum: ENUM_SETTING_UNIT, + }) + sizeUnit: ENUM_SETTING_UNIT; } diff --git a/src/modules/setting/dtos/response/setting.get.response.dto.ts b/src/modules/setting/dtos/response/setting.get.response.dto.ts deleted file mode 100644 index ca9a053ef..000000000 --- a/src/modules/setting/dtos/response/setting.get.response.dto.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { DatabaseDto } from 'src/common/database/dtos/database.dto'; -import { ENUM_SETTING_DATA_TYPE } from 'src/modules/setting/enums/setting.enum'; - -export class SettingGetResponseDto extends DatabaseDto { - @ApiProperty({ - description: 'Name of setting', - example: 'MaintenanceOn', - required: true, - nullable: false, - }) - name: string; - - @ApiProperty({ - description: 'Description of setting', - example: 'Maintenance Mode', - required: false, - nullable: true, - }) - description?: string; - - @ApiProperty({ - description: 'Data type of setting', - example: 'BOOLEAN', - required: true, - nullable: false, - enum: ENUM_SETTING_DATA_TYPE, - }) - type: ENUM_SETTING_DATA_TYPE; - - @ApiProperty({ - description: 'Value of string, can be type string/boolean/number', - oneOf: [ - { type: 'string', readOnly: true, examples: ['on', 'off'] }, - { type: 'number', readOnly: true, examples: [100, 200] }, - { type: 'boolean', readOnly: true, examples: [true, false] }, - ], - required: true, - nullable: false, - }) - value: T; -} diff --git a/src/modules/setting/dtos/response/setting.list.response.dto.ts b/src/modules/setting/dtos/response/setting.list.response.dto.ts deleted file mode 100644 index cc5aa6bb3..000000000 --- a/src/modules/setting/dtos/response/setting.list.response.dto.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { SettingGetResponseDto } from 'src/modules/setting/dtos/response/setting.get.response.dto'; - -export class SettingListResponseDto extends SettingGetResponseDto {} diff --git a/src/modules/setting/dtos/response/setting.middleware.response.dto.ts b/src/modules/setting/dtos/response/setting.middleware.response.dto.ts new file mode 100644 index 000000000..81ab4f140 --- /dev/null +++ b/src/modules/setting/dtos/response/setting.middleware.response.dto.ts @@ -0,0 +1,57 @@ +import { faker } from '@faker-js/faker'; +import { ApiProperty } from '@nestjs/swagger'; +import { ENUM_SETTING_UNIT } from 'src/modules/setting/enums/setting.enum'; + +export class SettingMiddlewareResponseDto { + @ApiProperty({ + required: true, + example: faker.number.int(), + }) + bodyJson: number; + + @ApiProperty({ + required: true, + example: ENUM_SETTING_UNIT.BYTE, + enum: ENUM_SETTING_UNIT, + }) + bodyJsonUnit: ENUM_SETTING_UNIT; + + @ApiProperty({ + required: true, + example: faker.number.int(), + }) + bodyRaw: number; + + @ApiProperty({ + required: true, + example: ENUM_SETTING_UNIT.BYTE, + enum: ENUM_SETTING_UNIT, + }) + bodyRawUnit: ENUM_SETTING_UNIT; + + @ApiProperty({ + required: true, + example: faker.number.int(), + }) + bodyText: number; + + @ApiProperty({ + required: true, + example: ENUM_SETTING_UNIT.BYTE, + enum: ENUM_SETTING_UNIT, + }) + bodyTextUnit: ENUM_SETTING_UNIT; + + @ApiProperty({ + required: true, + example: faker.number.int(), + }) + bodyUrlencoded: number; + + @ApiProperty({ + required: true, + example: ENUM_SETTING_UNIT.BYTE, + enum: ENUM_SETTING_UNIT, + }) + bodyUrlencodedUnit: ENUM_SETTING_UNIT; +} diff --git a/src/modules/setting/dtos/response/setting.timezone.response.dto.ts b/src/modules/setting/dtos/response/setting.timezone.response.dto.ts index 1fb8b372d..a5f570cfa 100644 --- a/src/modules/setting/dtos/response/setting.timezone.response.dto.ts +++ b/src/modules/setting/dtos/response/setting.timezone.response.dto.ts @@ -1,13 +1,16 @@ +import { faker } from '@faker-js/faker'; import { ApiProperty } from '@nestjs/swagger'; export class SettingTimezoneResponseDto { @ApiProperty({ required: true, + example: faker.date.timeZone(), }) timezone: string; @ApiProperty({ required: true, + example: `+0${faker.number.int({ min: 0, max: 12 })}:00`, }) timezoneOffset: string; } diff --git a/src/modules/setting/dtos/response/setting.user.response.dto.ts b/src/modules/setting/dtos/response/setting.user.response.dto.ts new file mode 100644 index 000000000..4d83bef22 --- /dev/null +++ b/src/modules/setting/dtos/response/setting.user.response.dto.ts @@ -0,0 +1,10 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class SettingUserResponseDto { + @ApiProperty({ + required: true, + nullable: false, + example: 'user', + }) + usernamePrefix: string; +} diff --git a/src/modules/setting/enums/setting.enum.ts b/src/modules/setting/enums/setting.enum.ts index eb5edc675..888a77ab8 100644 --- a/src/modules/setting/enums/setting.enum.ts +++ b/src/modules/setting/enums/setting.enum.ts @@ -1,5 +1,4 @@ -export enum ENUM_SETTING_DATA_TYPE { - BOOLEAN = 'BOOLEAN', - STRING = 'STRING', - NUMBER = 'NUMBER', +export enum ENUM_SETTING_UNIT { + MILLISECOND = 'millisecond', + BYTE = 'byte', } diff --git a/src/modules/setting/enums/setting.status-code.enum.ts b/src/modules/setting/enums/setting.status-code.enum.ts deleted file mode 100644 index 6436660f5..000000000 --- a/src/modules/setting/enums/setting.status-code.enum.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum ENUM_SETTING_STATUS_CODE_ERROR { - NOT_FOUND = 5130, - VALUE_NOT_ALLOWED = 5131, -} diff --git a/src/modules/setting/interfaces/setting.service.interface.ts b/src/modules/setting/interfaces/setting.service.interface.ts index d048233e1..30f9508c3 100644 --- a/src/modules/setting/interfaces/setting.service.interface.ts +++ b/src/modules/setting/interfaces/setting.service.interface.ts @@ -1,55 +1,5 @@ -import { - IDatabaseCreateOptions, - IDatabaseDeleteManyOptions, - IDatabaseFindAllOptions, - IDatabaseGetTotalOptions, - IDatabaseOptions, - IDatabaseSaveOptions, -} from 'src/common/database/interfaces/database.interface'; -import { ENUM_SETTING_DATA_TYPE } from 'src/modules/setting/enums/setting.enum'; -import { SettingCreateRequestDto } from 'src/modules/setting/dtos/request/setting.create.request.dto'; -import { SettingUpdateRequestDto } from 'src/modules/setting/dtos/request/setting.update.request.dto'; -import { SettingGetResponseDto } from 'src/modules/setting/dtos/response/setting.get.response.dto'; -import { SettingListResponseDto } from 'src/modules/setting/dtos/response/setting.list.response.dto'; -import { SettingDoc } from 'src/modules/setting/repository/entities/setting.entity'; +import { SettingCoreResponseDto } from 'src/modules/setting/dtos/response/setting.core.response.dto'; export interface ISettingService { - findAll( - find?: Record, - options?: IDatabaseFindAllOptions - ): Promise; - findOne( - find: Record, - options?: IDatabaseOptions - ): Promise; - findOneById(_id: string, options?: IDatabaseOptions): Promise; - findOneByName( - name: string, - options?: IDatabaseOptions - ): Promise; - getTotal( - find?: Record, - options?: IDatabaseGetTotalOptions - ): Promise; - create( - { name, description, type, value }: SettingCreateRequestDto, - options?: IDatabaseCreateOptions - ): Promise; - update( - repository: SettingDoc, - { description, value }: SettingUpdateRequestDto, - options?: IDatabaseSaveOptions - ): Promise; - deleteMany( - find: Record, - options?: IDatabaseDeleteManyOptions - ): Promise; - getValue(type: ENUM_SETTING_DATA_TYPE, value: string): T; - checkValue(type: ENUM_SETTING_DATA_TYPE, value: string): boolean; - getTimezone(): Promise; - getTimezoneOffset(): Promise; - mapList( - settings: SettingDoc[] - ): Promise[]>; - mapGet(setting: SettingDoc): Promise>; + core(): Promise; } diff --git a/src/modules/setting/pipes/setting.parse.pipe.ts b/src/modules/setting/pipes/setting.parse.pipe.ts deleted file mode 100644 index 3d44bbd26..000000000 --- a/src/modules/setting/pipes/setting.parse.pipe.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Injectable, NotFoundException, PipeTransform } from '@nestjs/common'; -import { ENUM_SETTING_STATUS_CODE_ERROR } from 'src/modules/setting/enums/setting.status-code.enum'; -import { SettingDoc } from 'src/modules/setting/repository/entities/setting.entity'; -import { SettingService } from 'src/modules/setting/services/setting.service'; - -@Injectable() -export class SettingParsePipe implements PipeTransform { - constructor(private readonly settingService: SettingService) {} - - async transform(value: any): Promise { - const setting: SettingDoc = - await this.settingService.findOneById(value); - if (!setting) { - throw new NotFoundException({ - statusCode: ENUM_SETTING_STATUS_CODE_ERROR.NOT_FOUND, - message: 'setting.error.notFound', - }); - } - - return setting; - } -} diff --git a/src/modules/setting/repository/entities/setting.entity.ts b/src/modules/setting/repository/entities/setting.entity.ts deleted file mode 100644 index cc48f06c3..000000000 --- a/src/modules/setting/repository/entities/setting.entity.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { DatabaseEntityAbstract } from 'src/common/database/abstracts/database.entity.abstract'; -import { - DatabaseEntity, - DatabaseProp, - DatabaseSchema, -} from 'src/common/database/decorators/database.decorator'; -import { ENUM_SETTING_DATA_TYPE } from 'src/modules/setting/enums/setting.enum'; -import { IDatabaseDocument } from 'src/common/database/interfaces/database.interface'; - -export const SettingTableName = 'Settings'; - -@DatabaseEntity({ collection: SettingTableName }) -export class SettingEntity extends DatabaseEntityAbstract { - @DatabaseProp({ - required: true, - index: true, - unique: true, - trim: true, - type: String, - }) - name: string; - - @DatabaseProp({ - required: false, - type: String, - }) - description?: string; - - @DatabaseProp({ - required: false, - type: String, - enum: ENUM_SETTING_DATA_TYPE, - }) - type: ENUM_SETTING_DATA_TYPE; - - @DatabaseProp({ - required: true, - trim: true, - type: String, - }) - value: string; -} - -export const SettingSchema = DatabaseSchema(SettingEntity); -export type SettingDoc = IDatabaseDocument; diff --git a/src/modules/setting/repository/repositories/setting.repository.ts b/src/modules/setting/repository/repositories/setting.repository.ts deleted file mode 100644 index fa3f3ce91..000000000 --- a/src/modules/setting/repository/repositories/setting.repository.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { Model } from 'mongoose'; -import { DatabaseRepositoryAbstract } from 'src/common/database/abstracts/database.repository.abstract'; -import { DatabaseModel } from 'src/common/database/decorators/database.decorator'; -import { - SettingDoc, - SettingEntity, -} from 'src/modules/setting/repository/entities/setting.entity'; - -@Injectable() -export class SettingRepository extends DatabaseRepositoryAbstract< - SettingEntity, - SettingDoc -> { - constructor( - @DatabaseModel(SettingEntity.name) - private readonly settingModel: Model - ) { - super(settingModel); - } -} diff --git a/src/modules/setting/repository/setting.repository.module.ts b/src/modules/setting/repository/setting.repository.module.ts deleted file mode 100644 index 48da28e52..000000000 --- a/src/modules/setting/repository/setting.repository.module.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Module } from '@nestjs/common'; -import { MongooseModule } from '@nestjs/mongoose'; -import { DATABASE_CONNECTION_NAME } from 'src/common/database/constants/database.constant'; -import { - SettingEntity, - SettingSchema, -} from 'src/modules/setting/repository/entities/setting.entity'; -import { SettingRepository } from 'src/modules/setting/repository/repositories/setting.repository'; - -@Module({ - providers: [SettingRepository], - exports: [SettingRepository], - controllers: [], - imports: [ - MongooseModule.forFeature( - [ - { - name: SettingEntity.name, - schema: SettingSchema, - }, - ], - DATABASE_CONNECTION_NAME - ), - ], -}) -export class SettingRepositoryModule {} diff --git a/src/modules/setting/services/setting.service.ts b/src/modules/setting/services/setting.service.ts index bf8b5e5ad..5a0e17d10 100644 --- a/src/modules/setting/services/setting.service.ts +++ b/src/modules/setting/services/setting.service.ts @@ -1,178 +1,123 @@ import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; -import { - IDatabaseCreateOptions, - IDatabaseDeleteManyOptions, - IDatabaseFindAllOptions, - IDatabaseGetTotalOptions, - IDatabaseOptions, - IDatabaseSaveOptions, -} from 'src/common/database/interfaces/database.interface'; -import { ENUM_HELPER_DATE_FORMAT } from 'src/common/helper/enums/helper.enum'; import { HelperDateService } from 'src/common/helper/services/helper.date.service'; -import { HelperNumberService } from 'src/common/helper/services/helper.number.service'; -import { ENUM_SETTING_DATA_TYPE } from 'src/modules/setting/enums/setting.enum'; -import { SettingCreateRequestDto } from 'src/modules/setting/dtos/request/setting.create.request.dto'; -import { SettingUpdateRequestDto } from 'src/modules/setting/dtos/request/setting.update.request.dto'; -import { SettingGetResponseDto } from 'src/modules/setting/dtos/response/setting.get.response.dto'; -import { SettingListResponseDto } from 'src/modules/setting/dtos/response/setting.list.response.dto'; +import { ENUM_SETTING_UNIT } from 'src/modules/setting/enums/setting.enum'; +import { SettingCoreResponseDto } from 'src/modules/setting/dtos/response/setting.core.response.dto'; +import { ENUM_MESSAGE_LANGUAGE } from 'src/common/message/enums/message.enum'; +import { SettingLanguageResponseDto } from 'src/modules/setting/dtos/response/setting.language.response.dto'; +import { SettingTimezoneResponseDto } from 'src/modules/setting/dtos/response/setting.timezone.response.dto'; +import { FILE_SIZE_IN_BYTES } from 'src/common/file/constants/file.constant'; +import { SettingFileResponseDto } from 'src/modules/setting/dtos/response/setting.file.response.dto'; +import { ENUM_APP_ENVIRONMENT } from 'src/app/enums/app.enum'; +import { SettingAuthResponseDto } from 'src/modules/setting/dtos/response/setting.auth.response.dto'; +import { SettingMiddlewareResponseDto } from 'src/modules/setting/dtos/response/setting.middleware.response.dto'; +import { SettingUserResponseDto } from 'src/modules/setting/dtos/response/setting.user.response.dto'; import { ISettingService } from 'src/modules/setting/interfaces/setting.service.interface'; -import { - SettingDoc, - SettingEntity, -} from 'src/modules/setting/repository/entities/setting.entity'; -import { SettingRepository } from 'src/modules/setting/repository/repositories/setting.repository'; - @Injectable() export class SettingService implements ISettingService { - private readonly timezone: string; - private readonly timezoneOffset: string; - constructor( - private readonly settingRepository: SettingRepository, - private readonly helperNumberService: HelperNumberService, private readonly configService: ConfigService, private readonly helperDateService: HelperDateService - ) { - this.timezone = this.configService.get('app.timezone'); - this.timezoneOffset = this.helperDateService.format( - this.helperDateService.create(), - { format: ENUM_HELPER_DATE_FORMAT.TIMEZONE } + ) {} + + async core(): Promise { + // app + const name = this.configService.get('app.name'); + const env = this.configService.get('app.env'); + const timeout = this.configService.get('middleware.timeout'); + + // language + const availableLanguage: ENUM_MESSAGE_LANGUAGE[] = + this.configService.get( + 'message.availableLanguage' + ); + const currentLanguage: ENUM_MESSAGE_LANGUAGE = + this.configService.get('message.language'); + const settingLanguage: SettingLanguageResponseDto = { + language: currentLanguage, + availableLanguage, + }; + + // timezone + const today = this.helperDateService.create(); + const timezone = this.helperDateService.getZoneOffset(today); + const timezoneOffset = this.helperDateService.getZone(today); + const settingTimezone: SettingTimezoneResponseDto = { + timezone: timezone, + timezoneOffset: timezoneOffset, + }; + + // file + const settingFile: SettingFileResponseDto = { + size: FILE_SIZE_IN_BYTES, + sizeUnit: ENUM_SETTING_UNIT.BYTE, + }; + + // auth + const passwordMaxAttempt = this.configService.get( + 'auth.password.maxAttempt' ); - } - - async findAll( - find?: Record, - options?: IDatabaseFindAllOptions - ): Promise { - return this.settingRepository.findAll(find, options); - } - - async findOne( - find: Record, - options?: IDatabaseOptions - ): Promise { - return this.settingRepository.findOne(find, options); - } - - async findOneById( - _id: string, - options?: IDatabaseOptions - ): Promise { - return this.settingRepository.findOneById(_id, options); - } - - async findOneByName( - name: string, - options?: IDatabaseOptions - ): Promise { - return this.settingRepository.findOne({ name }, options); - } - - async getTotal( - find?: Record, - options?: IDatabaseGetTotalOptions - ): Promise { - return this.settingRepository.getTotal(find, options); - } - - async create( - { name, description, type, value }: SettingCreateRequestDto, - options?: IDatabaseCreateOptions - ): Promise { - const create: SettingEntity = new SettingEntity(); - create.name = name; - create.description = description; - create.value = value; - create.type = type; - - return this.settingRepository.create(create, options); - } - - async update( - repository: SettingDoc, - { description, value }: SettingUpdateRequestDto, - options?: IDatabaseSaveOptions - ): Promise { - repository.description = description; - repository.value = value; - - return this.settingRepository.save(repository, options); - } - - async deleteMany( - find: Record, - options?: IDatabaseDeleteManyOptions - ): Promise { - try { - await this.settingRepository.deleteMany(find, options); - - return true; - } catch (error: unknown) { - throw error; - } - } - - getValue(type: ENUM_SETTING_DATA_TYPE, value: string): T { - if ( - type === ENUM_SETTING_DATA_TYPE.BOOLEAN && - (value === 'true' || value === 'false') - ) { - return (value === 'true') as T; - } else if ( - type === ENUM_SETTING_DATA_TYPE.NUMBER && - this.helperNumberService.check(value) - ) { - return Number.parseInt(value) as T; - } - - return value as T; - } - - checkValue(type: ENUM_SETTING_DATA_TYPE, value: string): boolean { - if ( - type === ENUM_SETTING_DATA_TYPE.BOOLEAN && - (value === 'true' || value === 'false') - ) { - return true; - } else if ( - type === ENUM_SETTING_DATA_TYPE.NUMBER && - this.helperNumberService.check(value) - ) { - return true; - } else if ( - type === ENUM_SETTING_DATA_TYPE.STRING && - typeof value === 'string' - ) { - return true; - } - - return false; - } - - async getTimezone(): Promise { - return this.timezone; - } - - async getTimezoneOffset(): Promise { - return this.timezoneOffset; - } - - async mapList( - settings: SettingDoc[] - ): Promise[]> { - return settings.map(e => { - const parseValue = this.getValue(e.type, e.value); - - return { ...e.toObject(), value: parseValue }; - }); - } - - async mapGet( - setting: SettingDoc - ): Promise> { - const parseValue = this.getValue(setting.type, setting.value); - - return { ...setting.toObject(), value: parseValue }; + const passwordExpiredIn = this.configService.get( + 'auth.password.expiredIn' + ); + const passwordExpiredInTemporary = this.configService.get( + 'auth.password.expiredInTemporary' + ); + const passwordPeriod = this.configService.get( + 'auth.password.period' + ); + const settingAuth: SettingAuthResponseDto = { + passwordMaxAttempt, + passwordExpiredIn, + passwordExpiredInUnit: ENUM_SETTING_UNIT.MILLISECOND, + passwordExpiredInTemporary, + passwordExpiredInTemporaryUnit: ENUM_SETTING_UNIT.MILLISECOND, + passwordPeriod, + passwordPeriodUnit: ENUM_SETTING_UNIT.MILLISECOND, + }; + + const bodyJson = this.configService.get( + 'middleware.body.json.maxFileSize' + ); + const bodyRaw = this.configService.get( + 'middleware.body.raw.maxFileSize' + ); + const bodyText = this.configService.get( + 'middleware.body.text.maxFileSize' + ); + const bodyUrlencoded = this.configService.get( + 'middleware.body.urlencoded.maxFileSize' + ); + const settingMiddleware: SettingMiddlewareResponseDto = { + bodyJson, + bodyJsonUnit: ENUM_SETTING_UNIT.BYTE, + bodyRaw, + bodyRawUnit: ENUM_SETTING_UNIT.BYTE, + bodyText, + bodyTextUnit: ENUM_SETTING_UNIT.BYTE, + bodyUrlencoded, + bodyUrlencodedUnit: ENUM_SETTING_UNIT.BYTE, + }; + + // user + const usernamePrefix = this.configService.get( + 'user.usernamePrefix' + ); + const settingUser: SettingUserResponseDto = { + usernamePrefix, + }; + + return { + name, + env, + timeout, + timeoutUnit: ENUM_SETTING_UNIT.MILLISECOND, + file: settingFile, + language: settingLanguage, + timezone: settingTimezone, + middleware: settingMiddleware, + auth: settingAuth, + user: settingUser, + }; } } diff --git a/src/modules/setting/setting.module.ts b/src/modules/setting/setting.module.ts index 9ff66e107..1e74664cb 100644 --- a/src/modules/setting/setting.module.ts +++ b/src/modules/setting/setting.module.ts @@ -1,9 +1,8 @@ import { Module } from '@nestjs/common'; -import { SettingRepositoryModule } from 'src/modules/setting/repository/setting.repository.module'; import { SettingService } from 'src/modules/setting/services/setting.service'; @Module({ - imports: [SettingRepositoryModule], + imports: [], exports: [SettingService], providers: [SettingService], controllers: [], diff --git a/src/modules/user/constants/user.doc.constant.ts b/src/modules/user/constants/user.doc.constant.ts index 7bdd40d02..0926b9065 100644 --- a/src/modules/user/constants/user.doc.constant.ts +++ b/src/modules/user/constants/user.doc.constant.ts @@ -1,4 +1,5 @@ import { faker } from '@faker-js/faker'; +import { ENUM_POLICY_ROLE_TYPE } from 'src/modules/policy/enums/policy.enum'; import { ENUM_USER_STATUS } from 'src/modules/user/enums/user.enum'; export const UserDocParamsId = [ @@ -11,13 +12,14 @@ export const UserDocParamsId = [ }, ]; -export const UserDocQueryRole = [ +export const UserDocQueryRoleType = [ { - name: 'role', + name: 'roleType', allowEmptyValue: true, required: false, type: 'string', - example: faker.string.uuid(), + example: Object.values(ENUM_POLICY_ROLE_TYPE).join(','), + description: "value with ',' delimiter", }, ]; diff --git a/src/modules/user/constants/user.list.constant.ts b/src/modules/user/constants/user.list.constant.ts index 6806fe06b..9e03c4e93 100644 --- a/src/modules/user/constants/user.list.constant.ts +++ b/src/modules/user/constants/user.list.constant.ts @@ -1,4 +1,9 @@ +import { ENUM_POLICY_ROLE_TYPE } from 'src/modules/policy/enums/policy.enum'; import { ENUM_USER_STATUS } from 'src/modules/user/enums/user.enum'; export const USER_DEFAULT_AVAILABLE_SEARCH = ['name', 'email']; export const USER_DEFAULT_STATUS = Object.values(ENUM_USER_STATUS); + +export const USER_DEFAULT_POLICY_ROLE_TYPE = Object.values( + ENUM_POLICY_ROLE_TYPE +); diff --git a/src/modules/user/controllers/user.admin.controller.ts b/src/modules/user/controllers/user.admin.controller.ts index a34d64aa0..dd31e4656 100644 --- a/src/modules/user/controllers/user.admin.controller.ts +++ b/src/modules/user/controllers/user.admin.controller.ts @@ -1,4 +1,5 @@ import { + BadRequestException, Body, ConflictException, Controller, @@ -36,24 +37,25 @@ import { ENUM_POLICY_SUBJECT, } from 'src/modules/policy/enums/policy.enum'; import { ApiKeyProtected } from 'src/modules/api-key/decorators/api-key.decorator'; -import { AuthJwtAccessProtected } from 'src/modules/auth/decorators/auth.jwt.decorator'; +import { + AuthJwtAccessProtected, + AuthJwtPayload, +} from 'src/modules/auth/decorators/auth.jwt.decorator'; import { RequestRequiredPipe } from 'src/common/request/pipes/request.required.pipe'; import { RoleService } from 'src/modules/role/services/role.service'; import { ENUM_ROLE_STATUS_CODE_ERROR } from 'src/modules/role/enums/role.status-code.enum'; import { IAuthPassword } from 'src/modules/auth/interfaces/auth.interface'; import { AuthService } from 'src/modules/auth/services/auth.service'; import { ClientSession, Connection } from 'mongoose'; -import { DatabaseConnection } from 'src/common/database/decorators/database.decorator'; +import { InjectDatabaseConnection } from 'src/common/database/decorators/database.decorator'; import { ENUM_COUNTRY_STATUS_CODE_ERROR } from 'src/modules/country/enums/country.status-code.enum'; import { CountryService } from 'src/modules/country/services/country.service'; import { - UserAdminActiveDoc, - UserAdminBlockedDoc, UserAdminCreateDoc, UserAdminGetDoc, - UserAdminInactiveDoc, UserAdminListDoc, UserAdminUpdateDoc, + UserAdminUpdateStatusDoc, } from 'src/modules/user/docs/user.admin.doc'; import { ENUM_USER_SIGN_UP_FROM, @@ -65,21 +67,29 @@ import { UserProfileResponseDto } from 'src/modules/user/dtos/response/user.prof import { UserService } from 'src/modules/user/services/user.service'; import { USER_DEFAULT_AVAILABLE_SEARCH, + USER_DEFAULT_POLICY_ROLE_TYPE, USER_DEFAULT_STATUS, } from 'src/modules/user/constants/user.list.constant'; -import { IUserDoc } from 'src/modules/user/interfaces/user.interface'; +import { + IUserDoc, + IUserEntity, +} from 'src/modules/user/interfaces/user.interface'; import { UserDoc } from 'src/modules/user/repository/entities/user.entity'; import { UserCreateRequestDto } from 'src/modules/user/dtos/request/user.create.request.dto'; import { ENUM_USER_STATUS_CODE_ERROR } from 'src/modules/user/enums/user.status-code.enum'; import { UserNotSelfPipe } from 'src/modules/user/pipes/user.not-self.pipe'; -import { UserStatusPipe } from 'src/modules/user/pipes/user.status.pipe'; import { UserUpdateRequestDto } from 'src/modules/user/dtos/request/user.update.request.dto'; import { ENUM_APP_STATUS_CODE_ERROR } from 'src/app/enums/app.status-code.enum'; import { DatabaseIdResponseDto } from 'src/common/database/dtos/response/database.id.response.dto'; -import { ENUM_EMAIL } from 'src/modules/email/enums/email.enum'; +import { ENUM_SEND_EMAIL_PROCESS } from 'src/modules/email/enums/email.enum'; import { Queue } from 'bullmq'; import { ENUM_WORKER_QUEUES } from 'src/worker/enums/worker.enum'; -import { WorkerQueue } from 'src/worker/decorators/worker.decorator'; +import { PasswordHistoryService } from 'src/modules/password-history/services/password-history.service'; +import { ENUM_PASSWORD_HISTORY_TYPE } from 'src/modules/password-history/enums/password-history.enum'; +import { ActivityService } from 'src/modules/activity/services/activity.service'; +import { MessageService } from 'src/common/message/services/message.service'; +import { InjectQueue } from '@nestjs/bullmq'; +import { UserUpdateStatusRequestDto } from 'src/modules/user/dtos/request/user.update-status.request.dto'; @ApiTags('modules.admin.user') @Controller({ @@ -88,14 +98,18 @@ import { WorkerQueue } from 'src/worker/decorators/worker.decorator'; }) export class UserAdminController { constructor( - @DatabaseConnection() private readonly databaseConnection: Connection, - @WorkerQueue(ENUM_WORKER_QUEUES.EMAIL_QUEUE) + @InjectDatabaseConnection() + private readonly databaseConnection: Connection, + @InjectQueue(ENUM_WORKER_QUEUES.EMAIL_QUEUE) private readonly emailQueue: Queue, private readonly paginationService: PaginationService, private readonly roleService: RoleService, private readonly authService: AuthService, private readonly userService: UserService, - private readonly countryService: CountryService + private readonly countryService: CountryService, + private readonly passwordHistoryService: PasswordHistoryService, + private readonly activityService: ActivityService, + private readonly messageService: MessageService ) {} @UserAdminListDoc() @@ -119,19 +133,28 @@ export class UserAdminController { ENUM_USER_STATUS ) status: Record, - @PaginationQueryFilterEqual('role') - role: Record, - @PaginationQueryFilterEqual('country') + @PaginationQueryFilterInEnum( + 'role.type', + USER_DEFAULT_POLICY_ROLE_TYPE, + ENUM_POLICY_ROLE_TYPE, + { + queryField: 'roleType', + } + ) + roleType: Record, + @PaginationQueryFilterEqual('country._id', { + queryField: 'country', + }) country: Record ): Promise> { const find: Record = { ..._search, ...status, - ...role, + ...roleType, ...country, }; - const users: IUserDoc[] = + const users: IUserEntity[] = await this.userService.findAllWithRoleAndCountry(find, { paging: { limit: _limit, @@ -139,7 +162,8 @@ export class UserAdminController { }, order: _order, }); - const total: number = await this.userService.getTotal(find); + const total: number = + await this.userService.getTotalWithRoleAndCountry(find); const totalPage: number = this.paginationService.totalPage( total, _limit @@ -183,13 +207,14 @@ export class UserAdminController { @AuthJwtAccessProtected() @Post('/create') async create( + @AuthJwtPayload('_id') _id: string, @Body() - { email, role, name, country }: UserCreateRequestDto + { email, role, name, country, gender }: UserCreateRequestDto ): Promise> { const promises: Promise[] = [ this.roleService.findOneById(role), this.userService.existByEmail(email), - this.countryService.findOneActiveById(country), + this.countryService.findOneById(country), ]; const [checkRole, emailExist, checkCountry] = @@ -213,8 +238,12 @@ export class UserAdminController { } const passwordString = await this.authService.createPasswordRandom(); - const password: IAuthPassword = - await this.authService.createPassword(passwordString); + const password: IAuthPassword = await this.authService.createPassword( + passwordString, + { + temporary: true, + } + ); const session: ClientSession = await this.databaseConnection.startSession(); @@ -227,23 +256,43 @@ export class UserAdminController { country, role, name, + gender, }, password, ENUM_USER_SIGN_UP_FROM.ADMIN, { session } ); + await this.passwordHistoryService.createByAdmin( + created, + { + by: _id, + type: ENUM_PASSWORD_HISTORY_TYPE.SIGN_UP, + }, + { session } + ); + + await this.activityService.createByAdmin( + created, + { + by: _id, + description: this.messageService.setMessage( + 'activity.user.createByAdmin' + ), + }, + { session } + ); + this.emailQueue.add( - ENUM_EMAIL.WELCOME_ADMIN, + ENUM_SEND_EMAIL_PROCESS.WELCOME_ADMIN, { - email: created.email, - name: created.name, + send: { email: created.email, name: created.name }, passwordExpiredAt: password.passwordExpired, password: passwordString, }, { debounce: { - id: `${ENUM_EMAIL.WELCOME_ADMIN}-${created._id}`, + id: `${ENUM_SEND_EMAIL_PROCESS.WELCOME_ADMIN}-${created._id}`, ttl: 1000, }, } @@ -278,15 +327,9 @@ export class UserAdminController { @ApiKeyProtected() @Put('/update/:user') async update( - @Param( - 'user', - RequestRequiredPipe, - UserParsePipe, - UserNotSelfPipe, - new UserStatusPipe([ENUM_USER_STATUS.ACTIVE]) - ) + @Param('user', RequestRequiredPipe, UserParsePipe, UserNotSelfPipe) user: UserDoc, - @Body() { name, country, role }: UserUpdateRequestDto + @Body() { name, country, role, gender }: UserUpdateRequestDto ): Promise { const checkRole = await this.roleService.findOneActiveById(role); if (!checkRole) { @@ -296,8 +339,7 @@ export class UserAdminController { }); } - const checkCountry = - await this.countryService.findOneActiveById(country); + const checkCountry = await this.countryService.findOneById(country); if (!checkCountry) { throw new NotFoundException({ statusCode: ENUM_COUNTRY_STATUS_CODE_ERROR.NOT_FOUND, @@ -305,40 +347,29 @@ export class UserAdminController { }); } - await this.userService.update(user, { name, country, role }); - } - - @UserAdminInactiveDoc() - @Response('user.inactive') - @PolicyAbilityProtected({ - subject: ENUM_POLICY_SUBJECT.USER, - action: [ENUM_POLICY_ACTION.READ, ENUM_POLICY_ACTION.UPDATE], - }) - @PolicyRoleProtected(ENUM_POLICY_ROLE_TYPE.ADMIN) - @AuthJwtAccessProtected() - @ApiKeyProtected() - @Patch('/update/:user/inactive') - async inactive( - @Param( - 'user', - RequestRequiredPipe, - UserParsePipe, - UserNotSelfPipe, - new UserStatusPipe([ENUM_USER_STATUS.ACTIVE]) - ) - user: UserDoc - ): Promise { const session: ClientSession = await this.databaseConnection.startSession(); session.startTransaction(); try { - await this.userService.inactive(user, { session }); + await this.userService.update( + user, + { name, country, role, gender }, + { session } + ); + + await this.activityService.createByUser( + user, + { + description: this.messageService.setMessage( + 'activity.user.updateByAdmin' + ), + }, + { session } + ); await session.commitTransaction(); await session.endSession(); - - return; } catch (err: any) { await session.abortTransaction(); await session.endSession(); @@ -351,8 +382,8 @@ export class UserAdminController { } } - @UserAdminActiveDoc() - @Response('user.active') + @UserAdminUpdateStatusDoc() + @Response('user.updateStatus') @PolicyAbilityProtected({ subject: ENUM_POLICY_SUBJECT.USER, action: [ENUM_POLICY_ACTION.READ, ENUM_POLICY_ACTION.UPDATE], @@ -360,69 +391,42 @@ export class UserAdminController { @PolicyRoleProtected(ENUM_POLICY_ROLE_TYPE.ADMIN) @AuthJwtAccessProtected() @ApiKeyProtected() - @Patch('/update/:user/active') - async active( - @Param( - 'user', - RequestRequiredPipe, - UserParsePipe, - UserNotSelfPipe, - new UserStatusPipe([ENUM_USER_STATUS.INACTIVE]) - ) - user: UserDoc + @Patch('/update/:user/status') + async updateStatus( + @Param('user', RequestRequiredPipe, UserParsePipe, UserNotSelfPipe) + user: UserDoc, + @Body() { status }: UserUpdateStatusRequestDto ): Promise { - const session: ClientSession = - await this.databaseConnection.startSession(); - session.startTransaction(); - - try { - await this.userService.active(user, { session }); - - await session.commitTransaction(); - await session.endSession(); - - return; - } catch (err: any) { - await session.abortTransaction(); - await session.endSession(); - - throw new InternalServerErrorException({ - statusCode: ENUM_APP_STATUS_CODE_ERROR.UNKNOWN, - message: 'http.serverError.internalServerError', - _error: err.message, + if (user.status === ENUM_USER_STATUS.BLOCKED) { + throw new BadRequestException({ + statusCode: ENUM_USER_STATUS_CODE_ERROR.STATUS_INVALID, + message: 'user.error.statusInvalid', + _metadata: { + customProperty: { + messageProperties: { + status: status.toLowerCase(), + }, + }, + }, }); } - } - @UserAdminBlockedDoc() - @Response('user.blocked') - @PolicyAbilityProtected({ - subject: ENUM_POLICY_SUBJECT.USER, - action: [ENUM_POLICY_ACTION.READ, ENUM_POLICY_ACTION.UPDATE], - }) - @PolicyRoleProtected(ENUM_POLICY_ROLE_TYPE.ADMIN) - @AuthJwtAccessProtected() - @ApiKeyProtected() - @Patch('/update/:user/blocked') - async blocked( - @Param( - 'user', - RequestRequiredPipe, - UserParsePipe, - UserNotSelfPipe, - new UserStatusPipe([ - ENUM_USER_STATUS.INACTIVE, - ENUM_USER_STATUS.ACTIVE, - ]) - ) - user: UserDoc - ): Promise { const session: ClientSession = await this.databaseConnection.startSession(); session.startTransaction(); try { - await this.userService.blocked(user, { session }); + await this.userService.updateStatus(user, { status }, { session }); + + await this.activityService.createByUser( + user, + { + description: this.messageService.setMessage( + `activity.user.${status.toLowerCase()}ByAdmin` + ), + }, + { session } + ); await session.commitTransaction(); await session.endSession(); diff --git a/src/modules/user/controllers/user.shared.controller.ts b/src/modules/user/controllers/user.shared.controller.ts index 0fc3e7741..8d6255719 100644 --- a/src/modules/user/controllers/user.shared.controller.ts +++ b/src/modules/user/controllers/user.shared.controller.ts @@ -2,19 +2,21 @@ import { Body, Controller, Get, + HttpCode, + HttpStatus, + InternalServerErrorException, NotFoundException, Post, Put, - UploadedFile, } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { FileUploadSingle } from 'src/common/file/decorators/file.decorator'; -import { ENUM_FILE_MIME_IMAGE } from 'src/common/file/enums/file.enum'; -import { IFile } from 'src/common/file/interfaces/file.interface'; -import { FileRequiredPipe } from 'src/common/file/pipes/file.required.pipe'; -import { FileTypePipe } from 'src/common/file/pipes/file.type.pipe'; +import { ClientSession, Connection } from 'mongoose'; +import { ENUM_APP_STATUS_CODE_ERROR } from 'src/app/enums/app.status-code.enum'; +import { InjectDatabaseConnection } from 'src/common/database/decorators/database.decorator'; +import { MessageService } from 'src/common/message/services/message.service'; import { Response } from 'src/common/response/decorators/response.decorator'; import { IResponse } from 'src/common/response/interfaces/response.interface'; +import { ActivityService } from 'src/modules/activity/services/activity.service'; import { ApiKeyProtected } from 'src/modules/api-key/decorators/api-key.decorator'; import { AuthJwtAccessProtected, @@ -22,13 +24,16 @@ import { } from 'src/modules/auth/decorators/auth.jwt.decorator'; import { AuthJwtAccessPayloadDto } from 'src/modules/auth/dtos/jwt/auth.jwt.access-payload.dto'; import { AwsS3Dto } from 'src/modules/aws/dtos/aws.s3.dto'; +import { AwsS3PresignRequestDto } from 'src/modules/aws/dtos/request/aws.s3-presign.request.dto'; +import { AwsS3PresignResponseDto } from 'src/modules/aws/dtos/response/aws.s3-presign.response.dto'; import { AwsS3Service } from 'src/modules/aws/services/aws.s3.service'; import { ENUM_COUNTRY_STATUS_CODE_ERROR } from 'src/modules/country/enums/country.status-code.enum'; import { CountryService } from 'src/modules/country/services/country.service'; import { UserSharedProfileDoc, + UserSharedUpdatePhotoProfileDoc, UserSharedUpdateProfileDoc, - UserSharedUploadProfileDoc, + UserSharedUploadPhotoProfileDoc, } from 'src/modules/user/docs/user.shared.doc'; import { UserUpdateProfileRequestDto } from 'src/modules/user/dtos/request/user.update-profile.dto'; import { UserProfileResponseDto } from 'src/modules/user/dtos/response/user.profile.response.dto'; @@ -47,9 +52,13 @@ import { UserService } from 'src/modules/user/services/user.service'; }) export class UserSharedController { constructor( + @InjectDatabaseConnection() + private readonly databaseConnection: Connection, private readonly awsS3Service: AwsS3Service, private readonly userService: UserService, - private readonly countryService: CountryService + private readonly countryService: CountryService, + private readonly activityService: ActivityService, + private readonly messageService: MessageService ) {} @UserSharedProfileDoc() @@ -77,7 +86,7 @@ export class UserSharedController { @Body() { country, ...body }: UserUpdateProfileRequestDto ): Promise { - const checkCountry = this.countryService.findOneActiveById(country); + const checkCountry = this.countryService.findOneById(country); if (!checkCountry) { throw new NotFoundException({ statusCode: ENUM_COUNTRY_STATUS_CODE_ERROR.NOT_FOUND, @@ -85,41 +94,111 @@ export class UserSharedController { }); } - await this.userService.updateProfile(user, { country, ...body }); + const session: ClientSession = + await this.databaseConnection.startSession(); + session.startTransaction(); + + try { + await this.userService.updateProfile( + user, + { country, ...body }, + { session } + ); + + await this.activityService.createByUser( + user, + { + description: this.messageService.setMessage( + 'activity.user.updateProfile' + ), + }, + { session } + ); + + await session.commitTransaction(); + await session.endSession(); + } catch (err: any) { + await session.abortTransaction(); + await session.endSession(); + + throw new InternalServerErrorException({ + statusCode: ENUM_APP_STATUS_CODE_ERROR.UNKNOWN, + message: 'http.serverError.internalServerError', + _error: err.message, + }); + } return; } - @UserSharedUploadProfileDoc() - @Response('user.updateProfileUpload') + @UserSharedUploadPhotoProfileDoc() + @Response('user.uploadPhotoProfile') @AuthJwtAccessProtected() - @FileUploadSingle() @ApiKeyProtected() - @Post('/profile/upload') - async updateProfileUpload( + @HttpCode(HttpStatus.OK) + @Post('/profile/upload-photo') + async uploadPhotoProfile( @AuthJwtPayload('_id', UserParsePipe) - user: UserDoc, - @UploadedFile( - new FileRequiredPipe(), - new FileTypePipe([ - ENUM_FILE_MIME_IMAGE.JPG, - ENUM_FILE_MIME_IMAGE.JPEG, - ENUM_FILE_MIME_IMAGE.PNG, - ]) - ) - file: IFile - ): Promise { + user: UserDoc + ): Promise> { const path: string = await this.userService.getPhotoUploadPath( user._id ); const randomFilename: string = await this.userService.createRandomFilenamePhoto(); - const aws: AwsS3Dto = await this.awsS3Service.putItemInBucket(file, { - customFilename: randomFilename, - path, - }); - await this.userService.updatePhoto(user, aws); + const aws: AwsS3PresignResponseDto = await this.awsS3Service.presign( + randomFilename, + { + path, + } + ); + + return { + data: aws, + }; + } + + @UserSharedUpdatePhotoProfileDoc() + @Response('user.updatePhotoProfile') + @AuthJwtAccessProtected() + @ApiKeyProtected() + @Put('/profile/update-photo') + async updatePhotoProfile( + @AuthJwtPayload('_id', UserParsePipe) + user: UserDoc, + @Body() body: AwsS3PresignRequestDto + ): Promise { + const session: ClientSession = + await this.databaseConnection.startSession(); + session.startTransaction(); + + try { + const aws: AwsS3Dto = this.awsS3Service.mapPresign(body); + + await this.userService.updatePhoto(user, aws, { session }); + await this.activityService.createByUser( + user, + { + description: this.messageService.setMessage( + 'activity.user.uploadPhotoProfile' + ), + }, + { session } + ); + + await session.commitTransaction(); + await session.endSession(); + } catch (err: any) { + await session.abortTransaction(); + await session.endSession(); + + throw new InternalServerErrorException({ + statusCode: ENUM_APP_STATUS_CODE_ERROR.UNKNOWN, + message: 'http.serverError.internalServerError', + _error: err.message, + }); + } return; } diff --git a/src/modules/user/controllers/user.system.controller.ts b/src/modules/user/controllers/user.system.controller.ts new file mode 100644 index 000000000..44dc07463 --- /dev/null +++ b/src/modules/user/controllers/user.system.controller.ts @@ -0,0 +1,93 @@ +import { Controller, Get } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; +import { + PaginationQuery, + PaginationQueryFilterEqual, + PaginationQueryFilterInEnum, +} from 'src/common/pagination/decorators/pagination.decorator'; +import { PaginationListDto } from 'src/common/pagination/dtos/pagination.list.dto'; +import { PaginationService } from 'src/common/pagination/services/pagination.service'; +import { ResponsePaging } from 'src/common/response/decorators/response.decorator'; +import { IResponsePaging } from 'src/common/response/interfaces/response.interface'; +import { ApiKeySystemProtected } from 'src/modules/api-key/decorators/api-key.decorator'; +import { ENUM_POLICY_ROLE_TYPE } from 'src/modules/policy/enums/policy.enum'; +import { + USER_DEFAULT_AVAILABLE_SEARCH, + USER_DEFAULT_POLICY_ROLE_TYPE, + USER_DEFAULT_STATUS, +} from 'src/modules/user/constants/user.list.constant'; +import { UserSystemListDoc } from 'src/modules/user/docs/user.system.doc'; +import { UserShortResponseDto } from 'src/modules/user/dtos/response/user.short.response.dto'; +import { ENUM_USER_STATUS } from 'src/modules/user/enums/user.enum'; +import { IUserEntity } from 'src/modules/user/interfaces/user.interface'; +import { UserService } from 'src/modules/user/services/user.service'; + +@ApiTags('modules.system.user') +@Controller({ + version: '1', + path: '/user', +}) +export class UserSystemController { + constructor( + private readonly paginationService: PaginationService, + private readonly userService: UserService + ) {} + + @UserSystemListDoc() + @ResponsePaging('user.list') + @ApiKeySystemProtected() + @Get('/list') + async list( + @PaginationQuery({ availableSearch: USER_DEFAULT_AVAILABLE_SEARCH }) + { _search, _limit, _offset, _order }: PaginationListDto, + @PaginationQueryFilterInEnum( + 'status', + USER_DEFAULT_STATUS, + ENUM_USER_STATUS + ) + status: Record, + @PaginationQueryFilterInEnum( + 'role.type', + USER_DEFAULT_POLICY_ROLE_TYPE, + ENUM_POLICY_ROLE_TYPE, + { + queryField: 'roleType', + } + ) + roleType: Record, + @PaginationQueryFilterEqual('country', { + queryField: 'country', + }) + country: Record + ): Promise> { + const find: Record = { + ..._search, + ...roleType, + ...country, + ...status, + }; + + const users: IUserEntity[] = + await this.userService.findAllWithRoleAndCountry(find, { + paging: { + limit: _limit, + offset: _offset, + }, + order: _order, + }); + + const total: number = + await this.userService.getTotalWithRoleAndCountry(find); + const totalPage: number = this.paginationService.totalPage( + total, + _limit + ); + const mapUsers: UserShortResponseDto[] = + await this.userService.mapShort(users); + + return { + _pagination: { total, totalPage }, + data: mapUsers, + }; + } +} diff --git a/src/modules/user/controllers/user.user.controller.ts b/src/modules/user/controllers/user.user.controller.ts index dc26060a1..f0343ddf6 100644 --- a/src/modules/user/controllers/user.user.controller.ts +++ b/src/modules/user/controllers/user.user.controller.ts @@ -3,6 +3,7 @@ import { ConflictException, Controller, Delete, + InternalServerErrorException, Put, } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; @@ -11,7 +12,6 @@ import { AuthJwtAccessProtected, AuthJwtPayload, } from 'src/modules/auth/decorators/auth.jwt.decorator'; -import { ENUM_POLICY_ROLE_TYPE } from 'src/modules/policy/enums/policy.enum'; import { PolicyRoleProtected } from 'src/modules/policy/decorators/policy.decorator'; import { Response } from 'src/common/response/decorators/response.decorator'; import { UserService } from 'src/modules/user/services/user.service'; @@ -25,6 +25,13 @@ import { UserUpdateClaimUsernameRequestDto } from 'src/modules/user/dtos/request import { ENUM_USER_STATUS_CODE_ERROR } from 'src/modules/user/enums/user.status-code.enum'; import { UserParsePipe } from 'src/modules/user/pipes/user.parse.pipe'; import { UserDoc } from 'src/modules/user/repository/entities/user.entity'; +import { InjectDatabaseConnection } from 'src/common/database/decorators/database.decorator'; +import { ClientSession, Connection } from 'mongoose'; +import { ActivityService } from 'src/modules/activity/services/activity.service'; +import { MessageService } from 'src/common/message/services/message.service'; +import { ENUM_APP_STATUS_CODE_ERROR } from 'src/app/enums/app.status-code.enum'; +import { SessionService } from 'src/modules/session/services/session.service'; +import { POLICY_ROLE_TYPE_USER_GROUP } from 'src/modules/policy/constants/policy.constant'; @ApiTags('modules.user.user') @Controller({ @@ -32,25 +39,67 @@ import { UserDoc } from 'src/modules/user/repository/entities/user.entity'; path: '/user', }) export class UserUserController { - constructor(private readonly userService: UserService) {} + constructor( + @InjectDatabaseConnection() + private readonly databaseConnection: Connection, + private readonly userService: UserService, + private readonly activityService: ActivityService, + private readonly messageService: MessageService, + private readonly sessionService: SessionService + ) {} @UserUserDeleteDoc() @Response('user.delete') - @PolicyRoleProtected(ENUM_POLICY_ROLE_TYPE.USER) + @PolicyRoleProtected(...POLICY_ROLE_TYPE_USER_GROUP) @AuthJwtAccessProtected() @ApiKeyProtected() @Delete('/delete') async delete( @AuthJwtPayload('_id', UserParsePipe) user: UserDoc ): Promise { - await this.userService.delete(user, { deletedBy: user._id }); + const session: ClientSession = + await this.databaseConnection.startSession(); + session.startTransaction(); + + try { + await this.userService.softDelete( + user, + { deletedBy: user._id }, + { session } + ); + + await this.activityService.createByUser( + user, + { + description: + this.messageService.setMessage('activity.delete'), + }, + { session } + ); + + await this.sessionService.updateManyRevokeByUser(user._id, { + session, + }); + + await session.commitTransaction(); + await session.endSession(); + } catch (err: any) { + await session.abortTransaction(); + await session.endSession(); + + throw new InternalServerErrorException({ + statusCode: ENUM_APP_STATUS_CODE_ERROR.UNKNOWN, + message: 'http.serverError.internalServerError', + _error: err.message, + }); + } return; } @UserUserUpdateMobileNumberDoc() @Response('user.updateMobileNumber') - @PolicyRoleProtected(ENUM_POLICY_ROLE_TYPE.USER) + @PolicyRoleProtected(...POLICY_ROLE_TYPE_USER_GROUP) @AuthJwtAccessProtected() @ApiKeyProtected() @Put('/update/mobile-number') @@ -59,14 +108,42 @@ export class UserUserController { @Body() body: UserUpdateMobileNumberRequestDto ): Promise { - await this.userService.updateMobileNumber(user, body); + const session: ClientSession = + await this.databaseConnection.startSession(); + session.startTransaction(); + + try { + await this.userService.updateMobileNumber(user, body, { session }); + + await this.activityService.createByUser( + user, + { + description: this.messageService.setMessage( + 'activity.user.updateMobileNumber' + ), + }, + { session } + ); + + await session.commitTransaction(); + await session.endSession(); + } catch (err: any) { + await session.abortTransaction(); + await session.endSession(); + + throw new InternalServerErrorException({ + statusCode: ENUM_APP_STATUS_CODE_ERROR.UNKNOWN, + message: 'http.serverError.internalServerError', + _error: err.message, + }); + } return; } @UserUserUpdateUsernameDoc() @Response('user.updateClaimUsername') - @PolicyRoleProtected(ENUM_POLICY_ROLE_TYPE.USER) + @PolicyRoleProtected(...POLICY_ROLE_TYPE_USER_GROUP) @AuthJwtAccessProtected() @ApiKeyProtected() @Put('/update/claim-username') @@ -75,16 +152,49 @@ export class UserUserController { @Body() { username }: UserUpdateClaimUsernameRequestDto ): Promise { - const checkUsername = await this.userService.existByUsername(username); - if (checkUsername) { - throw new ConflictException({ - statusCode: ENUM_USER_STATUS_CODE_ERROR.USERNAME_EXIST, - message: 'user.error.usernameExist', + const session: ClientSession = + await this.databaseConnection.startSession(); + session.startTransaction(); + + try { + const checkUsername = + await this.userService.existByUsername(username); + if (checkUsername) { + throw new ConflictException({ + statusCode: ENUM_USER_STATUS_CODE_ERROR.USERNAME_EXIST, + message: 'user.error.usernameExist', + }); + } + + await this.userService.updateClaimUsername( + user, + { username }, + { session } + ); + + await this.activityService.createByUser( + user, + { + description: this.messageService.setMessage( + 'activity.user.updateClaimUsername' + ), + }, + { session } + ); + + await session.commitTransaction(); + await session.endSession(); + } catch (err: any) { + await session.abortTransaction(); + await session.endSession(); + + throw new InternalServerErrorException({ + statusCode: ENUM_APP_STATUS_CODE_ERROR.UNKNOWN, + message: 'http.serverError.internalServerError', + _error: err.message, }); } - await this.userService.updateClaimUsername(user, { username }); - return; } } diff --git a/src/modules/user/docs/user.admin.doc.ts b/src/modules/user/docs/user.admin.doc.ts index c2717b107..27005ea90 100644 --- a/src/modules/user/docs/user.admin.doc.ts +++ b/src/modules/user/docs/user.admin.doc.ts @@ -12,10 +12,11 @@ import { ENUM_DOC_REQUEST_BODY_TYPE } from 'src/common/doc/enums/doc.enum'; import { UserDocParamsId, UserDocQueryCountry, - UserDocQueryRole, + UserDocQueryRoleType, UserDocQueryStatus, } from 'src/modules/user/constants/user.doc.constant'; import { UserCreateRequestDto } from 'src/modules/user/dtos/request/user.create.request.dto'; +import { UserUpdateStatusRequestDto } from 'src/modules/user/dtos/request/user.update-status.request.dto'; import { UserUpdateRequestDto } from 'src/modules/user/dtos/request/user.update.request.dto'; import { UserListResponseDto } from 'src/modules/user/dtos/response/user.list.response.dto'; import { UserProfileResponseDto } from 'src/modules/user/dtos/response/user.profile.response.dto'; @@ -28,7 +29,7 @@ export function UserAdminListDoc(): MethodDecorator { DocRequest({ queries: [ ...UserDocQueryStatus, - ...UserDocQueryRole, + ...UserDocQueryRoleType, ...UserDocQueryCountry, ], }), @@ -83,20 +84,22 @@ export function UserAdminCreateDoc(): MethodDecorator { ); } -export function UserAdminActiveDoc(): MethodDecorator { +export function UserAdminUpdateStatusDoc(): MethodDecorator { return applyDecorators( Doc({ - summary: 'make user be active', + summary: 'update status of user', }), DocRequest({ params: UserDocParamsId, + bodyType: ENUM_DOC_REQUEST_BODY_TYPE.JSON, + dto: UserUpdateStatusRequestDto, }), DocAuth({ xApiKey: true, jwtAccessToken: true, }), DocGuard({ role: true, policy: true }), - DocResponse('user.active') + DocResponse('user.updateStatus') ); } @@ -118,37 +121,3 @@ export function UserAdminUpdateDoc(): MethodDecorator { DocResponse('user.update') ); } - -export function UserAdminInactiveDoc(): MethodDecorator { - return applyDecorators( - Doc({ - summary: 'make user be inactive', - }), - DocRequest({ - params: UserDocParamsId, - }), - DocAuth({ - xApiKey: true, - jwtAccessToken: true, - }), - DocGuard({ role: true, policy: true }), - DocResponse('user.inactive') - ); -} - -export function UserAdminBlockedDoc(): MethodDecorator { - return applyDecorators( - Doc({ - summary: 'block a user', - }), - DocRequest({ - params: UserDocParamsId, - }), - DocAuth({ - xApiKey: true, - jwtAccessToken: true, - }), - DocGuard({ role: true, policy: true }), - DocResponse('user.blocked') - ); -} diff --git a/src/modules/user/docs/user.shared.doc.ts b/src/modules/user/docs/user.shared.doc.ts index 1b38afcca..49f3069b2 100644 --- a/src/modules/user/docs/user.shared.doc.ts +++ b/src/modules/user/docs/user.shared.doc.ts @@ -1,13 +1,13 @@ -import { applyDecorators, HttpStatus } from '@nestjs/common'; +import { applyDecorators } from '@nestjs/common'; import { Doc, DocAuth, DocRequest, - DocRequestFile, DocResponse, } from 'src/common/doc/decorators/doc.decorator'; import { ENUM_DOC_REQUEST_BODY_TYPE } from 'src/common/doc/enums/doc.enum'; -import { FileSingleDto } from 'src/common/file/dtos/file.single.dto'; +import { AwsS3PresignRequestDto } from 'src/modules/aws/dtos/request/aws.s3-presign.request.dto'; +import { AwsS3PresignResponseDto } from 'src/modules/aws/dtos/response/aws.s3-presign.response.dto'; import { UserUpdateProfileRequestDto } from 'src/modules/user/dtos/request/user.update-profile.dto'; import { UserProfileResponseDto } from 'src/modules/user/dtos/response/user.profile.response.dto'; @@ -43,20 +43,34 @@ export function UserSharedUpdateProfileDoc(): MethodDecorator { ); } -export function UserSharedUploadProfileDoc(): MethodDecorator { +export function UserSharedUploadPhotoProfileDoc(): MethodDecorator { return applyDecorators( Doc({ - summary: 'update profile photo', + summary: 'get presign url for photo profile', }), DocAuth({ xApiKey: true, jwtAccessToken: true, }), - DocRequestFile({ - dto: FileSingleDto, - }), - DocResponse('user.upload', { - httpStatus: HttpStatus.CREATED, + DocResponse('user.uploadPhotoProfile', { + dto: AwsS3PresignResponseDto, }) ); } + +export function UserSharedUpdatePhotoProfileDoc(): MethodDecorator { + return applyDecorators( + Doc({ + summary: 'update photo profile', + }), + DocAuth({ + xApiKey: true, + jwtAccessToken: true, + }), + DocRequest({ + bodyType: ENUM_DOC_REQUEST_BODY_TYPE.JSON, + dto: AwsS3PresignRequestDto, + }), + DocResponse('user.updatePhotoProfile') + ); +} diff --git a/src/modules/user/docs/user.system.doc.ts b/src/modules/user/docs/user.system.doc.ts new file mode 100644 index 000000000..07279dec5 --- /dev/null +++ b/src/modules/user/docs/user.system.doc.ts @@ -0,0 +1,34 @@ +import { applyDecorators } from '@nestjs/common'; +import { + Doc, + DocAuth, + DocRequest, + DocResponsePaging, +} from 'src/common/doc/decorators/doc.decorator'; +import { + UserDocQueryCountry, + UserDocQueryRoleType, + UserDocQueryStatus, +} from 'src/modules/user/constants/user.doc.constant'; +import { UserShortResponseDto } from 'src/modules/user/dtos/response/user.short.response.dto'; + +export function UserSystemListDoc(): MethodDecorator { + return applyDecorators( + Doc({ + summary: 'get all of users', + }), + DocRequest({ + queries: [ + ...UserDocQueryStatus, + ...UserDocQueryRoleType, + ...UserDocQueryCountry, + ], + }), + DocAuth({ + xApiKey: true, + }), + DocResponsePaging('user.list', { + dto: UserShortResponseDto, + }) + ); +} diff --git a/src/modules/user/dtos/request/user.create.request.dto.ts b/src/modules/user/dtos/request/user.create.request.dto.ts index 2207d7f88..340a5283d 100644 --- a/src/modules/user/dtos/request/user.create.request.dto.ts +++ b/src/modules/user/dtos/request/user.create.request.dto.ts @@ -1,6 +1,5 @@ import { faker } from '@faker-js/faker'; import { ApiProperty } from '@nestjs/swagger'; -import { Type } from 'class-transformer'; import { IsString, IsNotEmpty, @@ -8,7 +7,9 @@ import { MaxLength, MinLength, IsUUID, + IsEnum, } from 'class-validator'; +import { ENUM_USER_GENDER } from 'src/modules/user/enums/user.enum'; export class UserCreateRequestDto { @ApiProperty({ @@ -19,7 +20,6 @@ export class UserCreateRequestDto { @IsEmail() @IsNotEmpty() @MaxLength(100) - @Type(() => String) email: string; @ApiProperty({ @@ -40,7 +40,6 @@ export class UserCreateRequestDto { @IsNotEmpty() @MinLength(1) @MaxLength(100) - @Type(() => String) name: string; @ApiProperty({ @@ -51,4 +50,14 @@ export class UserCreateRequestDto { @IsUUID() @IsNotEmpty() country: string; + + @ApiProperty({ + required: true, + enum: ENUM_USER_GENDER, + example: ENUM_USER_GENDER.MALE, + }) + @IsString() + @IsEnum(ENUM_USER_GENDER) + @IsNotEmpty() + gender: ENUM_USER_GENDER; } diff --git a/src/modules/user/dtos/request/user.update-mobile-number.request.dto.ts b/src/modules/user/dtos/request/user.update-mobile-number.request.dto.ts index cb4143645..8a01f4276 100644 --- a/src/modules/user/dtos/request/user.update-mobile-number.request.dto.ts +++ b/src/modules/user/dtos/request/user.update-mobile-number.request.dto.ts @@ -1,6 +1,5 @@ import { faker } from '@faker-js/faker'; import { ApiProperty, PickType } from '@nestjs/swagger'; -import { Type } from 'class-transformer'; import { IsNotEmpty, IsString, MaxLength, MinLength } from 'class-validator'; import { UserCreateRequestDto } from 'src/modules/user/dtos/request/user.create.request.dto'; @@ -21,6 +20,5 @@ export class UserUpdateMobileNumberRequestDto extends PickType( @IsNotEmpty() @MinLength(8) @MaxLength(20) - @Type(() => String) number: string; } diff --git a/src/modules/user/dtos/request/user.update-password-attempt.request.dto.ts b/src/modules/user/dtos/request/user.update-password-attempt.request.dto.ts index 5ad61bd6c..abf04ce63 100644 --- a/src/modules/user/dtos/request/user.update-password-attempt.request.dto.ts +++ b/src/modules/user/dtos/request/user.update-password-attempt.request.dto.ts @@ -1,16 +1,15 @@ import { ApiProperty } from '@nestjs/swagger'; -import { Type } from 'class-transformer'; import { IsNotEmpty, IsNumber, Max, Min } from 'class-validator'; export class UserUpdatePasswordAttemptRequestDto { @ApiProperty({ required: true, - type: 'integer', + minimum: 0, + maximum: 3, }) @IsNumber({ allowNaN: false, allowInfinity: false }) @IsNotEmpty() @Min(0) @Max(3) - @Type(() => Number) passwordAttempt: number; } diff --git a/src/modules/user/dtos/request/user.update-profile.dto.ts b/src/modules/user/dtos/request/user.update-profile.dto.ts index 94d02e78b..c6d592634 100644 --- a/src/modules/user/dtos/request/user.update-profile.dto.ts +++ b/src/modules/user/dtos/request/user.update-profile.dto.ts @@ -1,31 +1,7 @@ -import { faker } from '@faker-js/faker'; -import { ApiProperty, PickType } from '@nestjs/swagger'; -import { Type } from 'class-transformer'; -import { IsOptional, IsString, MaxLength, MinLength } from 'class-validator'; +import { PickType } from '@nestjs/swagger'; import { UserCreateRequestDto } from 'src/modules/user/dtos/request/user.create.request.dto'; export class UserUpdateProfileRequestDto extends PickType( UserCreateRequestDto, - ['name', 'country'] as const -) { - @ApiProperty({ - example: faker.person.lastName(), - required: false, - maxLength: 50, - minLength: 1, - }) - @IsString() - @IsOptional() - @MinLength(1) - @MaxLength(50) - @Type(() => String) - readonly familyName?: string; - - @ApiProperty({ - required: false, - maxLength: 200, - }) - @IsString() - @IsOptional() - readonly address?: string; -} + ['name', 'country', 'gender'] as const +) {} diff --git a/src/modules/user/dtos/request/user.update-status.request.dto.ts b/src/modules/user/dtos/request/user.update-status.request.dto.ts new file mode 100644 index 000000000..4f3609e47 --- /dev/null +++ b/src/modules/user/dtos/request/user.update-status.request.dto.ts @@ -0,0 +1,15 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum, IsNotEmpty, IsString } from 'class-validator'; +import { ENUM_USER_STATUS } from 'src/modules/user/enums/user.enum'; + +export class UserUpdateStatusRequestDto { + @ApiProperty({ + required: true, + enum: ENUM_USER_STATUS, + default: ENUM_USER_STATUS.ACTIVE, + }) + @IsString() + @IsEnum(ENUM_USER_STATUS) + @IsNotEmpty() + status: ENUM_USER_STATUS; +} diff --git a/src/modules/user/dtos/request/user.update.request.dto.ts b/src/modules/user/dtos/request/user.update.request.dto.ts index 995da388f..50bb1cc69 100644 --- a/src/modules/user/dtos/request/user.update.request.dto.ts +++ b/src/modules/user/dtos/request/user.update.request.dto.ts @@ -1,8 +1,6 @@ -import { PickType } from '@nestjs/swagger'; +import { OmitType } from '@nestjs/swagger'; import { UserCreateRequestDto } from 'src/modules/user/dtos/request/user.create.request.dto'; -export class UserUpdateRequestDto extends PickType(UserCreateRequestDto, [ - 'name', - 'country', - 'role', +export class UserUpdateRequestDto extends OmitType(UserCreateRequestDto, [ + 'email', ] as const) {} diff --git a/src/modules/user/dtos/response/user.get.response.dto.ts b/src/modules/user/dtos/response/user.get.response.dto.ts index 69a63f06b..8f8268cc4 100644 --- a/src/modules/user/dtos/response/user.get.response.dto.ts +++ b/src/modules/user/dtos/response/user.get.response.dto.ts @@ -8,7 +8,8 @@ import { } from 'src/modules/user/enums/user.enum'; import { UserUpdateMobileNumberRequestDto } from 'src/modules/user/dtos/request/user.update-mobile-number.request.dto'; import { DatabaseDto } from 'src/common/database/dtos/database.dto'; -import { AwsS3Dto } from 'src/modules/aws/dtos/aws.s3.dto'; +import { UserVerificationResponseDto } from 'src/modules/user/dtos/response/user.verification.response.dto'; +import { AwsS3ResponseDto } from 'src/modules/aws/dtos/response/aws.s3-response.dto'; export class UserGetResponseDto extends DatabaseDto { @ApiProperty({ @@ -30,6 +31,7 @@ export class UserGetResponseDto extends DatabaseDto { @ApiProperty({ required: false, type: UserUpdateMobileNumberRequestDto, + oneOf: [{ $ref: getSchemaPath(UserUpdateMobileNumberRequestDto) }], }) @Type(() => UserUpdateMobileNumberRequestDto) mobileNumber?: UserUpdateMobileNumberRequestDto; @@ -82,6 +84,7 @@ export class UserGetResponseDto extends DatabaseDto { required: true, nullable: false, example: ENUM_USER_SIGN_UP_FROM.ADMIN, + enum: ENUM_USER_SIGN_UP_FROM, }) signUpFrom: ENUM_USER_SIGN_UP_FROM; @@ -93,17 +96,18 @@ export class UserGetResponseDto extends DatabaseDto { required: true, nullable: false, example: ENUM_USER_STATUS.ACTIVE, + enum: ENUM_USER_STATUS, }) status: ENUM_USER_STATUS; @ApiProperty({ nullable: true, required: false, - type: AwsS3Dto, - oneOf: [{ $ref: getSchemaPath(AwsS3Dto) }], + type: AwsS3ResponseDto, + oneOf: [{ $ref: getSchemaPath(AwsS3ResponseDto) }], }) - @Type(() => AwsS3Dto) - photo?: AwsS3Dto; + @Type(() => AwsS3ResponseDto) + photo?: AwsS3ResponseDto; @ApiProperty({ example: ENUM_USER_GENDER.MALE, @@ -119,19 +123,12 @@ export class UserGetResponseDto extends DatabaseDto { }) country: string; - @ApiProperty({ - example: faker.location.streetAddress(), - required: false, - nullable: true, - maxLength: 200, - }) - address?: string; - @ApiProperty({ example: faker.person.lastName(), - required: false, - nullable: true, - maxLength: 50, + required: true, + type: UserVerificationResponseDto, + oneOf: [{ $ref: getSchemaPath(UserVerificationResponseDto) }], }) - familyName?: string; + @Type(() => UserVerificationResponseDto) + verification: UserVerificationResponseDto; } diff --git a/src/modules/user/dtos/response/user.list.response.dto.ts b/src/modules/user/dtos/response/user.list.response.dto.ts index 17ec36b1a..8c87d7d55 100644 --- a/src/modules/user/dtos/response/user.list.response.dto.ts +++ b/src/modules/user/dtos/response/user.list.response.dto.ts @@ -1,4 +1,9 @@ -import { ApiHideProperty, ApiProperty, OmitType } from '@nestjs/swagger'; +import { + ApiHideProperty, + ApiProperty, + getSchemaPath, + OmitType, +} from '@nestjs/swagger'; import { Exclude, Type } from 'class-transformer'; import { CountryShortResponseDto } from 'src/modules/country/dtos/response/country.short.response.dto'; import { RoleListResponseDto } from 'src/modules/role/dtos/response/role.list.response.dto'; @@ -8,6 +13,7 @@ import { } from 'src/modules/user/enums/user.enum'; import { UserUpdateMobileNumberRequestDto } from 'src/modules/user/dtos/request/user.update-mobile-number.request.dto'; import { UserGetResponseDto } from 'src/modules/user/dtos/response/user.get.response.dto'; +import { UserVerificationResponseDto } from 'src/modules/user/dtos/response/user.verification.response.dto'; export class UserListResponseDto extends OmitType(UserGetResponseDto, [ 'passwordExpired', @@ -18,13 +24,13 @@ export class UserListResponseDto extends OmitType(UserGetResponseDto, [ 'role', 'country', 'mobileNumber', - 'address', - 'familyName', + 'verification', ] as const) { @ApiProperty({ required: true, nullable: false, type: RoleListResponseDto, + oneOf: [{ $ref: getSchemaPath(RoleListResponseDto) }], }) @Type(() => RoleListResponseDto) role: RoleListResponseDto; @@ -33,6 +39,7 @@ export class UserListResponseDto extends OmitType(UserGetResponseDto, [ required: true, nullable: false, type: CountryShortResponseDto, + oneOf: [{ $ref: getSchemaPath(CountryShortResponseDto) }], }) @Type(() => CountryShortResponseDto) country: CountryShortResponseDto; @@ -63,9 +70,5 @@ export class UserListResponseDto extends OmitType(UserGetResponseDto, [ @ApiHideProperty() @Exclude() - address?: string; - - @ApiHideProperty() - @Exclude() - familyName?: string; + verification: UserVerificationResponseDto; } diff --git a/src/modules/user/dtos/response/user.mobile-number.response.dto.ts b/src/modules/user/dtos/response/user.mobile-number.response.dto.ts index 1f17e5925..c6349bb58 100644 --- a/src/modules/user/dtos/response/user.mobile-number.response.dto.ts +++ b/src/modules/user/dtos/response/user.mobile-number.response.dto.ts @@ -1,5 +1,5 @@ import { faker } from '@faker-js/faker'; -import { ApiProperty } from '@nestjs/swagger'; +import { ApiProperty, getSchemaPath } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { CountryShortResponseDto } from 'src/modules/country/dtos/response/country.short.response.dto'; @@ -13,13 +13,13 @@ export class UserMobileNumberResponseDto { maxLength: 20, minLength: 8, }) - @Type(() => String) number: string; @ApiProperty({ required: true, nullable: false, type: CountryShortResponseDto, + oneOf: [{ $ref: getSchemaPath(CountryShortResponseDto) }], }) @Type(() => CountryShortResponseDto) country: CountryShortResponseDto; diff --git a/src/modules/user/dtos/response/user.profile.response.dto.ts b/src/modules/user/dtos/response/user.profile.response.dto.ts index f61742a25..b6d50a336 100644 --- a/src/modules/user/dtos/response/user.profile.response.dto.ts +++ b/src/modules/user/dtos/response/user.profile.response.dto.ts @@ -1,8 +1,7 @@ -import { ApiProperty, OmitType } from '@nestjs/swagger'; +import { ApiProperty, getSchemaPath, OmitType } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { CountryShortResponseDto } from 'src/modules/country/dtos/response/country.short.response.dto'; import { RoleGetResponseDto } from 'src/modules/role/dtos/response/role.get.response.dto'; -import { RoleListResponseDto } from 'src/modules/role/dtos/response/role.list.response.dto'; import { UserGetResponseDto } from 'src/modules/user/dtos/response/user.get.response.dto'; import { UserMobileNumberResponseDto } from 'src/modules/user/dtos/response/user.mobile-number.response.dto'; @@ -15,6 +14,7 @@ export class UserProfileResponseDto extends OmitType(UserGetResponseDto, [ required: true, nullable: false, type: RoleGetResponseDto, + oneOf: [{ $ref: getSchemaPath(RoleGetResponseDto) }], }) @Type(() => RoleGetResponseDto) role: RoleGetResponseDto; @@ -23,6 +23,7 @@ export class UserProfileResponseDto extends OmitType(UserGetResponseDto, [ required: true, nullable: false, type: CountryShortResponseDto, + oneOf: [{ $ref: getSchemaPath(CountryShortResponseDto) }], }) @Type(() => CountryShortResponseDto) country: CountryShortResponseDto; @@ -31,7 +32,8 @@ export class UserProfileResponseDto extends OmitType(UserGetResponseDto, [ required: false, nullable: false, type: UserMobileNumberResponseDto, + oneOf: [{ $ref: getSchemaPath(UserMobileNumberResponseDto) }], }) - @Type(() => RoleListResponseDto) + @Type(() => UserMobileNumberResponseDto) mobileNumber?: UserMobileNumberResponseDto; } diff --git a/src/modules/user/dtos/response/user.short.response.dto.ts b/src/modules/user/dtos/response/user.short.response.dto.ts index 6c3200ce2..313c76436 100644 --- a/src/modules/user/dtos/response/user.short.response.dto.ts +++ b/src/modules/user/dtos/response/user.short.response.dto.ts @@ -3,7 +3,7 @@ import { Exclude } from 'class-transformer'; import { RoleListResponseDto } from 'src/modules/role/dtos/response/role.list.response.dto'; import { ENUM_USER_STATUS } from 'src/modules/user/enums/user.enum'; import { UserListResponseDto } from 'src/modules/user/dtos/response/user.list.response.dto'; -import { AwsS3Dto } from 'src/modules/aws/dtos/aws.s3.dto'; +import { AwsS3ResponseDto } from 'src/modules/aws/dtos/response/aws.s3-response.dto'; export class UserShortResponseDto extends OmitType(UserListResponseDto, [ 'role', @@ -22,7 +22,7 @@ export class UserShortResponseDto extends OmitType(UserListResponseDto, [ @ApiHideProperty() @Exclude() - photo?: AwsS3Dto; + photo?: AwsS3ResponseDto; @ApiHideProperty() @Exclude() diff --git a/src/modules/user/dtos/response/user.verification.response.dto.ts b/src/modules/user/dtos/response/user.verification.response.dto.ts new file mode 100644 index 000000000..0ce6852c2 --- /dev/null +++ b/src/modules/user/dtos/response/user.verification.response.dto.ts @@ -0,0 +1,10 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class UserVerificationResponseDto { + @ApiProperty({ + example: false, + required: true, + default: false, + }) + email: boolean; +} diff --git a/src/modules/user/enums/user.enum.ts b/src/modules/user/enums/user.enum.ts index a42a008a9..8e814ff33 100644 --- a/src/modules/user/enums/user.enum.ts +++ b/src/modules/user/enums/user.enum.ts @@ -5,10 +5,8 @@ export enum ENUM_USER_SIGN_UP_FROM { } export enum ENUM_USER_STATUS { - CREATED = 'CREATED', ACTIVE = 'ACTIVE', INACTIVE = 'INACTIVE', - DELETED = 'DELETED', BLOCKED = 'BLOCKED', } diff --git a/src/modules/user/interfaces/user.interface.ts b/src/modules/user/interfaces/user.interface.ts index ae211ae84..cbee84915 100644 --- a/src/modules/user/interfaces/user.interface.ts +++ b/src/modules/user/interfaces/user.interface.ts @@ -9,9 +9,11 @@ import { import { UserDoc, UserEntity, +} from 'src/modules/user/repository/entities/user.entity'; +import { UserMobileNumberDoc, UserMobileNumberEntity, -} from 'src/modules/user/repository/entities/user.entity'; +} from 'src/modules/user/repository/entities/user.mobile-number.entity'; export interface IUserMobileNumberEntity extends Omit { diff --git a/src/modules/user/interfaces/user.service.interface.ts b/src/modules/user/interfaces/user.service.interface.ts index 60e827b8a..ccfe371a0 100644 --- a/src/modules/user/interfaces/user.service.interface.ts +++ b/src/modules/user/interfaces/user.service.interface.ts @@ -1,8 +1,9 @@ import { IAuthPassword } from 'src/modules/auth/interfaces/auth.interface'; import { + IDatabaseAggregateOptions, IDatabaseCreateOptions, IDatabaseDeleteManyOptions, - IDatabaseExistOptions, + IDatabaseFindAllAggregateOptions, IDatabaseFindAllOptions, IDatabaseGetTotalOptions, IDatabaseOptions, @@ -31,6 +32,8 @@ import { AuthSignUpRequestDto } from 'src/modules/auth/dtos/request/auth.sign-up import { UserUpdateClaimUsernameRequestDto } from 'src/modules/user/dtos/request/user.update-claim-username.dto'; import { DatabaseSoftDeleteDto } from 'src/common/database/dtos/database.soft-delete.dto'; import { UserUpdateProfileRequestDto } from 'src/modules/user/dtos/request/user.update-profile.dto'; +import { UserUpdateStatusRequestDto } from 'src/modules/user/dtos/request/user.update-status.request.dto'; +import { PipelineStage } from 'mongoose'; export interface IUserService { findAll( @@ -41,6 +44,17 @@ export interface IUserService { find?: Record, options?: IDatabaseGetTotalOptions ): Promise; + createRawQueryFindAllWithRoleAndCountry( + find?: Record + ): PipelineStage[]; + findAllWithRoleAndCountry( + find?: Record, + options?: IDatabaseFindAllAggregateOptions + ): Promise; + getTotalWithRoleAndCountry( + find?: Record, + options?: IDatabaseAggregateOptions + ): Promise; findOneById(_id: string, options?: IDatabaseOptions): Promise; findOne( find: Record, @@ -48,13 +62,10 @@ export interface IUserService { ): Promise; findOneByEmail(email: string, options?: IDatabaseOptions): Promise; findOneByMobileNumber( + country: string, mobileNumber: string, options?: IDatabaseOptions ): Promise; - findAllWithRoleAndCountry( - find?: Record, - options?: IDatabaseFindAllOptions - ): Promise; findOneWithRoleAndCountry( find?: Record, options?: IDatabaseFindAllOptions @@ -80,6 +91,7 @@ export interface IUserService { options?: IDatabaseOptions ): Promise; findOneActiveByMobileNumber( + country: string, mobileNumber: string, options?: IDatabaseOptions ): Promise; @@ -95,17 +107,10 @@ export interface IUserService { { passwordExpired, passwordHash, salt, passwordCreated }: IAuthPassword, options?: IDatabaseCreateOptions ): Promise; - existByEmail( - email: string, - options?: IDatabaseExistOptions - ): Promise; + existByEmail(email: string, options?: IDatabaseOptions): Promise; existByUsername( username: string, - options?: IDatabaseExistOptions - ): Promise; - existByMobileNumber( - mobileNumber: string, - options?: IDatabaseExistOptions + options?: IDatabaseOptions ): Promise; updatePhoto( repository: UserDoc, @@ -117,18 +122,11 @@ export interface IUserService { { passwordHash, passwordExpired, salt, passwordCreated }: IAuthPassword, options?: IDatabaseSaveOptions ): Promise; - active( + updateStatus( repository: UserDoc, + { status }: UserUpdateStatusRequestDto, options?: IDatabaseSaveOptions ): Promise; - inactive( - repository: UserDoc, - options?: IDatabaseSaveOptions - ): Promise; - blocked( - repository: UserDoc, - options?: IDatabaseSaveOptions - ): Promise; updatePasswordAttempt( repository: UserDoc, { passwordAttempt }: UserUpdatePasswordAttemptRequestDto, @@ -166,7 +164,7 @@ export interface IUserService { repository: UserDoc, options?: IDatabaseSaveOptions ): Promise; - delete( + softDelete( repository: UserDoc, dto: DatabaseSoftDeleteDto, options?: IDatabaseSaveOptions @@ -177,7 +175,11 @@ export interface IUserService { ): Promise; updateProfile( repository: UserDoc, - { country, name, address, familyName }: UserUpdateProfileRequestDto, + { country, name, gender }: UserUpdateProfileRequestDto, + options?: IDatabaseSaveOptions + ): Promise; + updateVerificationEmail( + repository: UserDoc, options?: IDatabaseSaveOptions ): Promise; join(repository: UserDoc): Promise; diff --git a/src/modules/user/pipes/user.status.pipe.ts b/src/modules/user/pipes/user.status.pipe.ts deleted file mode 100644 index 936c93b4e..000000000 --- a/src/modules/user/pipes/user.status.pipe.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { BadRequestException, Injectable, PipeTransform } from '@nestjs/common'; -import { ENUM_USER_STATUS } from 'src/modules/user/enums/user.enum'; -import { ENUM_USER_STATUS_CODE_ERROR } from 'src/modules/user/enums/user.status-code.enum'; -import { UserDoc } from 'src/modules/user/repository/entities/user.entity'; -@Injectable() -export class UserStatusPipe implements PipeTransform { - private readonly status: ENUM_USER_STATUS[]; - - constructor(status: ENUM_USER_STATUS[]) { - this.status = status; - } - - async transform(value: UserDoc): Promise { - if (!this.status.includes(value.status)) { - throw new BadRequestException({ - statusCode: ENUM_USER_STATUS_CODE_ERROR.STATUS_INVALID, - message: 'user.error.statusInvalid', - }); - } - - return value; - } -} diff --git a/src/modules/user/repository/entities/user.entity.ts b/src/modules/user/repository/entities/user.entity.ts index 6c10c7134..bfdbe6641 100644 --- a/src/modules/user/repository/entities/user.entity.ts +++ b/src/modules/user/repository/entities/user.entity.ts @@ -1,4 +1,4 @@ -import { DatabaseEntityAbstract } from 'src/common/database/abstracts/database.entity.abstract'; +import { DatabaseEntityBase } from 'src/common/database/bases/database.entity'; import { DatabaseEntity, DatabaseProp, @@ -16,37 +16,19 @@ import { ENUM_USER_SIGN_UP_FROM, ENUM_USER_STATUS, } from 'src/modules/user/enums/user.enum'; +import { + UserMobileNumberEntity, + UserMobileNumberSchema, +} from 'src/modules/user/repository/entities/user.mobile-number.entity'; +import { + UserVerificationEntity, + UserVerificationSchema, +} from 'src/modules/user/repository/entities/user.verification.entity'; export const UserTableName = 'Users'; -@DatabaseEntity({ - _id: false, - timestamps: false, -}) -export class UserMobileNumberEntity { - @DatabaseProp({ - required: true, - type: String, - ref: CountryEntity.name, - trim: true, - }) - country: string; - - @DatabaseProp({ - required: false, - trim: true, - type: String, - maxlength: 20, - minlength: 8, - }) - number: string; -} - -export const UserMobileNumberSchema = DatabaseSchema(UserMobileNumberEntity); -export type UserMobileNumberDoc = IDatabaseDocument; - @DatabaseEntity({ collection: UserTableName }) -export class UserEntity extends DatabaseEntityAbstract { +export class UserEntity extends DatabaseEntityBase { @DatabaseProp({ required: true, index: true, @@ -73,6 +55,12 @@ export class UserEntity extends DatabaseEntityAbstract { }) mobileNumber?: UserMobileNumberEntity; + @DatabaseProp({ + required: true, + schema: UserVerificationSchema, + }) + verification: UserVerificationEntity; + @DatabaseProp({ required: true, unique: true, @@ -126,6 +114,7 @@ export class UserEntity extends DatabaseEntityAbstract { @DatabaseProp({ required: true, + type: String, enum: ENUM_USER_SIGN_UP_FROM, }) signUpFrom: ENUM_USER_SIGN_UP_FROM; @@ -153,6 +142,7 @@ export class UserEntity extends DatabaseEntityAbstract { @DatabaseProp({ required: false, + type: String, enum: ENUM_USER_GENDER, }) gender?: ENUM_USER_GENDER; diff --git a/src/modules/user/repository/entities/user.mobile-number.entity.ts b/src/modules/user/repository/entities/user.mobile-number.entity.ts new file mode 100644 index 000000000..4e76cf4b6 --- /dev/null +++ b/src/modules/user/repository/entities/user.mobile-number.entity.ts @@ -0,0 +1,33 @@ +import { + DatabaseEntity, + DatabaseProp, + DatabaseSchema, +} from 'src/common/database/decorators/database.decorator'; +import { IDatabaseDocument } from 'src/common/database/interfaces/database.interface'; +import { CountryEntity } from 'src/modules/country/repository/entities/country.entity'; + +@DatabaseEntity({ + _id: false, + timestamps: false, +}) +export class UserMobileNumberEntity { + @DatabaseProp({ + required: true, + type: String, + ref: CountryEntity.name, + trim: true, + }) + country: string; + + @DatabaseProp({ + required: false, + trim: true, + type: String, + maxlength: 20, + minlength: 8, + }) + number: string; +} + +export const UserMobileNumberSchema = DatabaseSchema(UserMobileNumberEntity); +export type UserMobileNumberDoc = IDatabaseDocument; diff --git a/src/modules/user/repository/entities/user.verification.entity.ts b/src/modules/user/repository/entities/user.verification.entity.ts new file mode 100644 index 000000000..e31f26655 --- /dev/null +++ b/src/modules/user/repository/entities/user.verification.entity.ts @@ -0,0 +1,22 @@ +import { + DatabaseEntity, + DatabaseProp, + DatabaseSchema, +} from 'src/common/database/decorators/database.decorator'; +import { IDatabaseDocument } from 'src/common/database/interfaces/database.interface'; + +@DatabaseEntity({ + _id: false, + timestamps: false, +}) +export class UserVerificationEntity { + @DatabaseProp({ + required: true, + index: true, + default: false, + }) + email: boolean; +} + +export const UserVerificationSchema = DatabaseSchema(UserVerificationEntity); +export type UserVerificationDoc = IDatabaseDocument; diff --git a/src/modules/user/repository/repositories/user.repository.ts b/src/modules/user/repository/repositories/user.repository.ts index d4e2291ae..a9c979a7e 100644 --- a/src/modules/user/repository/repositories/user.repository.ts +++ b/src/modules/user/repository/repositories/user.repository.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { Model, PopulateOptions } from 'mongoose'; -import { DatabaseRepositoryAbstract } from 'src/common/database/abstracts/database.repository.abstract'; -import { DatabaseModel } from 'src/common/database/decorators/database.decorator'; +import { DatabaseRepositoryBase } from 'src/common/database/bases/database.repository'; +import { InjectDatabaseModel } from 'src/common/database/decorators/database.decorator'; import { CountryEntity } from 'src/modules/country/repository/entities/country.entity'; import { RoleEntity } from 'src/modules/role/repository/entities/role.entity'; import { @@ -10,7 +10,7 @@ import { } from 'src/modules/user/repository/entities/user.entity'; @Injectable() -export class UserRepository extends DatabaseRepositoryAbstract< +export class UserRepository extends DatabaseRepositoryBase< UserEntity, UserDoc > { @@ -42,7 +42,7 @@ export class UserRepository extends DatabaseRepositoryAbstract< ]; constructor( - @DatabaseModel(UserEntity.name) + @InjectDatabaseModel(UserEntity.name) private readonly userModel: Model ) { super(userModel, [ diff --git a/src/modules/user/services/user.service.ts b/src/modules/user/services/user.service.ts index 98131afbf..109fd9acb 100644 --- a/src/modules/user/services/user.service.ts +++ b/src/modules/user/services/user.service.ts @@ -1,9 +1,11 @@ import { Injectable } from '@nestjs/common'; import { + IDatabaseAggregateOptions, IDatabaseCreateOptions, IDatabaseDeleteManyOptions, - IDatabaseExistOptions, + IDatabaseFindAllAggregateOptions, IDatabaseFindAllOptions, + IDatabaseFindOneOptions, IDatabaseGetTotalOptions, IDatabaseOptions, IDatabaseSaveOptions, @@ -13,8 +15,7 @@ import { HelperDateService } from 'src/common/helper/services/helper.date.servic import { ConfigService } from '@nestjs/config'; import { IAuthPassword } from 'src/modules/auth/interfaces/auth.interface'; import { plainToInstance } from 'class-transformer'; -import { Document } from 'mongoose'; -import { DatabaseQueryContain } from 'src/common/database/decorators/database.decorator'; +import { Document, PipelineStage } from 'mongoose'; import { IUserService } from 'src/modules/user/interfaces/user.service.interface'; import { UserRepository } from 'src/modules/user/repository/repositories/user.repository'; import { @@ -43,6 +44,10 @@ import { AuthSignUpRequestDto } from 'src/modules/auth/dtos/request/auth.sign-up import { UserUpdateClaimUsernameRequestDto } from 'src/modules/user/dtos/request/user.update-claim-username.dto'; import { DatabaseSoftDeleteDto } from 'src/common/database/dtos/database.soft-delete.dto'; import { UserUpdateProfileRequestDto } from 'src/modules/user/dtos/request/user.update-profile.dto'; +import { CountryTableName } from 'src/modules/country/repository/entities/country.entity'; +import { RoleTableName } from 'src/modules/role/repository/entities/role.entity'; +import { UserUpdateStatusRequestDto } from 'src/modules/user/dtos/request/user.update-status.request.dto'; +import { DatabaseHelperQueryContain } from 'src/common/database/decorators/database.decorator'; @Injectable() export class UserService implements IUserService { @@ -79,47 +84,116 @@ export class UserService implements IUserService { return this.userRepository.getTotal(find, options); } + createRawQueryFindAllWithRoleAndCountry( + find?: Record + ): PipelineStage[] { + return [ + { + $lookup: { + from: RoleTableName, + as: 'role', + foreignField: '_id', + localField: 'role', + }, + }, + { + $unwind: '$role', + }, + { + $lookup: { + from: CountryTableName, + as: 'mobileNumber.country', + foreignField: '_id', + localField: 'mobileNumber.country', + }, + }, + { + $unwind: { + path: '$mobileNumber.country', + preserveNullAndEmptyArrays: true, + }, + }, + { + $lookup: { + from: CountryTableName, + as: 'country', + foreignField: '_id', + localField: 'country', + }, + }, + { + $unwind: '$country', + }, + { + $match: find, + }, + ]; + } + + async findAllWithRoleAndCountry( + find?: Record, + options?: IDatabaseFindAllAggregateOptions + ): Promise { + const pipeline: PipelineStage[] = + this.createRawQueryFindAllWithRoleAndCountry(find); + + return this.userRepository.findAllAggregate( + pipeline, + options + ); + } + + async getTotalWithRoleAndCountry( + find?: Record, + options?: IDatabaseAggregateOptions + ): Promise { + const pipeline: PipelineStage[] = + this.createRawQueryFindAllWithRoleAndCountry(find); + + return this.userRepository.getTotalAggregate( + pipeline, + options + ); + } + async findOneById( _id: string, - options?: IDatabaseOptions + options?: IDatabaseFindOneOptions ): Promise { return this.userRepository.findOneById(_id, options); } async findOne( find: Record, - options?: IDatabaseOptions + options?: IDatabaseFindOneOptions ): Promise { return this.userRepository.findOne(find, options); } async findOneByEmail( email: string, - options?: IDatabaseOptions + options?: IDatabaseFindOneOptions ): Promise { return this.userRepository.findOne({ email }, options); } async findOneByMobileNumber( + country: string, mobileNumber: string, - options?: IDatabaseOptions + options?: IDatabaseFindOneOptions ): Promise { - return this.userRepository.findOne({ mobileNumber }, options); - } - - async findAllWithRoleAndCountry( - find?: Record, - options?: IDatabaseFindAllOptions - ): Promise { - return this.userRepository.findAll(find, { - ...options, - join: true, - }); + return this.userRepository.findOne( + { + 'mobileNumber.number': mobileNumber, + 'mobileNumber.country': country, + }, + options + ); } async findOneWithRoleAndCountry( find?: Record, - options?: IDatabaseFindAllOptions + options?: IDatabaseFindOneOptions ): Promise { return this.userRepository.findOne(find, { ...options, @@ -129,7 +203,7 @@ export class UserService implements IUserService { async findOneWithRoleAndCountryById( _id: string, - options?: IDatabaseFindAllOptions + options?: IDatabaseFindOneOptions ): Promise { return this.userRepository.findOneById(_id, { ...options, @@ -165,7 +239,7 @@ export class UserService implements IUserService { async findOneActiveById( _id: string, - options?: IDatabaseOptions + options?: IDatabaseFindOneOptions ): Promise { return this.userRepository.findOne( { _id, status: ENUM_USER_STATUS.ACTIVE }, @@ -178,7 +252,7 @@ export class UserService implements IUserService { async findOneActiveByEmail( email: string, - options?: IDatabaseOptions + options?: IDatabaseFindOneOptions ): Promise { return this.userRepository.findOne( { email, status: ENUM_USER_STATUS.ACTIVE }, @@ -190,12 +264,14 @@ export class UserService implements IUserService { } async findOneActiveByMobileNumber( + country: string, mobileNumber: string, - options?: IDatabaseOptions + options?: IDatabaseFindOneOptions ): Promise { return this.userRepository.findOne( { - mobileNumber, + 'mobileNumber.number': mobileNumber, + 'mobileNumber.country': country, status: ENUM_USER_STATUS.ACTIVE, }, { @@ -206,7 +282,7 @@ export class UserService implements IUserService { } async create( - { email, name, role, country }: UserCreateRequestDto, + { email, name, role, country, gender }: UserCreateRequestDto, { passwordExpired, passwordHash, salt, passwordCreated }: IAuthPassword, signUpFrom: ENUM_USER_SIGN_UP_FROM, options?: IDatabaseCreateOptions @@ -217,6 +293,7 @@ export class UserService implements IUserService { create.name = name; create.email = email; create.role = role; + create.gender = gender; create.status = ENUM_USER_STATUS.ACTIVE; create.password = passwordHash; create.salt = salt; @@ -227,6 +304,9 @@ export class UserService implements IUserService { create.signUpFrom = signUpFrom; create.country = country; create.username = username; + create.verification = { + email: false, + }; return this.userRepository.create(create, options); } @@ -253,38 +333,31 @@ export class UserService implements IUserService { create.signUpFrom = ENUM_USER_SIGN_UP_FROM.PUBLIC; create.country = country; create.username = username; + create.verification = { + email: false, + }; return this.userRepository.create(create, options); } async existByEmail( email: string, - options?: IDatabaseExistOptions + options?: IDatabaseOptions ): Promise { return this.userRepository.exists( - DatabaseQueryContain('email', email, { fullWord: true }), + DatabaseHelperQueryContain('email', email, { fullWord: true }), { ...options, withDeleted: true } ); } async existByUsername( username: string, - options?: IDatabaseExistOptions + options?: IDatabaseOptions ): Promise { return this.userRepository.exists( - DatabaseQueryContain('username', username, { fullWord: true }), - { ...options, withDeleted: true } - ); - } - - async existByMobileNumber( - mobileNumber: string, - options?: IDatabaseExistOptions - ): Promise { - return this.userRepository.exists( - { - mobileNumber, - }, + DatabaseHelperQueryContain('username', username, { + fullWord: true, + }), { ...options, withDeleted: true } ); } @@ -312,29 +385,12 @@ export class UserService implements IUserService { return this.userRepository.save(repository, options); } - async active( + async updateStatus( repository: UserDoc, + { status }: UserUpdateStatusRequestDto, options?: IDatabaseSaveOptions ): Promise { - repository.status = ENUM_USER_STATUS.ACTIVE; - - return this.userRepository.save(repository, options); - } - - async inactive( - repository: UserDoc, - options?: IDatabaseSaveOptions - ): Promise { - repository.status = ENUM_USER_STATUS.INACTIVE; - - return this.userRepository.save(repository, options); - } - - async blocked( - repository: UserDoc, - options?: IDatabaseSaveOptions - ): Promise { - repository.status = ENUM_USER_STATUS.BLOCKED; + repository.status = status; return this.userRepository.save(repository, options); } @@ -385,12 +441,13 @@ export class UserService implements IUserService { async update( repository: UserDoc, - { country, name, role }: UserUpdateRequestDto, + { country, name, role, gender }: UserUpdateRequestDto, options?: IDatabaseSaveOptions ): Promise { repository.country = country; repository.name = name; repository.role = role; + repository.gender = gender; return this.userRepository.save(repository, options); } @@ -427,7 +484,7 @@ export class UserService implements IUserService { return this.userRepository.save(repository, options); } - async delete( + async softDelete( repository: UserDoc, dto: DatabaseSoftDeleteDto, options?: IDatabaseSaveOptions @@ -439,24 +496,28 @@ export class UserService implements IUserService { find: Record, options?: IDatabaseDeleteManyOptions ): Promise { - try { - await this.userRepository.deleteMany(find, options); + await this.userRepository.deleteMany(find, options); - return true; - } catch (error: unknown) { - throw error; - } + return true; } async updateProfile( repository: UserDoc, - { country, name, address, familyName }: UserUpdateProfileRequestDto, + { country, name, gender }: UserUpdateProfileRequestDto, options?: IDatabaseSaveOptions ): Promise { repository.country = country; repository.name = name; - repository.address = address; - repository.familyName = familyName; + repository.gender = gender; + + return this.userRepository.save(repository, options); + } + + async updateVerificationEmail( + repository: UserDoc, + options?: IDatabaseSaveOptions + ): Promise { + repository.verification.email = true; return this.userRepository.save(repository, options); } diff --git a/src/modules/user/user.module.ts b/src/modules/user/user.module.ts index 8f900389c..304d331f7 100644 --- a/src/modules/user/user.module.ts +++ b/src/modules/user/user.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { UserService } from './services/user.service'; import { UserRepositoryModule } from 'src/modules/user/repository/user.repository.module'; +import { UserService } from 'src/modules/user/services/user.service'; @Module({ imports: [UserRepositoryModule], diff --git a/src/modules/verification/verification.module.ts b/src/modules/verification/verification.module.ts index 4a4281736..5ca60fa97 100644 --- a/src/modules/verification/verification.module.ts +++ b/src/modules/verification/verification.module.ts @@ -1 +1,2 @@ // TODO: VERIFICATION EMAIL MODULE +// 5060 diff --git a/src/router/routes/routes.admin.module.ts b/src/router/routes/routes.admin.module.ts index d59418afc..c25f9f044 100644 --- a/src/router/routes/routes.admin.module.ts +++ b/src/router/routes/routes.admin.module.ts @@ -1,29 +1,33 @@ import { BullModule } from '@nestjs/bullmq'; import { Module } from '@nestjs/common'; +import { ActivityModule } from 'src/modules/activity/activity.module'; +import { ActivityAdminController } from 'src/modules/activity/controllers/activity.admin.controller'; import { ApiKeyModule } from 'src/modules/api-key/api-key.module'; import { ApiKeyAdminController } from 'src/modules/api-key/controllers/api-key.admin.controller'; import { AuthModule } from 'src/modules/auth/auth.module'; import { AuthAdminController } from 'src/modules/auth/controllers/auth.admin.controller'; -import { CountryAdminController } from 'src/modules/country/controllers/country.admin.controller'; import { CountryModule } from 'src/modules/country/country.module'; import { EmailModule } from 'src/modules/email/email.module'; +import { PasswordHistoryAdminController } from 'src/modules/password-history/controllers/password-history.admin.controller'; +import { PasswordHistoryModule } from 'src/modules/password-history/password-history.module'; import { RoleAdminController } from 'src/modules/role/controllers/role.admin.controller'; import { RoleModule } from 'src/modules/role/role.module'; -import { SettingAdminController } from 'src/modules/setting/controllers/setting.admin.controller'; +import { SessionAdminController } from 'src/modules/session/controllers/session.admin.controller'; +import { SessionModule } from 'src/modules/session/session.module'; import { SettingModule } from 'src/modules/setting/setting.module'; import { UserAdminController } from 'src/modules/user/controllers/user.admin.controller'; import { UserModule } from 'src/modules/user/user.module'; -import { WORKER_CONNECTION_NAME } from 'src/worker/constants/worker.constant'; import { ENUM_WORKER_QUEUES } from 'src/worker/enums/worker.enum'; @Module({ controllers: [ ApiKeyAdminController, - SettingAdminController, RoleAdminController, UserAdminController, - CountryAdminController, AuthAdminController, + SessionAdminController, + PasswordHistoryAdminController, + ActivityAdminController, ], providers: [], exports: [], @@ -35,10 +39,10 @@ import { ENUM_WORKER_QUEUES } from 'src/worker/enums/worker.enum'; AuthModule, EmailModule, CountryModule, - BullModule.registerQueue({ - connection: { - name: WORKER_CONNECTION_NAME, - }, + SessionModule, + PasswordHistoryModule, + ActivityModule, + BullModule.registerQueueAsync({ name: ENUM_WORKER_QUEUES.EMAIL_QUEUE, }), ], diff --git a/src/router/routes/routes.public.module.ts b/src/router/routes/routes.public.module.ts index beefe783c..44df07ccd 100644 --- a/src/router/routes/routes.public.module.ts +++ b/src/router/routes/routes.public.module.ts @@ -1,14 +1,16 @@ import { BullModule } from '@nestjs/bullmq'; import { Module } from '@nestjs/common'; +import { ActivityModule } from 'src/modules/activity/activity.module'; import { AuthModule } from 'src/modules/auth/auth.module'; import { AuthPublicController } from 'src/modules/auth/controllers/auth.public.controller'; import { CountryModule } from 'src/modules/country/country.module'; import { EmailModule } from 'src/modules/email/email.module'; import { HelloPublicController } from 'src/modules/hello/controllers/hello.public.controller'; +import { PasswordHistoryModule } from 'src/modules/password-history/password-history.module'; import { RoleModule } from 'src/modules/role/role.module'; +import { SessionModule } from 'src/modules/session/session.module'; import { SettingModule } from 'src/modules/setting/setting.module'; import { UserModule } from 'src/modules/user/user.module'; -import { WORKER_CONNECTION_NAME } from 'src/worker/constants/worker.constant'; import { ENUM_WORKER_QUEUES } from 'src/worker/enums/worker.enum'; @Module({ @@ -22,10 +24,10 @@ import { ENUM_WORKER_QUEUES } from 'src/worker/enums/worker.enum'; RoleModule, EmailModule, CountryModule, - BullModule.registerQueue({ - connection: { - name: WORKER_CONNECTION_NAME, - }, + PasswordHistoryModule, + SessionModule, + ActivityModule, + BullModule.registerQueueAsync({ name: ENUM_WORKER_QUEUES.EMAIL_QUEUE, }), ], diff --git a/src/router/routes/routes.shared.module.ts b/src/router/routes/routes.shared.module.ts index 3e9340662..f16eb9510 100644 --- a/src/router/routes/routes.shared.module.ts +++ b/src/router/routes/routes.shared.module.ts @@ -1,17 +1,28 @@ import { BullModule } from '@nestjs/bullmq'; import { Module } from '@nestjs/common'; +import { ActivityModule } from 'src/modules/activity/activity.module'; +import { ActivitySharedController } from 'src/modules/activity/controllers/activity.shared.controller'; import { AuthModule } from 'src/modules/auth/auth.module'; import { AuthSharedController } from 'src/modules/auth/controllers/auth.shared.controller'; import { AwsModule } from 'src/modules/aws/aws.module'; import { CountryModule } from 'src/modules/country/country.module'; import { EmailModule } from 'src/modules/email/email.module'; +import { PasswordHistorySharedController } from 'src/modules/password-history/controllers/password-history.shared.controller'; +import { PasswordHistoryModule } from 'src/modules/password-history/password-history.module'; +import { SessionSharedController } from 'src/modules/session/controllers/session.shared.controller'; +import { SessionModule } from 'src/modules/session/session.module'; import { UserSharedController } from 'src/modules/user/controllers/user.shared.controller'; import { UserModule } from 'src/modules/user/user.module'; -import { WORKER_CONNECTION_NAME } from 'src/worker/constants/worker.constant'; import { ENUM_WORKER_QUEUES } from 'src/worker/enums/worker.enum'; @Module({ - controllers: [UserSharedController, AuthSharedController], + controllers: [ + UserSharedController, + AuthSharedController, + SessionSharedController, + PasswordHistorySharedController, + ActivitySharedController, + ], providers: [], exports: [], imports: [ @@ -20,10 +31,10 @@ import { ENUM_WORKER_QUEUES } from 'src/worker/enums/worker.enum'; AuthModule, AwsModule, CountryModule, - BullModule.registerQueue({ - connection: { - name: WORKER_CONNECTION_NAME, - }, + SessionModule, + PasswordHistoryModule, + ActivityModule, + BullModule.registerQueueAsync({ name: ENUM_WORKER_QUEUES.EMAIL_QUEUE, }), ], diff --git a/src/router/routes/routes.system.module.ts b/src/router/routes/routes.system.module.ts index dd4447d6f..e3c1bc552 100644 --- a/src/router/routes/routes.system.module.ts +++ b/src/router/routes/routes.system.module.ts @@ -8,6 +8,7 @@ import { RoleSystemController } from 'src/modules/role/controllers/role.system.c import { RoleModule } from 'src/modules/role/role.module'; import { SettingSystemController } from 'src/modules/setting/controllers/setting.system.controller'; import { SettingModule } from 'src/modules/setting/setting.module'; +import { UserSystemController } from 'src/modules/user/controllers/user.system.controller'; import { UserModule } from 'src/modules/user/user.module'; @Module({ @@ -16,6 +17,7 @@ import { UserModule } from 'src/modules/user/user.module'; SettingSystemController, CountrySystemController, RoleSystemController, + UserSystemController, ], providers: [], exports: [], diff --git a/src/router/routes/routes.user.module.ts b/src/router/routes/routes.user.module.ts index 15eb3427b..418de0a9c 100644 --- a/src/router/routes/routes.user.module.ts +++ b/src/router/routes/routes.user.module.ts @@ -1,5 +1,7 @@ import { Module } from '@nestjs/common'; +import { ActivityModule } from 'src/modules/activity/activity.module'; import { AuthModule } from 'src/modules/auth/auth.module'; +import { SessionModule } from 'src/modules/session/session.module'; import { UserUserController } from 'src/modules/user/controllers/user.user.controller'; import { UserModule } from 'src/modules/user/user.module'; @@ -7,6 +9,6 @@ import { UserModule } from 'src/modules/user/user.module'; controllers: [UserUserController], providers: [], exports: [], - imports: [UserModule, AuthModule], + imports: [UserModule, AuthModule, ActivityModule, SessionModule], }) export class RoutesUserModule {} diff --git a/src/swagger.ts b/src/swagger.ts index 8b22a479e..69e793c6f 100644 --- a/src/swagger.ts +++ b/src/swagger.ts @@ -65,14 +65,6 @@ export default async function (app: NestApplication) { }, }); - logger.log( - `==========================================================` - ); - logger.log(`Docs will serve on ${docPrefix}`, 'NestApplication'); - - logger.log( - `==========================================================` - ); } } diff --git a/src/worker/constants/worker.constant.ts b/src/worker/constants/worker.constant.ts deleted file mode 100644 index 78b8cfa15..000000000 --- a/src/worker/constants/worker.constant.ts +++ /dev/null @@ -1 +0,0 @@ -export const WORKER_CONNECTION_NAME = 'PrimaryConnectionWorker'; diff --git a/src/worker/decorators/worker.decorator.ts b/src/worker/decorators/worker.decorator.ts deleted file mode 100644 index 301e0d03f..000000000 --- a/src/worker/decorators/worker.decorator.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { InjectQueue } from '@nestjs/bullmq'; - -export function WorkerQueue(queue: string): ParameterDecorator { - return InjectQueue(queue); -} diff --git a/src/worker/enums/worker.enum.ts b/src/worker/enums/worker.enum.ts index edf1e5630..84f4d32b1 100644 --- a/src/worker/enums/worker.enum.ts +++ b/src/worker/enums/worker.enum.ts @@ -1,3 +1,10 @@ export enum ENUM_WORKER_QUEUES { EMAIL_QUEUE = 'EMAIL_QUEUE', + SESSION_QUEUE = 'SESSION_QUEUE', +} + +export enum ENUM_WORKER_PRIORITY { + HIGH = 1, + MEDIUM = 5, + LOW = 10, } diff --git a/src/worker/worker.module.ts b/src/worker/worker.module.ts index 83da39235..0b3a5b01c 100644 --- a/src/worker/worker.module.ts +++ b/src/worker/worker.module.ts @@ -1,34 +1,11 @@ -import { BullModule } from '@nestjs/bullmq'; import { Module } from '@nestjs/common'; -import { ConfigModule, ConfigService } from '@nestjs/config'; import { EmailModule } from 'src/modules/email/email.module'; import { EmailProcessor } from 'src/modules/email/processors/email.processor'; -import { WORKER_CONNECTION_NAME } from 'src/worker/constants/worker.constant'; +import { SessionProcessor } from 'src/modules/session/processors/session.processor'; +import { SessionModule } from 'src/modules/session/session.module'; @Module({ - imports: [ - BullModule.forRootAsync({ - imports: [ConfigModule], - inject: [ConfigService], - useFactory: (configService: ConfigService) => ({ - connection: { - name: WORKER_CONNECTION_NAME, - host: configService.get('redis.host'), - port: configService.get('redis.port'), - password: configService.get('redis.password'), - tls: configService.get('redis.tls'), - }, - defaultJobOptions: { - backoff: { - type: 'exponential', - delay: 3000, - }, - attempts: 3, - }, - }), - }), - EmailModule, - ], - providers: [EmailProcessor], + imports: [EmailModule, SessionModule], + providers: [EmailProcessor, SessionProcessor], }) export class WorkerModule {} diff --git a/src/modules/email/templates/email.change-password.template.html b/templates/email/change-password.template.html similarity index 100% rename from src/modules/email/templates/email.change-password.template.html rename to templates/email/change-password.template.html diff --git a/src/modules/email/templates/email.temp-password.template.html b/templates/email/temp-password.template.html similarity index 100% rename from src/modules/email/templates/email.temp-password.template.html rename to templates/email/temp-password.template.html diff --git a/src/modules/email/templates/email.welcome-admin.template.html b/templates/email/welcome-admin.template.html similarity index 100% rename from src/modules/email/templates/email.welcome-admin.template.html rename to templates/email/welcome-admin.template.html diff --git a/src/modules/email/templates/email.welcome.template.html b/templates/email/welcome.template.html similarity index 100% rename from src/modules/email/templates/email.welcome.template.html rename to templates/email/welcome.template.html diff --git a/test/app/filters/app.http.filter.spec.ts b/test/app/filters/app.http.filter.spec.ts index 9269ed5e9..82500eb48 100644 --- a/test/app/filters/app.http.filter.spec.ts +++ b/test/app/filters/app.http.filter.spec.ts @@ -24,7 +24,7 @@ describe('AppHttpFilter', () => { mockConfigService = { get: jest.fn().mockImplementation((key: string) => { const config = { - 'app.debug': true, + 'debug.enable': true, 'app.globalPrefix': 'api', 'doc.prefix': 'docs', }; @@ -111,7 +111,7 @@ describe('AppHttpFilter', () => { __language: null, __version: null, } as IRequestApp; - appHttpFilter['app.debug'] = true; + appHttpFilter['debug.enable'] = true; appHttpFilter['app.globalPrefix'] = '/path'; const mockArgumentsHost = { diff --git a/test/app/filters/app.validation-import.filter.spec.ts b/test/app/filters/app.validation-import.filter.spec.ts index c64f3679e..e817d43b1 100644 --- a/test/app/filters/app.validation-import.filter.spec.ts +++ b/test/app/filters/app.validation-import.filter.spec.ts @@ -18,7 +18,7 @@ class MockMessageService { class MockConfigService { get = jest.fn().mockImplementation((key: string) => { const config = { - 'app.debug': true, + 'debug.enable': true, 'app.urlVersion.version': '1.0', 'app.repoVersion': 'v1.0.0', }; diff --git a/test/app/filters/app.validation.filter.spec.ts b/test/app/filters/app.validation.filter.spec.ts index a903b37e1..68e8cf5ff 100644 --- a/test/app/filters/app.validation.filter.spec.ts +++ b/test/app/filters/app.validation.filter.spec.ts @@ -17,7 +17,7 @@ class MockMessageService { class MockConfigService { get = jest.fn().mockImplementation((key: string) => { const config = { - 'app.debug': true, + 'debug.enable': true, 'app.urlVersion.version': '1.0', 'app.repoVersion': 'v1.0.0', }; diff --git a/test/jest.json b/test/jest.json index d2e3f6ab5..d71e8fc47 100644 --- a/test/jest.json +++ b/test/jest.json @@ -47,7 +47,7 @@ }, "moduleFileExtensions": ["js", "ts", "json"], "transform": { - "^.+\\.(t|j)s$": "ts-jest" + "^.+\\.(t|j)s?$": ["@swc/jest"] }, "modulePathIgnorePatterns": ["/dist"] } diff --git a/test/modules/api-key/guards/x-api-key/strategies/api-key.x-api-key.strategy.spec.ts b/test/modules/api-key/guards/x-api-key/strategies/api-key.x-api-key.strategy.spec.ts index e2c3a026a..56bdc169c 100644 --- a/test/modules/api-key/guards/x-api-key/strategies/api-key.x-api-key.strategy.spec.ts +++ b/test/modules/api-key/guards/x-api-key/strategies/api-key.x-api-key.strategy.spec.ts @@ -44,7 +44,7 @@ describe('ApiKeyXApiKeyStrategy', () => { const mockRequest: IRequestApp = { apiKey: null } as IRequestApp; const callback = ( - error: Error, + _error: Error, verified: ( error: Error, user?: Record, diff --git a/tsconfig.build.json b/tsconfig.build.json deleted file mode 100644 index 6190fbf82..000000000 --- a/tsconfig.build.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "./tsconfig.json", - "exclude": [ - "node_modules", - "dist", - "*coverage", - "logs", - "test", - "data" - ] -} diff --git a/tsconfig.json b/tsconfig.json index f11445436..2be804f9f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,32 +1,25 @@ { - "compilerOptions": { - "module": "commonjs", - "declaration": true, - "removeComments": true, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "useDefineForClassFields": false, - "target": "ESNext", - "sourceMap": true, - "outDir": "./dist", - "baseUrl": "./", - "incremental": true, - "skipLibCheck": true, - "allowJs": false, - "esModuleInterop": true, - "moduleResolution": "node", - "resolveJsonModule": true - }, - "include": [ - "src", - "test", - ], - "exclude": [ - "node_modules", - "dist", - "coverage", - "logs", - "data" - ] + "compilerOptions": { + "module": "commonjs", + "declaration": true, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "allowSyntheticDefaultImports": true, + "useDefineForClassFields": false, + "target": "ESNext", + "sourceMap": true, + "inlineSources": true, + "sourceRoot": "/", + "outDir": "./dist", + "baseUrl": "./", + "incremental": true, + "skipLibCheck": true, + "allowJs": false, + "esModuleInterop": true, + "moduleResolution": "node", + "resolveJsonModule": true + }, + "include": ["src", "test"], + "exclude": ["node_modules", "dist", "coverage", "logs", "data"] } diff --git a/yarn.lock b/yarn.lock index f39f73c93..a7f513e80 100644 --- a/yarn.lock +++ b/yarn.lock @@ -26,6 +26,18 @@ rxjs "7.8.1" source-map "0.7.4" +"@angular-devkit/core@17.3.11": + version "17.3.11" + resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-17.3.11.tgz#a74b042ec06cf626d5a2f6a3971b156c6759fe09" + integrity sha512-vTNDYNsLIWpYk2I969LMQFH29GTsLzxNk/0cLw5q56ARF0v5sIWfHYwGTS88jdDqIpuuettcSczbxeA7EuAmqQ== + dependencies: + ajv "8.12.0" + ajv-formats "2.1.1" + jsonc-parser "3.2.1" + picomatch "4.0.1" + rxjs "7.8.1" + source-map "0.7.4" + "@angular-devkit/core@17.3.8": version "17.3.8" resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-17.3.8.tgz#8679cacf84cf79764f027811020e235ab32016d2" @@ -61,6 +73,17 @@ ora "5.4.1" rxjs "7.8.1" +"@angular-devkit/schematics@17.3.11": + version "17.3.11" + resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-17.3.11.tgz#37095fb08b0ab0343c7c0dde57ca81115178714f" + integrity sha512-I5wviiIqiFwar9Pdk30Lujk8FczEEc18i22A5c6Z9lbmhPQdTroDnEQdsfXjy404wPe8H62s0I15o4pmMGfTYQ== + dependencies: + "@angular-devkit/core" "17.3.11" + jsonc-parser "3.2.1" + magic-string "0.30.8" + ora "5.4.1" + rxjs "7.8.1" + "@angular-devkit/schematics@17.3.8": version "17.3.8" resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-17.3.8.tgz#f853eb21682aadfb6667e090b5b509fc95ce8442" @@ -140,533 +163,545 @@ "@smithy/util-utf8" "^2.0.0" tslib "^2.6.2" -"@aws-sdk/client-s3@^3.629.0": - version "3.629.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-s3/-/client-s3-3.629.0.tgz#6c22639c0cdb73b05409b5633a87010bfec7b107" - integrity sha512-Q0YXKdUA7NboPl94JOKD4clHHuERG1Kwy0JPbU+3Hvmz/UuwUGBmlfaRAqd9y4LXsTv/2xKtFPW9R+nBfy9mwA== +"@aws-sdk/client-s3@^3.685.0": + version "3.685.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-s3/-/client-s3-3.685.0.tgz#bf2fd9fe310a2d20fdaf3585755e9a8416d08c2b" + integrity sha512-ClvMeQHbLhWkpxnVymo4qWS5/yZcPXjorDbSday3joCWYWCSHTO409nWd+jx6eA4MKT/EY/uJ6ZBJRFfByKLuA== dependencies: "@aws-crypto/sha1-browser" "5.2.0" "@aws-crypto/sha256-browser" "5.2.0" "@aws-crypto/sha256-js" "5.2.0" - "@aws-sdk/client-sso-oidc" "3.629.0" - "@aws-sdk/client-sts" "3.629.0" - "@aws-sdk/core" "3.629.0" - "@aws-sdk/credential-provider-node" "3.629.0" - "@aws-sdk/middleware-bucket-endpoint" "3.620.0" - "@aws-sdk/middleware-expect-continue" "3.620.0" - "@aws-sdk/middleware-flexible-checksums" "3.620.0" - "@aws-sdk/middleware-host-header" "3.620.0" - "@aws-sdk/middleware-location-constraint" "3.609.0" - "@aws-sdk/middleware-logger" "3.609.0" - "@aws-sdk/middleware-recursion-detection" "3.620.0" - "@aws-sdk/middleware-sdk-s3" "3.629.0" - "@aws-sdk/middleware-ssec" "3.609.0" - "@aws-sdk/middleware-user-agent" "3.620.0" - "@aws-sdk/region-config-resolver" "3.614.0" - "@aws-sdk/signature-v4-multi-region" "3.629.0" - "@aws-sdk/types" "3.609.0" - "@aws-sdk/util-endpoints" "3.614.0" - "@aws-sdk/util-user-agent-browser" "3.609.0" - "@aws-sdk/util-user-agent-node" "3.614.0" - "@aws-sdk/xml-builder" "3.609.0" - "@smithy/config-resolver" "^3.0.5" - "@smithy/core" "^2.3.2" - "@smithy/eventstream-serde-browser" "^3.0.6" - "@smithy/eventstream-serde-config-resolver" "^3.0.3" - "@smithy/eventstream-serde-node" "^3.0.5" - "@smithy/fetch-http-handler" "^3.2.4" - "@smithy/hash-blob-browser" "^3.1.2" - "@smithy/hash-node" "^3.0.3" - "@smithy/hash-stream-node" "^3.1.2" - "@smithy/invalid-dependency" "^3.0.3" - "@smithy/md5-js" "^3.0.3" - "@smithy/middleware-content-length" "^3.0.5" - "@smithy/middleware-endpoint" "^3.1.0" - "@smithy/middleware-retry" "^3.0.14" - "@smithy/middleware-serde" "^3.0.3" - "@smithy/middleware-stack" "^3.0.3" - "@smithy/node-config-provider" "^3.1.4" - "@smithy/node-http-handler" "^3.1.4" - "@smithy/protocol-http" "^4.1.0" - "@smithy/smithy-client" "^3.1.12" - "@smithy/types" "^3.3.0" - "@smithy/url-parser" "^3.0.3" + "@aws-sdk/client-sso-oidc" "3.682.0" + "@aws-sdk/client-sts" "3.682.0" + "@aws-sdk/core" "3.679.0" + "@aws-sdk/credential-provider-node" "3.682.0" + "@aws-sdk/middleware-bucket-endpoint" "3.679.0" + "@aws-sdk/middleware-expect-continue" "3.679.0" + "@aws-sdk/middleware-flexible-checksums" "3.682.0" + "@aws-sdk/middleware-host-header" "3.679.0" + "@aws-sdk/middleware-location-constraint" "3.679.0" + "@aws-sdk/middleware-logger" "3.679.0" + "@aws-sdk/middleware-recursion-detection" "3.679.0" + "@aws-sdk/middleware-sdk-s3" "3.685.0" + "@aws-sdk/middleware-ssec" "3.679.0" + "@aws-sdk/middleware-user-agent" "3.682.0" + "@aws-sdk/region-config-resolver" "3.679.0" + "@aws-sdk/signature-v4-multi-region" "3.685.0" + "@aws-sdk/types" "3.679.0" + "@aws-sdk/util-endpoints" "3.679.0" + "@aws-sdk/util-user-agent-browser" "3.679.0" + "@aws-sdk/util-user-agent-node" "3.682.0" + "@aws-sdk/xml-builder" "3.679.0" + "@smithy/config-resolver" "^3.0.9" + "@smithy/core" "^2.4.8" + "@smithy/eventstream-serde-browser" "^3.0.10" + "@smithy/eventstream-serde-config-resolver" "^3.0.7" + "@smithy/eventstream-serde-node" "^3.0.9" + "@smithy/fetch-http-handler" "^3.2.9" + "@smithy/hash-blob-browser" "^3.1.6" + "@smithy/hash-node" "^3.0.7" + "@smithy/hash-stream-node" "^3.1.6" + "@smithy/invalid-dependency" "^3.0.7" + "@smithy/md5-js" "^3.0.7" + "@smithy/middleware-content-length" "^3.0.9" + "@smithy/middleware-endpoint" "^3.1.4" + "@smithy/middleware-retry" "^3.0.23" + "@smithy/middleware-serde" "^3.0.7" + "@smithy/middleware-stack" "^3.0.7" + "@smithy/node-config-provider" "^3.1.8" + "@smithy/node-http-handler" "^3.2.4" + "@smithy/protocol-http" "^4.1.4" + "@smithy/smithy-client" "^3.4.0" + "@smithy/types" "^3.5.0" + "@smithy/url-parser" "^3.0.7" "@smithy/util-base64" "^3.0.0" "@smithy/util-body-length-browser" "^3.0.0" "@smithy/util-body-length-node" "^3.0.0" - "@smithy/util-defaults-mode-browser" "^3.0.14" - "@smithy/util-defaults-mode-node" "^3.0.14" - "@smithy/util-endpoints" "^2.0.5" - "@smithy/util-middleware" "^3.0.3" - "@smithy/util-retry" "^3.0.3" - "@smithy/util-stream" "^3.1.3" + "@smithy/util-defaults-mode-browser" "^3.0.23" + "@smithy/util-defaults-mode-node" "^3.0.23" + "@smithy/util-endpoints" "^2.1.3" + "@smithy/util-middleware" "^3.0.7" + "@smithy/util-retry" "^3.0.7" + "@smithy/util-stream" "^3.1.9" "@smithy/util-utf8" "^3.0.0" - "@smithy/util-waiter" "^3.1.2" + "@smithy/util-waiter" "^3.1.6" tslib "^2.6.2" -"@aws-sdk/client-ses@^3.629.0": - version "3.629.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-ses/-/client-ses-3.629.0.tgz#e54e01aa38540b414b1546ad644bbb5bd11ccbec" - integrity sha512-KreCdUAO/gIzWCgnPV1/dGUvLDDTdXI3fZzjjHUWFa1bE4wENjenNnWGw0qZgc8xB8pgiMdgPn7N+JvxJ7c/ZQ== +"@aws-sdk/client-ses@^3.682.0": + version "3.682.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-ses/-/client-ses-3.682.0.tgz#0b1a6af05879129a0c06662cf1b547ad3323ca30" + integrity sha512-N0CAEwWSlmgGw6FjQG9+0bVis9ucvXnc9924nHVjBCrem8+oAUfR/FrrMTedn/VausxCXCgHZYPDZwFlFFQGmA== dependencies: "@aws-crypto/sha256-browser" "5.2.0" "@aws-crypto/sha256-js" "5.2.0" - "@aws-sdk/client-sso-oidc" "3.629.0" - "@aws-sdk/client-sts" "3.629.0" - "@aws-sdk/core" "3.629.0" - "@aws-sdk/credential-provider-node" "3.629.0" - "@aws-sdk/middleware-host-header" "3.620.0" - "@aws-sdk/middleware-logger" "3.609.0" - "@aws-sdk/middleware-recursion-detection" "3.620.0" - "@aws-sdk/middleware-user-agent" "3.620.0" - "@aws-sdk/region-config-resolver" "3.614.0" - "@aws-sdk/types" "3.609.0" - "@aws-sdk/util-endpoints" "3.614.0" - "@aws-sdk/util-user-agent-browser" "3.609.0" - "@aws-sdk/util-user-agent-node" "3.614.0" - "@smithy/config-resolver" "^3.0.5" - "@smithy/core" "^2.3.2" - "@smithy/fetch-http-handler" "^3.2.4" - "@smithy/hash-node" "^3.0.3" - "@smithy/invalid-dependency" "^3.0.3" - "@smithy/middleware-content-length" "^3.0.5" - "@smithy/middleware-endpoint" "^3.1.0" - "@smithy/middleware-retry" "^3.0.14" - "@smithy/middleware-serde" "^3.0.3" - "@smithy/middleware-stack" "^3.0.3" - "@smithy/node-config-provider" "^3.1.4" - "@smithy/node-http-handler" "^3.1.4" - "@smithy/protocol-http" "^4.1.0" - "@smithy/smithy-client" "^3.1.12" - "@smithy/types" "^3.3.0" - "@smithy/url-parser" "^3.0.3" + "@aws-sdk/client-sso-oidc" "3.682.0" + "@aws-sdk/client-sts" "3.682.0" + "@aws-sdk/core" "3.679.0" + "@aws-sdk/credential-provider-node" "3.682.0" + "@aws-sdk/middleware-host-header" "3.679.0" + "@aws-sdk/middleware-logger" "3.679.0" + "@aws-sdk/middleware-recursion-detection" "3.679.0" + "@aws-sdk/middleware-user-agent" "3.682.0" + "@aws-sdk/region-config-resolver" "3.679.0" + "@aws-sdk/types" "3.679.0" + "@aws-sdk/util-endpoints" "3.679.0" + "@aws-sdk/util-user-agent-browser" "3.679.0" + "@aws-sdk/util-user-agent-node" "3.682.0" + "@smithy/config-resolver" "^3.0.9" + "@smithy/core" "^2.4.8" + "@smithy/fetch-http-handler" "^3.2.9" + "@smithy/hash-node" "^3.0.7" + "@smithy/invalid-dependency" "^3.0.7" + "@smithy/middleware-content-length" "^3.0.9" + "@smithy/middleware-endpoint" "^3.1.4" + "@smithy/middleware-retry" "^3.0.23" + "@smithy/middleware-serde" "^3.0.7" + "@smithy/middleware-stack" "^3.0.7" + "@smithy/node-config-provider" "^3.1.8" + "@smithy/node-http-handler" "^3.2.4" + "@smithy/protocol-http" "^4.1.4" + "@smithy/smithy-client" "^3.4.0" + "@smithy/types" "^3.5.0" + "@smithy/url-parser" "^3.0.7" "@smithy/util-base64" "^3.0.0" "@smithy/util-body-length-browser" "^3.0.0" "@smithy/util-body-length-node" "^3.0.0" - "@smithy/util-defaults-mode-browser" "^3.0.14" - "@smithy/util-defaults-mode-node" "^3.0.14" - "@smithy/util-endpoints" "^2.0.5" - "@smithy/util-middleware" "^3.0.3" - "@smithy/util-retry" "^3.0.3" + "@smithy/util-defaults-mode-browser" "^3.0.23" + "@smithy/util-defaults-mode-node" "^3.0.23" + "@smithy/util-endpoints" "^2.1.3" + "@smithy/util-middleware" "^3.0.7" + "@smithy/util-retry" "^3.0.7" "@smithy/util-utf8" "^3.0.0" - "@smithy/util-waiter" "^3.1.2" + "@smithy/util-waiter" "^3.1.6" tslib "^2.6.2" -"@aws-sdk/client-sso-oidc@3.629.0": - version "3.629.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.629.0.tgz#8bd4138c4ff24962e0f2753cfa9722a18330ad1f" - integrity sha512-3if0LauNJPqubGYf8vnlkp+B3yAeKRuRNxfNbHlE6l510xWGcKK/ZsEmiFmfePzKKSRrDh/cxMFMScgOrXptNg== +"@aws-sdk/client-sso-oidc@3.682.0": + version "3.682.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.682.0.tgz#423d6b3179fe560a515e3b286689414590f3263b" + integrity sha512-ZPZ7Y/r/w3nx/xpPzGSqSQsB090Xk5aZZOH+WBhTDn/pBEuim09BYXCLzvvxb7R7NnuoQdrTJiwimdJAhHl7ZQ== dependencies: "@aws-crypto/sha256-browser" "5.2.0" "@aws-crypto/sha256-js" "5.2.0" - "@aws-sdk/core" "3.629.0" - "@aws-sdk/credential-provider-node" "3.629.0" - "@aws-sdk/middleware-host-header" "3.620.0" - "@aws-sdk/middleware-logger" "3.609.0" - "@aws-sdk/middleware-recursion-detection" "3.620.0" - "@aws-sdk/middleware-user-agent" "3.620.0" - "@aws-sdk/region-config-resolver" "3.614.0" - "@aws-sdk/types" "3.609.0" - "@aws-sdk/util-endpoints" "3.614.0" - "@aws-sdk/util-user-agent-browser" "3.609.0" - "@aws-sdk/util-user-agent-node" "3.614.0" - "@smithy/config-resolver" "^3.0.5" - "@smithy/core" "^2.3.2" - "@smithy/fetch-http-handler" "^3.2.4" - "@smithy/hash-node" "^3.0.3" - "@smithy/invalid-dependency" "^3.0.3" - "@smithy/middleware-content-length" "^3.0.5" - "@smithy/middleware-endpoint" "^3.1.0" - "@smithy/middleware-retry" "^3.0.14" - "@smithy/middleware-serde" "^3.0.3" - "@smithy/middleware-stack" "^3.0.3" - "@smithy/node-config-provider" "^3.1.4" - "@smithy/node-http-handler" "^3.1.4" - "@smithy/protocol-http" "^4.1.0" - "@smithy/smithy-client" "^3.1.12" - "@smithy/types" "^3.3.0" - "@smithy/url-parser" "^3.0.3" + "@aws-sdk/core" "3.679.0" + "@aws-sdk/credential-provider-node" "3.682.0" + "@aws-sdk/middleware-host-header" "3.679.0" + "@aws-sdk/middleware-logger" "3.679.0" + "@aws-sdk/middleware-recursion-detection" "3.679.0" + "@aws-sdk/middleware-user-agent" "3.682.0" + "@aws-sdk/region-config-resolver" "3.679.0" + "@aws-sdk/types" "3.679.0" + "@aws-sdk/util-endpoints" "3.679.0" + "@aws-sdk/util-user-agent-browser" "3.679.0" + "@aws-sdk/util-user-agent-node" "3.682.0" + "@smithy/config-resolver" "^3.0.9" + "@smithy/core" "^2.4.8" + "@smithy/fetch-http-handler" "^3.2.9" + "@smithy/hash-node" "^3.0.7" + "@smithy/invalid-dependency" "^3.0.7" + "@smithy/middleware-content-length" "^3.0.9" + "@smithy/middleware-endpoint" "^3.1.4" + "@smithy/middleware-retry" "^3.0.23" + "@smithy/middleware-serde" "^3.0.7" + "@smithy/middleware-stack" "^3.0.7" + "@smithy/node-config-provider" "^3.1.8" + "@smithy/node-http-handler" "^3.2.4" + "@smithy/protocol-http" "^4.1.4" + "@smithy/smithy-client" "^3.4.0" + "@smithy/types" "^3.5.0" + "@smithy/url-parser" "^3.0.7" "@smithy/util-base64" "^3.0.0" "@smithy/util-body-length-browser" "^3.0.0" "@smithy/util-body-length-node" "^3.0.0" - "@smithy/util-defaults-mode-browser" "^3.0.14" - "@smithy/util-defaults-mode-node" "^3.0.14" - "@smithy/util-endpoints" "^2.0.5" - "@smithy/util-middleware" "^3.0.3" - "@smithy/util-retry" "^3.0.3" + "@smithy/util-defaults-mode-browser" "^3.0.23" + "@smithy/util-defaults-mode-node" "^3.0.23" + "@smithy/util-endpoints" "^2.1.3" + "@smithy/util-middleware" "^3.0.7" + "@smithy/util-retry" "^3.0.7" "@smithy/util-utf8" "^3.0.0" tslib "^2.6.2" -"@aws-sdk/client-sso@3.629.0": - version "3.629.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.629.0.tgz#19ad0236cf3985da68552dc597ed14736450630e" - integrity sha512-2w8xU4O0Grca5HmT2dXZ5fF0g39RxODtmoqHJDsK5DSt750LqDG4w3ktmBvQs3+SrpkkJOjlX5v/hb2PCxVbww== +"@aws-sdk/client-sso@3.682.0": + version "3.682.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.682.0.tgz#7533f677456d5f79cfcceed44a3481bcd86b560e" + integrity sha512-PYH9RFUMYLFl66HSBq4tIx6fHViMLkhJHTYJoJONpBs+Td+NwVJ895AdLtDsBIhMS0YseCbPpuyjUCJgsUrwUw== dependencies: "@aws-crypto/sha256-browser" "5.2.0" "@aws-crypto/sha256-js" "5.2.0" - "@aws-sdk/core" "3.629.0" - "@aws-sdk/middleware-host-header" "3.620.0" - "@aws-sdk/middleware-logger" "3.609.0" - "@aws-sdk/middleware-recursion-detection" "3.620.0" - "@aws-sdk/middleware-user-agent" "3.620.0" - "@aws-sdk/region-config-resolver" "3.614.0" - "@aws-sdk/types" "3.609.0" - "@aws-sdk/util-endpoints" "3.614.0" - "@aws-sdk/util-user-agent-browser" "3.609.0" - "@aws-sdk/util-user-agent-node" "3.614.0" - "@smithy/config-resolver" "^3.0.5" - "@smithy/core" "^2.3.2" - "@smithy/fetch-http-handler" "^3.2.4" - "@smithy/hash-node" "^3.0.3" - "@smithy/invalid-dependency" "^3.0.3" - "@smithy/middleware-content-length" "^3.0.5" - "@smithy/middleware-endpoint" "^3.1.0" - "@smithy/middleware-retry" "^3.0.14" - "@smithy/middleware-serde" "^3.0.3" - "@smithy/middleware-stack" "^3.0.3" - "@smithy/node-config-provider" "^3.1.4" - "@smithy/node-http-handler" "^3.1.4" - "@smithy/protocol-http" "^4.1.0" - "@smithy/smithy-client" "^3.1.12" - "@smithy/types" "^3.3.0" - "@smithy/url-parser" "^3.0.3" + "@aws-sdk/core" "3.679.0" + "@aws-sdk/middleware-host-header" "3.679.0" + "@aws-sdk/middleware-logger" "3.679.0" + "@aws-sdk/middleware-recursion-detection" "3.679.0" + "@aws-sdk/middleware-user-agent" "3.682.0" + "@aws-sdk/region-config-resolver" "3.679.0" + "@aws-sdk/types" "3.679.0" + "@aws-sdk/util-endpoints" "3.679.0" + "@aws-sdk/util-user-agent-browser" "3.679.0" + "@aws-sdk/util-user-agent-node" "3.682.0" + "@smithy/config-resolver" "^3.0.9" + "@smithy/core" "^2.4.8" + "@smithy/fetch-http-handler" "^3.2.9" + "@smithy/hash-node" "^3.0.7" + "@smithy/invalid-dependency" "^3.0.7" + "@smithy/middleware-content-length" "^3.0.9" + "@smithy/middleware-endpoint" "^3.1.4" + "@smithy/middleware-retry" "^3.0.23" + "@smithy/middleware-serde" "^3.0.7" + "@smithy/middleware-stack" "^3.0.7" + "@smithy/node-config-provider" "^3.1.8" + "@smithy/node-http-handler" "^3.2.4" + "@smithy/protocol-http" "^4.1.4" + "@smithy/smithy-client" "^3.4.0" + "@smithy/types" "^3.5.0" + "@smithy/url-parser" "^3.0.7" "@smithy/util-base64" "^3.0.0" "@smithy/util-body-length-browser" "^3.0.0" "@smithy/util-body-length-node" "^3.0.0" - "@smithy/util-defaults-mode-browser" "^3.0.14" - "@smithy/util-defaults-mode-node" "^3.0.14" - "@smithy/util-endpoints" "^2.0.5" - "@smithy/util-middleware" "^3.0.3" - "@smithy/util-retry" "^3.0.3" + "@smithy/util-defaults-mode-browser" "^3.0.23" + "@smithy/util-defaults-mode-node" "^3.0.23" + "@smithy/util-endpoints" "^2.1.3" + "@smithy/util-middleware" "^3.0.7" + "@smithy/util-retry" "^3.0.7" "@smithy/util-utf8" "^3.0.0" tslib "^2.6.2" -"@aws-sdk/client-sts@3.629.0": - version "3.629.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-sts/-/client-sts-3.629.0.tgz#a6ee546ebda64be90d310bb0a7316d98feabf1bd" - integrity sha512-RjOs371YwnSVGxhPjuluJKaxl4gcPYTAky0nPjwBime0i9/iS9nI8R8l5j7k7ec9tpFWjBPvNnThCU07pvjdzw== +"@aws-sdk/client-sts@3.682.0": + version "3.682.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sts/-/client-sts-3.682.0.tgz#97ff70ca141aa6ef48a22f14ef9727bd6ae17b03" + integrity sha512-xKuo4HksZ+F8m9DOfx/ZuWNhaPuqZFPwwy0xqcBT6sWH7OAuBjv/fnpOTzyQhpVTWddlf+ECtMAMrxjxuOExGQ== dependencies: "@aws-crypto/sha256-browser" "5.2.0" "@aws-crypto/sha256-js" "5.2.0" - "@aws-sdk/client-sso-oidc" "3.629.0" - "@aws-sdk/core" "3.629.0" - "@aws-sdk/credential-provider-node" "3.629.0" - "@aws-sdk/middleware-host-header" "3.620.0" - "@aws-sdk/middleware-logger" "3.609.0" - "@aws-sdk/middleware-recursion-detection" "3.620.0" - "@aws-sdk/middleware-user-agent" "3.620.0" - "@aws-sdk/region-config-resolver" "3.614.0" - "@aws-sdk/types" "3.609.0" - "@aws-sdk/util-endpoints" "3.614.0" - "@aws-sdk/util-user-agent-browser" "3.609.0" - "@aws-sdk/util-user-agent-node" "3.614.0" - "@smithy/config-resolver" "^3.0.5" - "@smithy/core" "^2.3.2" - "@smithy/fetch-http-handler" "^3.2.4" - "@smithy/hash-node" "^3.0.3" - "@smithy/invalid-dependency" "^3.0.3" - "@smithy/middleware-content-length" "^3.0.5" - "@smithy/middleware-endpoint" "^3.1.0" - "@smithy/middleware-retry" "^3.0.14" - "@smithy/middleware-serde" "^3.0.3" - "@smithy/middleware-stack" "^3.0.3" - "@smithy/node-config-provider" "^3.1.4" - "@smithy/node-http-handler" "^3.1.4" - "@smithy/protocol-http" "^4.1.0" - "@smithy/smithy-client" "^3.1.12" - "@smithy/types" "^3.3.0" - "@smithy/url-parser" "^3.0.3" + "@aws-sdk/client-sso-oidc" "3.682.0" + "@aws-sdk/core" "3.679.0" + "@aws-sdk/credential-provider-node" "3.682.0" + "@aws-sdk/middleware-host-header" "3.679.0" + "@aws-sdk/middleware-logger" "3.679.0" + "@aws-sdk/middleware-recursion-detection" "3.679.0" + "@aws-sdk/middleware-user-agent" "3.682.0" + "@aws-sdk/region-config-resolver" "3.679.0" + "@aws-sdk/types" "3.679.0" + "@aws-sdk/util-endpoints" "3.679.0" + "@aws-sdk/util-user-agent-browser" "3.679.0" + "@aws-sdk/util-user-agent-node" "3.682.0" + "@smithy/config-resolver" "^3.0.9" + "@smithy/core" "^2.4.8" + "@smithy/fetch-http-handler" "^3.2.9" + "@smithy/hash-node" "^3.0.7" + "@smithy/invalid-dependency" "^3.0.7" + "@smithy/middleware-content-length" "^3.0.9" + "@smithy/middleware-endpoint" "^3.1.4" + "@smithy/middleware-retry" "^3.0.23" + "@smithy/middleware-serde" "^3.0.7" + "@smithy/middleware-stack" "^3.0.7" + "@smithy/node-config-provider" "^3.1.8" + "@smithy/node-http-handler" "^3.2.4" + "@smithy/protocol-http" "^4.1.4" + "@smithy/smithy-client" "^3.4.0" + "@smithy/types" "^3.5.0" + "@smithy/url-parser" "^3.0.7" "@smithy/util-base64" "^3.0.0" "@smithy/util-body-length-browser" "^3.0.0" "@smithy/util-body-length-node" "^3.0.0" - "@smithy/util-defaults-mode-browser" "^3.0.14" - "@smithy/util-defaults-mode-node" "^3.0.14" - "@smithy/util-endpoints" "^2.0.5" - "@smithy/util-middleware" "^3.0.3" - "@smithy/util-retry" "^3.0.3" + "@smithy/util-defaults-mode-browser" "^3.0.23" + "@smithy/util-defaults-mode-node" "^3.0.23" + "@smithy/util-endpoints" "^2.1.3" + "@smithy/util-middleware" "^3.0.7" + "@smithy/util-retry" "^3.0.7" "@smithy/util-utf8" "^3.0.0" tslib "^2.6.2" -"@aws-sdk/core@3.629.0": - version "3.629.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.629.0.tgz#1ed02c657edcd22ffdce9b3b5bdbd2a36fe899aa" - integrity sha512-+/ShPU/tyIBM3oY1cnjgNA/tFyHtlWq+wXF9xEKRv19NOpYbWQ+xzNwVjGq8vR07cCRqy/sDQLWPhxjtuV/FiQ== - dependencies: - "@smithy/core" "^2.3.2" - "@smithy/node-config-provider" "^3.1.4" - "@smithy/property-provider" "^3.1.3" - "@smithy/protocol-http" "^4.1.0" - "@smithy/signature-v4" "^4.1.0" - "@smithy/smithy-client" "^3.1.12" - "@smithy/types" "^3.3.0" - "@smithy/util-middleware" "^3.0.3" +"@aws-sdk/core@3.679.0": + version "3.679.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.679.0.tgz#102aa1d19db5bdcabefc2dcd044f2fb5d0771568" + integrity sha512-CS6PWGX8l4v/xyvX8RtXnBisdCa5+URzKd0L6GvHChype9qKUVxO/Gg6N/y43Hvg7MNWJt9FBPNWIxUB+byJwg== + dependencies: + "@aws-sdk/types" "3.679.0" + "@smithy/core" "^2.4.8" + "@smithy/node-config-provider" "^3.1.8" + "@smithy/property-provider" "^3.1.7" + "@smithy/protocol-http" "^4.1.4" + "@smithy/signature-v4" "^4.2.0" + "@smithy/smithy-client" "^3.4.0" + "@smithy/types" "^3.5.0" + "@smithy/util-middleware" "^3.0.7" fast-xml-parser "4.4.1" tslib "^2.6.2" -"@aws-sdk/credential-provider-env@3.620.1": - version "3.620.1" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.620.1.tgz#d4692c49a65ebc11dae3f7f8b053fee9268a953c" - integrity sha512-ExuILJ2qLW5ZO+rgkNRj0xiAipKT16Rk77buvPP8csR7kkCflT/gXTyzRe/uzIiETTxM7tr8xuO9MP/DQXqkfg== +"@aws-sdk/credential-provider-env@3.679.0": + version "3.679.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.679.0.tgz#abf297714b77197a9da0d3d95a0f5687ae28e5b3" + integrity sha512-EdlTYbzMm3G7VUNAMxr9S1nC1qUNqhKlAxFU8E7cKsAe8Bp29CD5HAs3POc56AVo9GC4yRIS+/mtlZSmrckzUA== dependencies: - "@aws-sdk/types" "3.609.0" - "@smithy/property-provider" "^3.1.3" - "@smithy/types" "^3.3.0" + "@aws-sdk/core" "3.679.0" + "@aws-sdk/types" "3.679.0" + "@smithy/property-provider" "^3.1.7" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@aws-sdk/credential-provider-http@3.622.0": - version "3.622.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.622.0.tgz#db481fdef859849d07dd5870894f45df2debab3d" - integrity sha512-VUHbr24Oll1RK3WR8XLUugLpgK9ZuxEm/NVeVqyFts1Ck9gsKpRg1x4eH7L7tW3SJ4TDEQNMbD7/7J+eoL2svg== - dependencies: - "@aws-sdk/types" "3.609.0" - "@smithy/fetch-http-handler" "^3.2.4" - "@smithy/node-http-handler" "^3.1.4" - "@smithy/property-provider" "^3.1.3" - "@smithy/protocol-http" "^4.1.0" - "@smithy/smithy-client" "^3.1.12" - "@smithy/types" "^3.3.0" - "@smithy/util-stream" "^3.1.3" +"@aws-sdk/credential-provider-http@3.679.0": + version "3.679.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.679.0.tgz#9fc29f4ec7ab52ecf394288c05295823e818d812" + integrity sha512-ZoKLubW5DqqV1/2a3TSn+9sSKg0T8SsYMt1JeirnuLJF0mCoYFUaWMyvxxKuxPoqvUsaycxKru4GkpJ10ltNBw== + dependencies: + "@aws-sdk/core" "3.679.0" + "@aws-sdk/types" "3.679.0" + "@smithy/fetch-http-handler" "^3.2.9" + "@smithy/node-http-handler" "^3.2.4" + "@smithy/property-provider" "^3.1.7" + "@smithy/protocol-http" "^4.1.4" + "@smithy/smithy-client" "^3.4.0" + "@smithy/types" "^3.5.0" + "@smithy/util-stream" "^3.1.9" tslib "^2.6.2" -"@aws-sdk/credential-provider-ini@3.629.0": - version "3.629.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.629.0.tgz#88a88ec752d8db388300143a37e70d96d6ea2cef" - integrity sha512-r9fI7BABARvVDp77DBUImQzYdvarAIdhbvpCEZib0rlpvfWu3zxE9KZcapCAAi0MPjxeDfb7RMehFQIkAP7mYw== - dependencies: - "@aws-sdk/credential-provider-env" "3.620.1" - "@aws-sdk/credential-provider-http" "3.622.0" - "@aws-sdk/credential-provider-process" "3.620.1" - "@aws-sdk/credential-provider-sso" "3.629.0" - "@aws-sdk/credential-provider-web-identity" "3.621.0" - "@aws-sdk/types" "3.609.0" - "@smithy/credential-provider-imds" "^3.2.0" - "@smithy/property-provider" "^3.1.3" - "@smithy/shared-ini-file-loader" "^3.1.4" - "@smithy/types" "^3.3.0" +"@aws-sdk/credential-provider-ini@3.682.0": + version "3.682.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.682.0.tgz#36a68cd8d0ec3b14acf413166dce72a201fcc2bd" + integrity sha512-6eqWeHdK6EegAxqDdiCi215nT3QZPwukgWAYuVxNfJ/5m0/P7fAzF+D5kKVgByUvGJEbq/FEL8Fw7OBe64AA+g== + dependencies: + "@aws-sdk/core" "3.679.0" + "@aws-sdk/credential-provider-env" "3.679.0" + "@aws-sdk/credential-provider-http" "3.679.0" + "@aws-sdk/credential-provider-process" "3.679.0" + "@aws-sdk/credential-provider-sso" "3.682.0" + "@aws-sdk/credential-provider-web-identity" "3.679.0" + "@aws-sdk/types" "3.679.0" + "@smithy/credential-provider-imds" "^3.2.4" + "@smithy/property-provider" "^3.1.7" + "@smithy/shared-ini-file-loader" "^3.1.8" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@aws-sdk/credential-provider-node@3.629.0": - version "3.629.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.629.0.tgz#4004ada7d3edbf0d28c710a5a5d42027dc34bfb2" - integrity sha512-868hnVOLlXOBHk91Rl0jZIRgr/M4WJCa0nOrW9A9yidsQxuZp9P0vshDmm4hMvNZadmPIfo0Rra2MpA4RELoCw== - dependencies: - "@aws-sdk/credential-provider-env" "3.620.1" - "@aws-sdk/credential-provider-http" "3.622.0" - "@aws-sdk/credential-provider-ini" "3.629.0" - "@aws-sdk/credential-provider-process" "3.620.1" - "@aws-sdk/credential-provider-sso" "3.629.0" - "@aws-sdk/credential-provider-web-identity" "3.621.0" - "@aws-sdk/types" "3.609.0" - "@smithy/credential-provider-imds" "^3.2.0" - "@smithy/property-provider" "^3.1.3" - "@smithy/shared-ini-file-loader" "^3.1.4" - "@smithy/types" "^3.3.0" +"@aws-sdk/credential-provider-node@3.682.0": + version "3.682.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.682.0.tgz#4ec1ebd00dcacb46ae76747b23ebf7bda04808bd" + integrity sha512-HSmDqZcBVZrTctHCT9m++vdlDfJ1ARI218qmZa+TZzzOFNpKWy6QyHMEra45GB9GnkkMmV6unoDSPMuN0AqcMg== + dependencies: + "@aws-sdk/credential-provider-env" "3.679.0" + "@aws-sdk/credential-provider-http" "3.679.0" + "@aws-sdk/credential-provider-ini" "3.682.0" + "@aws-sdk/credential-provider-process" "3.679.0" + "@aws-sdk/credential-provider-sso" "3.682.0" + "@aws-sdk/credential-provider-web-identity" "3.679.0" + "@aws-sdk/types" "3.679.0" + "@smithy/credential-provider-imds" "^3.2.4" + "@smithy/property-provider" "^3.1.7" + "@smithy/shared-ini-file-loader" "^3.1.8" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@aws-sdk/credential-provider-process@3.620.1": - version "3.620.1" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.620.1.tgz#10387cf85400420bb4bbda9cc56937dcc6d6d0ee" - integrity sha512-hWqFMidqLAkaV9G460+1at6qa9vySbjQKKc04p59OT7lZ5cO5VH5S4aI05e+m4j364MBROjjk2ugNvfNf/8ILg== +"@aws-sdk/credential-provider-process@3.679.0": + version "3.679.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.679.0.tgz#a06b5193cdad2c14382708bcd44d487af52b11dc" + integrity sha512-u/p4TV8kQ0zJWDdZD4+vdQFTMhkDEJFws040Gm113VHa/Xo1SYOjbpvqeuFoz6VmM0bLvoOWjxB9MxnSQbwKpQ== dependencies: - "@aws-sdk/types" "3.609.0" - "@smithy/property-provider" "^3.1.3" - "@smithy/shared-ini-file-loader" "^3.1.4" - "@smithy/types" "^3.3.0" + "@aws-sdk/core" "3.679.0" + "@aws-sdk/types" "3.679.0" + "@smithy/property-provider" "^3.1.7" + "@smithy/shared-ini-file-loader" "^3.1.8" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@aws-sdk/credential-provider-sso@3.629.0": - version "3.629.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.629.0.tgz#f6c550d74007d1262149ae736df5868d4ea5aad7" - integrity sha512-Lf4XOuj6jamxgGZGrVojERh5S+NS2t2S4CUOnAu6tJ5U0GPlpjhINUKlcVxJBpsIXudMGW1nkumAd3+kazCPig== - dependencies: - "@aws-sdk/client-sso" "3.629.0" - "@aws-sdk/token-providers" "3.614.0" - "@aws-sdk/types" "3.609.0" - "@smithy/property-provider" "^3.1.3" - "@smithy/shared-ini-file-loader" "^3.1.4" - "@smithy/types" "^3.3.0" +"@aws-sdk/credential-provider-sso@3.682.0": + version "3.682.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.682.0.tgz#aa7e3ffdac82bfc14fc0cf136cec3152f863a63a" + integrity sha512-h7IH1VsWgV6YAJSWWV6y8uaRjGqLY3iBpGZlXuTH/c236NMLaNv+WqCBLeBxkFGUb2WeQ+FUPEJDCD69rgLIkg== + dependencies: + "@aws-sdk/client-sso" "3.682.0" + "@aws-sdk/core" "3.679.0" + "@aws-sdk/token-providers" "3.679.0" + "@aws-sdk/types" "3.679.0" + "@smithy/property-provider" "^3.1.7" + "@smithy/shared-ini-file-loader" "^3.1.8" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@aws-sdk/credential-provider-web-identity@3.621.0": - version "3.621.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.621.0.tgz#b25878c0a05dad60cd5f91e7e5a31a145c2f14be" - integrity sha512-w7ASSyfNvcx7+bYGep3VBgC3K6vEdLmlpjT7nSIHxxQf+WSdvy+HynwJosrpZax0sK5q0D1Jpn/5q+r5lwwW6w== +"@aws-sdk/credential-provider-web-identity@3.679.0": + version "3.679.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.679.0.tgz#5871c44e5846e7c93810fd033224c00493db65a3" + integrity sha512-a74tLccVznXCaBefWPSysUcLXYJiSkeUmQGtalNgJ1vGkE36W5l/8czFiiowdWdKWz7+x6xf0w+Kjkjlj42Ung== dependencies: - "@aws-sdk/types" "3.609.0" - "@smithy/property-provider" "^3.1.3" - "@smithy/types" "^3.3.0" + "@aws-sdk/core" "3.679.0" + "@aws-sdk/types" "3.679.0" + "@smithy/property-provider" "^3.1.7" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@aws-sdk/middleware-bucket-endpoint@3.620.0": - version "3.620.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.620.0.tgz#c5dc0e98b6209a91479cad6c2c74fbc5a3429fab" - integrity sha512-eGLL0W6L3HDb3OACyetZYOWpHJ+gLo0TehQKeQyy2G8vTYXqNTeqYhuI6up9HVjBzU9eQiULVQETmgQs7TFaRg== +"@aws-sdk/middleware-bucket-endpoint@3.679.0": + version "3.679.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.679.0.tgz#cc5acad018d3b1646340fa2d0d0d412436b95e04" + integrity sha512-5EpiPhhGgnF+uJR4DzWUk6Lx3pOn9oM6JGXxeHsiynfoBfq7vHMleq+uABHHSQS+y7XzbyZ7x8tXNQlliMwOsg== dependencies: - "@aws-sdk/types" "3.609.0" - "@aws-sdk/util-arn-parser" "3.568.0" - "@smithy/node-config-provider" "^3.1.4" - "@smithy/protocol-http" "^4.1.0" - "@smithy/types" "^3.3.0" + "@aws-sdk/types" "3.679.0" + "@aws-sdk/util-arn-parser" "3.679.0" + "@smithy/node-config-provider" "^3.1.8" + "@smithy/protocol-http" "^4.1.4" + "@smithy/types" "^3.5.0" "@smithy/util-config-provider" "^3.0.0" tslib "^2.6.2" -"@aws-sdk/middleware-expect-continue@3.620.0": - version "3.620.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.620.0.tgz#6a362c0f0696dc6749108a33de9998e0fa6b50ec" - integrity sha512-QXeRFMLfyQ31nAHLbiTLtk0oHzG9QLMaof5jIfqcUwnOkO8YnQdeqzakrg1Alpy/VQ7aqzIi8qypkBe2KXZz0A== +"@aws-sdk/middleware-expect-continue@3.679.0": + version "3.679.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.679.0.tgz#6b22403fa6d7a7b9b0312c4453cfef69da66334b" + integrity sha512-nYsh9PdWrF4EahTRdXHGlNud82RPc508CNGdh1lAGfPU3tNveGfMBX3PcGBtPOse3p9ebNKRWVmUc9eXSjGvHA== dependencies: - "@aws-sdk/types" "3.609.0" - "@smithy/protocol-http" "^4.1.0" - "@smithy/types" "^3.3.0" + "@aws-sdk/types" "3.679.0" + "@smithy/protocol-http" "^4.1.4" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@aws-sdk/middleware-flexible-checksums@3.620.0": - version "3.620.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.620.0.tgz#42cd48cdc0ad9639545be000bf537969210ce8c5" - integrity sha512-ftz+NW7qka2sVuwnnO1IzBku5ccP+s5qZGeRTPgrKB7OzRW85gthvIo1vQR2w+OwHFk7WJbbhhWwbCbktnP4UA== +"@aws-sdk/middleware-flexible-checksums@3.682.0": + version "3.682.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.682.0.tgz#1370919775140dfda2a860892792bf560914c93a" + integrity sha512-5u1STth6iZUtAvPDO0NJVYKUX2EYKU7v84MYYaZ3O27HphRjFqDos0keL2KTnHn/KmMD68rM3yiUareWR8hnAQ== dependencies: "@aws-crypto/crc32" "5.2.0" "@aws-crypto/crc32c" "5.2.0" - "@aws-sdk/types" "3.609.0" + "@aws-sdk/core" "3.679.0" + "@aws-sdk/types" "3.679.0" "@smithy/is-array-buffer" "^3.0.0" - "@smithy/protocol-http" "^4.1.0" - "@smithy/types" "^3.3.0" + "@smithy/node-config-provider" "^3.1.8" + "@smithy/protocol-http" "^4.1.4" + "@smithy/types" "^3.5.0" + "@smithy/util-middleware" "^3.0.7" "@smithy/util-utf8" "^3.0.0" tslib "^2.6.2" -"@aws-sdk/middleware-host-header@3.620.0": - version "3.620.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.620.0.tgz#b561d419a08a984ba364c193376b482ff5224d74" - integrity sha512-VMtPEZwqYrII/oUkffYsNWY9PZ9xpNJpMgmyU0rlDQ25O1c0Hk3fJmZRe6pEkAJ0omD7kLrqGl1DUjQVxpd/Rg== +"@aws-sdk/middleware-host-header@3.679.0": + version "3.679.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.679.0.tgz#1eabe42250c57a9e28742dd04786781573faad1a" + integrity sha512-y176HuQ8JRY3hGX8rQzHDSbCl9P5Ny9l16z4xmaiLo+Qfte7ee4Yr3yaAKd7GFoJ3/Mhud2XZ37fR015MfYl2w== dependencies: - "@aws-sdk/types" "3.609.0" - "@smithy/protocol-http" "^4.1.0" - "@smithy/types" "^3.3.0" + "@aws-sdk/types" "3.679.0" + "@smithy/protocol-http" "^4.1.4" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@aws-sdk/middleware-location-constraint@3.609.0": - version "3.609.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.609.0.tgz#7ed82d71e5ddcd50683ef2bbde10d1cc2492057e" - integrity sha512-xzsdoTkszGVqGVPjUmgoP7TORiByLueMHieI1fhQL888WPdqctwAx3ES6d/bA9Q/i8jnc6hs+Fjhy8UvBTkE9A== +"@aws-sdk/middleware-location-constraint@3.679.0": + version "3.679.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.679.0.tgz#99ed75f1bf5ec005656af1c9efdb35aa2ddc7216" + integrity sha512-SA1C1D3XgoKTGxyNsOqd016ONpk46xJLWDgJUd00Zb21Ox5wYCoY6aDRKiaMRW+1VfCJdezs1Do3XLyIU9KxyA== dependencies: - "@aws-sdk/types" "3.609.0" - "@smithy/types" "^3.3.0" + "@aws-sdk/types" "3.679.0" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@aws-sdk/middleware-logger@3.609.0": - version "3.609.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.609.0.tgz#ed44d201f091b8bac908cbf14724c7a4d492553f" - integrity sha512-S62U2dy4jMDhDFDK5gZ4VxFdWzCtLzwbYyFZx2uvPYTECkepLUfzLic2BHg2Qvtu4QjX+oGE3P/7fwaGIsGNuQ== +"@aws-sdk/middleware-logger@3.679.0": + version "3.679.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.679.0.tgz#cb0f205ddb5341d8327fc9ca1897bf06526c1896" + integrity sha512-0vet8InEj7nvIvGKk+ch7bEF5SyZ7Us9U7YTEgXPrBNStKeRUsgwRm0ijPWWd0a3oz2okaEwXsFl7G/vI0XiEA== dependencies: - "@aws-sdk/types" "3.609.0" - "@smithy/types" "^3.3.0" + "@aws-sdk/types" "3.679.0" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@aws-sdk/middleware-recursion-detection@3.620.0": - version "3.620.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.620.0.tgz#f8270dfff843fd756be971e5673f89c6a24c6513" - integrity sha512-nh91S7aGK3e/o1ck64sA/CyoFw+gAYj2BDOnoNa6ouyCrVJED96ZXWbhye/fz9SgmNUZR2g7GdVpiLpMKZoI5w== +"@aws-sdk/middleware-recursion-detection@3.679.0": + version "3.679.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.679.0.tgz#3542de5baa466abffbfe5ee485fd87f60d5f917e" + integrity sha512-sQoAZFsQiW/LL3DfKMYwBoGjYDEnMbA9WslWN8xneCmBAwKo6IcSksvYs23PP8XMIoBGe2I2J9BSr654XWygTQ== dependencies: - "@aws-sdk/types" "3.609.0" - "@smithy/protocol-http" "^4.1.0" - "@smithy/types" "^3.3.0" + "@aws-sdk/types" "3.679.0" + "@smithy/protocol-http" "^4.1.4" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@aws-sdk/middleware-sdk-s3@3.629.0": - version "3.629.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.629.0.tgz#10ad7b8af945f915d31f00cec0198248be95291c" - integrity sha512-FRXLcnPWXBoq/T9mnGnrpqhrSKNSm22rqJ0L7P14KESmbGuwhF/7ELYYxXIpgnIpb/CIUVmIU5EE8lsW1VTe8A== - dependencies: - "@aws-sdk/core" "3.629.0" - "@aws-sdk/types" "3.609.0" - "@aws-sdk/util-arn-parser" "3.568.0" - "@smithy/core" "^2.3.2" - "@smithy/node-config-provider" "^3.1.4" - "@smithy/protocol-http" "^4.1.0" - "@smithy/signature-v4" "^4.1.0" - "@smithy/smithy-client" "^3.1.12" - "@smithy/types" "^3.3.0" +"@aws-sdk/middleware-sdk-s3@3.685.0": + version "3.685.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.685.0.tgz#9e198973cc8d7ead142e5b5ba38694a957cf462b" + integrity sha512-C4w92b3A99NbghrA2Ssw6y1RbDF3I3Bgzi2Izh0pXgyIoDiX0xs9bUs/FGYLK4uepYr78DAZY8DwEpzjWIXkSA== + dependencies: + "@aws-sdk/core" "3.679.0" + "@aws-sdk/types" "3.679.0" + "@aws-sdk/util-arn-parser" "3.679.0" + "@smithy/core" "^2.4.8" + "@smithy/node-config-provider" "^3.1.8" + "@smithy/protocol-http" "^4.1.4" + "@smithy/signature-v4" "^4.2.0" + "@smithy/smithy-client" "^3.4.0" + "@smithy/types" "^3.5.0" "@smithy/util-config-provider" "^3.0.0" - "@smithy/util-middleware" "^3.0.3" - "@smithy/util-stream" "^3.1.3" + "@smithy/util-middleware" "^3.0.7" + "@smithy/util-stream" "^3.1.9" "@smithy/util-utf8" "^3.0.0" tslib "^2.6.2" -"@aws-sdk/middleware-ssec@3.609.0": - version "3.609.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-ssec/-/middleware-ssec-3.609.0.tgz#b87a8bc6133f3f6bdc6801183d0f9dad3f93cf9f" - integrity sha512-GZSD1s7+JswWOTamVap79QiDaIV7byJFssBW68GYjyRS5EBjNfwA/8s+6uE6g39R3ojyTbYOmvcANoZEhSULXg== +"@aws-sdk/middleware-ssec@3.679.0": + version "3.679.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-ssec/-/middleware-ssec-3.679.0.tgz#72c68c46073d1e93654b9b47be61cbcf852d7804" + integrity sha512-4GNUxXbs1M71uFHRiCAZtN0/g23ogI9YjMe5isAuYMHXwDB3MhqF7usKf954mBP6tplvN44vYlbJ84faaLrTtg== dependencies: - "@aws-sdk/types" "3.609.0" - "@smithy/types" "^3.3.0" + "@aws-sdk/types" "3.679.0" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@aws-sdk/middleware-user-agent@3.620.0": - version "3.620.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.620.0.tgz#1fe3104f04f576a942cf0469bfbd73c38eef3d9e" - integrity sha512-bvS6etn+KsuL32ubY5D3xNof1qkenpbJXf/ugGXbg0n98DvDFQ/F+SMLxHgbnER5dsKYchNnhmtI6/FC3HFu/A== - dependencies: - "@aws-sdk/types" "3.609.0" - "@aws-sdk/util-endpoints" "3.614.0" - "@smithy/protocol-http" "^4.1.0" - "@smithy/types" "^3.3.0" +"@aws-sdk/middleware-user-agent@3.682.0": + version "3.682.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.682.0.tgz#07d75723bce31e65a29ad0934347537e50e3536e" + integrity sha512-7TyvYR9HdGH1/Nq0eeApUTM4izB6rExiw87khVYuJwZHr6FmvIL1FsOVFro/4WlXa0lg4LiYOm/8H8dHv+fXTg== + dependencies: + "@aws-sdk/core" "3.679.0" + "@aws-sdk/types" "3.679.0" + "@aws-sdk/util-endpoints" "3.679.0" + "@smithy/core" "^2.4.8" + "@smithy/protocol-http" "^4.1.4" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@aws-sdk/region-config-resolver@3.614.0": - version "3.614.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/region-config-resolver/-/region-config-resolver-3.614.0.tgz#9cebb31a5bcfea2a41891fff7f28d0164cde179a" - integrity sha512-vDCeMXvic/LU0KFIUjpC3RiSTIkkvESsEfbVHiHH0YINfl8HnEqR5rj+L8+phsCeVg2+LmYwYxd5NRz4PHxt5g== +"@aws-sdk/region-config-resolver@3.679.0": + version "3.679.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/region-config-resolver/-/region-config-resolver-3.679.0.tgz#d205dbaea8385aaf05e637fb7cb095c60bc708be" + integrity sha512-Ybx54P8Tg6KKq5ck7uwdjiKif7n/8g1x+V0V9uTjBjRWqaIgiqzXwKWoPj6NCNkE7tJNtqI4JrNxp/3S3HvmRw== dependencies: - "@aws-sdk/types" "3.609.0" - "@smithy/node-config-provider" "^3.1.4" - "@smithy/types" "^3.3.0" + "@aws-sdk/types" "3.679.0" + "@smithy/node-config-provider" "^3.1.8" + "@smithy/types" "^3.5.0" "@smithy/util-config-provider" "^3.0.0" - "@smithy/util-middleware" "^3.0.3" + "@smithy/util-middleware" "^3.0.7" tslib "^2.6.2" -"@aws-sdk/s3-request-presigner@^3.629.0": - version "3.629.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.629.0.tgz#a8ab2aa3f59287baa7e7ef404fcdfe9bf201085a" - integrity sha512-6lVgK9Y5m+AqisPNLs1Low5oJHFg/lfsuEsQMKG5y0/uqR1KVLswiaY1mhp0cprMEXRN2DDMAhP7i+jy5/WLNw== - dependencies: - "@aws-sdk/signature-v4-multi-region" "3.629.0" - "@aws-sdk/types" "3.609.0" - "@aws-sdk/util-format-url" "3.609.0" - "@smithy/middleware-endpoint" "^3.1.0" - "@smithy/protocol-http" "^4.1.0" - "@smithy/smithy-client" "^3.1.12" - "@smithy/types" "^3.3.0" +"@aws-sdk/s3-request-presigner@^3.685.0": + version "3.685.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.685.0.tgz#d2cd353c45ac745b8ec744640521c061e1b957c7" + integrity sha512-OTFQRXlAff/tRE2GfhqAgVaWkRSHUzj9ebvdd3979zcIELHY3kWBJ/XbefElXg1lhsumKdZl/gLpeA76GyQDPQ== + dependencies: + "@aws-sdk/signature-v4-multi-region" "3.685.0" + "@aws-sdk/types" "3.679.0" + "@aws-sdk/util-format-url" "3.679.0" + "@smithy/middleware-endpoint" "^3.1.4" + "@smithy/protocol-http" "^4.1.4" + "@smithy/smithy-client" "^3.4.0" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@aws-sdk/signature-v4-multi-region@3.629.0": - version "3.629.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.629.0.tgz#ca75443f3324fd398d228c3cba0f4275e7bb4a3a" - integrity sha512-GPX6dnmuLGDFp7CsGqGCzleEoNyr9ekgOzSBtcL5nKX++NruxO7f1QzJAbcYvz0gdKvz958UO0EKsGM6hnkTSg== +"@aws-sdk/signature-v4-multi-region@3.685.0": + version "3.685.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.685.0.tgz#8bf6ae3d535666dd30ac255c9ba3bbde991b13df" + integrity sha512-IHLwuAZGqfUWVrNqw0ugnBa7iL8uBP4x6A7bfBDXRXWCWjUCed/1/D//0lKDHwpFkV74fGW6KoBacnWSUlXmwA== dependencies: - "@aws-sdk/middleware-sdk-s3" "3.629.0" - "@aws-sdk/types" "3.609.0" - "@smithy/protocol-http" "^4.1.0" - "@smithy/signature-v4" "^4.1.0" - "@smithy/types" "^3.3.0" + "@aws-sdk/middleware-sdk-s3" "3.685.0" + "@aws-sdk/types" "3.679.0" + "@smithy/protocol-http" "^4.1.4" + "@smithy/signature-v4" "^4.2.0" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@aws-sdk/token-providers@3.614.0": - version "3.614.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.614.0.tgz#88da04f6d4ce916b0b0f6e045676d04201fb47fd" - integrity sha512-okItqyY6L9IHdxqs+Z116y5/nda7rHxLvROxtAJdLavWTYDydxrZstImNgGWTeVdmc0xX2gJCI77UYUTQWnhRw== +"@aws-sdk/token-providers@3.679.0": + version "3.679.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.679.0.tgz#7ec462d93941dd3cfdc245104ad32971f6ebc4f6" + integrity sha512-1/+Zso/x2jqgutKixYFQEGli0FELTgah6bm7aB+m2FAWH4Hz7+iMUsazg6nSWm714sG9G3h5u42Dmpvi9X6/hA== dependencies: - "@aws-sdk/types" "3.609.0" - "@smithy/property-provider" "^3.1.3" - "@smithy/shared-ini-file-loader" "^3.1.4" - "@smithy/types" "^3.3.0" + "@aws-sdk/types" "3.679.0" + "@smithy/property-provider" "^3.1.7" + "@smithy/shared-ini-file-loader" "^3.1.8" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@aws-sdk/types@3.609.0": - version "3.609.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.609.0.tgz#06b39d799c9f197a7b43670243e8e78a3bf7d6a5" - integrity sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q== +"@aws-sdk/types@3.679.0": + version "3.679.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.679.0.tgz#3737bb0f190add9e788b838a24cd5d8106dbed4f" + integrity sha512-NwVq8YvInxQdJ47+zz4fH3BRRLC6lL+WLkvr242PVBbUOLRyK/lkwHlfiKUoeVIMyK5NF+up6TRg71t/8Bny6Q== dependencies: - "@smithy/types" "^3.3.0" + "@smithy/types" "^3.5.0" tslib "^2.6.2" "@aws-sdk/types@^3.222.0": @@ -676,31 +711,31 @@ dependencies: tslib "^2.3.1" -"@aws-sdk/util-arn-parser@3.568.0": - version "3.568.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-arn-parser/-/util-arn-parser-3.568.0.tgz#6a19a8c6bbaa520b6be1c278b2b8c17875b91527" - integrity sha512-XUKJWWo+KOB7fbnPP0+g/o5Ulku/X53t7i/h+sPHr5xxYTJJ9CYnbToo95mzxe7xWvkLrsNtJ8L+MnNn9INs2w== +"@aws-sdk/util-arn-parser@3.679.0": + version "3.679.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-arn-parser/-/util-arn-parser-3.679.0.tgz#1b7793c8ae31305ca6c6f7497066f3e74ad69716" + integrity sha512-CwzEbU8R8rq9bqUFryO50RFBlkfufV9UfMArHPWlo+lmsC+NlSluHQALoj6Jkq3zf5ppn1CN0c1DDLrEqdQUXg== dependencies: tslib "^2.6.2" -"@aws-sdk/util-endpoints@3.614.0": - version "3.614.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-endpoints/-/util-endpoints-3.614.0.tgz#6564b0ffd7dc3728221e9f9821f5aab1cc58468e" - integrity sha512-wK2cdrXHH4oz4IomV/yrGkftU9A+ITB6nFL+rxxyO78is2ifHJpFdV4aqk4LSkXYPi6CXWNru/Dqc7yiKXgJPw== +"@aws-sdk/util-endpoints@3.679.0": + version "3.679.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-endpoints/-/util-endpoints-3.679.0.tgz#b249ad8b4289e634cb5dfb3873a70b7aecbf323f" + integrity sha512-YL6s4Y/1zC45OvddvgE139fjeWSKKPgLlnfrvhVL7alNyY9n7beR4uhoDpNrt5mI6sn9qiBF17790o+xLAXjjg== dependencies: - "@aws-sdk/types" "3.609.0" - "@smithy/types" "^3.3.0" - "@smithy/util-endpoints" "^2.0.5" + "@aws-sdk/types" "3.679.0" + "@smithy/types" "^3.5.0" + "@smithy/util-endpoints" "^2.1.3" tslib "^2.6.2" -"@aws-sdk/util-format-url@3.609.0": - version "3.609.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-format-url/-/util-format-url-3.609.0.tgz#f53907193bb636b52b61c81bbe6d7bd5ddc76c68" - integrity sha512-fuk29BI/oLQlJ7pfm6iJ4gkEpHdavffAALZwXh9eaY1vQ0ip0aKfRTiNudPoJjyyahnz5yJ1HkmlcDitlzsOrQ== +"@aws-sdk/util-format-url@3.679.0": + version "3.679.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-format-url/-/util-format-url-3.679.0.tgz#5defda8e1601d5d4c4afe694348ad6d7e2420a9b" + integrity sha512-pqV1b/hJ/kumtF8AwObJ7bsGgs/2zuAdZtalSD8Pu4jdjOji3IBwP79giAHyhVwoXaMjkpG3mG4ldn9CVtzZJA== dependencies: - "@aws-sdk/types" "3.609.0" - "@smithy/querystring-builder" "^3.0.3" - "@smithy/types" "^3.3.0" + "@aws-sdk/types" "3.679.0" + "@smithy/querystring-builder" "^3.0.7" + "@smithy/types" "^3.5.0" tslib "^2.6.2" "@aws-sdk/util-locate-window@^3.0.0": @@ -710,32 +745,33 @@ dependencies: tslib "^2.3.1" -"@aws-sdk/util-user-agent-browser@3.609.0": - version "3.609.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.609.0.tgz#aa15421b2e32ae8bc589dac2bd6e8969832ce588" - integrity sha512-fojPU+mNahzQ0YHYBsx0ZIhmMA96H+ZIZ665ObU9tl+SGdbLneVZVikGve+NmHTQwHzwkFsZYYnVKAkreJLAtA== +"@aws-sdk/util-user-agent-browser@3.679.0": + version "3.679.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.679.0.tgz#bbaa5a8771c8a16388cd3cd934bb84a641ce907d" + integrity sha512-CusSm2bTBG1kFypcsqU8COhnYc6zltobsqs3nRrvYqYaOqtMnuE46K4XTWpnzKgwDejgZGOE+WYyprtAxrPvmQ== dependencies: - "@aws-sdk/types" "3.609.0" - "@smithy/types" "^3.3.0" + "@aws-sdk/types" "3.679.0" + "@smithy/types" "^3.5.0" bowser "^2.11.0" tslib "^2.6.2" -"@aws-sdk/util-user-agent-node@3.614.0": - version "3.614.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.614.0.tgz#1e3f49a80f841a3f21647baed2adce01aac5beb5" - integrity sha512-15ElZT88peoHnq5TEoEtZwoXTXRxNrk60TZNdpl/TUBJ5oNJ9Dqb5Z4ryb8ofN6nm9aFf59GVAerFDz8iUoHBA== +"@aws-sdk/util-user-agent-node@3.682.0": + version "3.682.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.682.0.tgz#a493d2afb160c5cd4ab0520f929e9b7a2b36f74e" + integrity sha512-so5s+j0gPoTS0HM4HPL+G0ajk0T6cQAg8JXzRgvyiQAxqie+zGCZAV3VuVeMNWMVbzsgZl0pYZaatPFTLG/AxA== dependencies: - "@aws-sdk/types" "3.609.0" - "@smithy/node-config-provider" "^3.1.4" - "@smithy/types" "^3.3.0" + "@aws-sdk/middleware-user-agent" "3.682.0" + "@aws-sdk/types" "3.679.0" + "@smithy/node-config-provider" "^3.1.8" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@aws-sdk/xml-builder@3.609.0": - version "3.609.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/xml-builder/-/xml-builder-3.609.0.tgz#eeb3d5cde000a23cfeeefe0354b6193440dc7d87" - integrity sha512-l9XxNcA4HX98rwCC2/KoiWcmEiRfZe4G+mYwDbCFT87JIMj6GBhLDkAzr/W8KAaA2IDr8Vc6J8fZPgVulxxfMA== +"@aws-sdk/xml-builder@3.679.0": + version "3.679.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/xml-builder/-/xml-builder-3.679.0.tgz#96ccb7a4a4d4faa881d1fec5fc0554dc726843b5" + integrity sha512-nPmhVZb39ty5bcQ7mAwtjezBcsBqTYZ9A2D9v/lE92KCLdu5RhSkPH7O71ZqbZx1mUSg9fAOxHPiG79U5VlpLQ== dependencies: - "@smithy/types" "^3.3.0" + "@smithy/types" "^3.5.0" tslib "^2.6.2" "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.18.6": @@ -1095,10 +1131,10 @@ resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@casl/ability@^6.7.1": - version "6.7.1" - resolved "https://registry.yarnpkg.com/@casl/ability/-/ability-6.7.1.tgz#89691083aafd1cfc4ae9519ffbcb0e7cb77ac201" - integrity sha512-e+Vgrehd1/lzOSwSqKHtmJ6kmIuZbGBlM2LBS5IuYGGKmVHuhUuyh3XgTn1VIw9+TO4gqU+uptvxfIRBUEdJuw== +"@casl/ability@^6.7.2": + version "6.7.2" + resolved "https://registry.yarnpkg.com/@casl/ability/-/ability-6.7.2.tgz#da727933d8310545db274e02866fad0dda847903" + integrity sha512-KjKXlcjKbUz8dKw7PY56F7qlfOFgxTU6tnlJ8YrbDyWkJMIlHa6VRWzCD8RU20zbJUC1hExhOFggZjm6tf1mUw== dependencies: "@ucast/mongo2js" "^1.3.0" @@ -1107,87 +1143,87 @@ resolved "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz" integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== -"@commitlint/cli@^19.4.0": - version "19.4.0" - resolved "https://registry.yarnpkg.com/@commitlint/cli/-/cli-19.4.0.tgz#9f93d3ed07e531fcfa371015c8c87e0aa26d974f" - integrity sha512-sJX4J9UioVwZHq7JWM9tjT5bgWYaIN3rC4FP7YwfEwBYiIO+wMyRttRvQLNkow0vCdM0D67r9NEWU0Ui03I4Eg== - dependencies: - "@commitlint/format" "^19.3.0" - "@commitlint/lint" "^19.2.2" - "@commitlint/load" "^19.4.0" - "@commitlint/read" "^19.4.0" - "@commitlint/types" "^19.0.3" - execa "^8.0.1" +"@commitlint/cli@^19.5.0": + version "19.5.0" + resolved "https://registry.yarnpkg.com/@commitlint/cli/-/cli-19.5.0.tgz#a6e2f7f8397ddf9abd5ee5870e30a1bf51b7be2b" + integrity sha512-gaGqSliGwB86MDmAAKAtV9SV1SHdmN8pnGq4EJU4+hLisQ7IFfx4jvU4s+pk6tl0+9bv6yT+CaZkufOinkSJIQ== + dependencies: + "@commitlint/format" "^19.5.0" + "@commitlint/lint" "^19.5.0" + "@commitlint/load" "^19.5.0" + "@commitlint/read" "^19.5.0" + "@commitlint/types" "^19.5.0" + tinyexec "^0.3.0" yargs "^17.0.0" -"@commitlint/config-conventional@^19.2.2": - version "19.2.2" - resolved "https://registry.yarnpkg.com/@commitlint/config-conventional/-/config-conventional-19.2.2.tgz#1f4e6975d428985deacf2b3ff6547e02c9302054" - integrity sha512-mLXjsxUVLYEGgzbxbxicGPggDuyWNkf25Ht23owXIH+zV2pv1eJuzLK3t1gDY5Gp6pxdE60jZnWUY5cvgL3ufw== +"@commitlint/config-conventional@^19.5.0": + version "19.5.0" + resolved "https://registry.yarnpkg.com/@commitlint/config-conventional/-/config-conventional-19.5.0.tgz#f838cdaed0e0e223cdc2e865f055d474a49fe18c" + integrity sha512-OBhdtJyHNPryZKg0fFpZNOBM1ZDbntMvqMuSmpfyP86XSfwzGw4CaoYRG4RutUPg0BTK07VMRIkNJT6wi2zthg== dependencies: - "@commitlint/types" "^19.0.3" + "@commitlint/types" "^19.5.0" conventional-changelog-conventionalcommits "^7.0.2" -"@commitlint/config-validator@^19.0.3": - version "19.0.3" - resolved "https://registry.yarnpkg.com/@commitlint/config-validator/-/config-validator-19.0.3.tgz#052b181a30da6b4fc16dc5230f4589ac95e0bc81" - integrity sha512-2D3r4PKjoo59zBc2auodrSCaUnCSALCx54yveOFwwP/i2kfEAQrygwOleFWswLqK0UL/F9r07MFi5ev2ohyM4Q== +"@commitlint/config-validator@^19.5.0": + version "19.5.0" + resolved "https://registry.yarnpkg.com/@commitlint/config-validator/-/config-validator-19.5.0.tgz#f0a4eda2109fc716ef01bb8831af9b02e3a1e568" + integrity sha512-CHtj92H5rdhKt17RmgALhfQt95VayrUo2tSqY9g2w+laAXyk7K/Ef6uPm9tn5qSIwSmrLjKaXK9eiNuxmQrDBw== dependencies: - "@commitlint/types" "^19.0.3" + "@commitlint/types" "^19.5.0" ajv "^8.11.0" -"@commitlint/ensure@^19.0.3": - version "19.0.3" - resolved "https://registry.yarnpkg.com/@commitlint/ensure/-/ensure-19.0.3.tgz#d172b1b72ca88cbd317ea1ee79f3a03dbaccc76e" - integrity sha512-SZEpa/VvBLoT+EFZVb91YWbmaZ/9rPH3ESrINOl0HD2kMYsjvl0tF7nMHh0EpTcv4+gTtZBAe1y/SS6/OhfZzQ== +"@commitlint/ensure@^19.5.0": + version "19.5.0" + resolved "https://registry.yarnpkg.com/@commitlint/ensure/-/ensure-19.5.0.tgz#b087374a6a0a0140e5925a82901d234885d9f6dd" + integrity sha512-Kv0pYZeMrdg48bHFEU5KKcccRfKmISSm9MvgIgkpI6m+ohFTB55qZlBW6eYqh/XDfRuIO0x4zSmvBjmOwWTwkg== dependencies: - "@commitlint/types" "^19.0.3" + "@commitlint/types" "^19.5.0" lodash.camelcase "^4.3.0" lodash.kebabcase "^4.1.1" lodash.snakecase "^4.1.1" lodash.startcase "^4.4.0" lodash.upperfirst "^4.3.1" -"@commitlint/execute-rule@^19.0.0": - version "19.0.0" - resolved "https://registry.yarnpkg.com/@commitlint/execute-rule/-/execute-rule-19.0.0.tgz#928fb239ae8deec82a6e3b05ec9cfe20afa83856" - integrity sha512-mtsdpY1qyWgAO/iOK0L6gSGeR7GFcdW7tIjcNFxcWkfLDF5qVbPHKuGATFqRMsxcO8OUKNj0+3WOHB7EHm4Jdw== +"@commitlint/execute-rule@^19.5.0": + version "19.5.0" + resolved "https://registry.yarnpkg.com/@commitlint/execute-rule/-/execute-rule-19.5.0.tgz#c13da8c03ea0379f30856111e27d57518e25b8a2" + integrity sha512-aqyGgytXhl2ejlk+/rfgtwpPexYyri4t8/n4ku6rRJoRhGZpLFMqrZ+YaubeGysCP6oz4mMA34YSTaSOKEeNrg== -"@commitlint/format@^19.3.0": - version "19.3.0" - resolved "https://registry.yarnpkg.com/@commitlint/format/-/format-19.3.0.tgz#48dd9e6930d41eb0ca19f36159ee940c5b25d857" - integrity sha512-luguk5/aF68HiF4H23ACAfk8qS8AHxl4LLN5oxPc24H+2+JRPsNr1OS3Gaea0CrH7PKhArBMKBz5RX9sA5NtTg== +"@commitlint/format@^19.5.0": + version "19.5.0" + resolved "https://registry.yarnpkg.com/@commitlint/format/-/format-19.5.0.tgz#d879db2d97d70ae622397839fb8603d56e85a250" + integrity sha512-yNy088miE52stCI3dhG/vvxFo9e4jFkU1Mj3xECfzp/bIS/JUay4491huAlVcffOoMK1cd296q0W92NlER6r3A== dependencies: - "@commitlint/types" "^19.0.3" + "@commitlint/types" "^19.5.0" chalk "^5.3.0" -"@commitlint/is-ignored@^19.2.2": - version "19.2.2" - resolved "https://registry.yarnpkg.com/@commitlint/is-ignored/-/is-ignored-19.2.2.tgz#503ddcf908ac6b2bc4586a49cb53893a1856f5b2" - integrity sha512-eNX54oXMVxncORywF4ZPFtJoBm3Tvp111tg1xf4zWXGfhBPKpfKG6R+G3G4v5CPlRROXpAOpQ3HMhA9n1Tck1g== +"@commitlint/is-ignored@^19.5.0": + version "19.5.0" + resolved "https://registry.yarnpkg.com/@commitlint/is-ignored/-/is-ignored-19.5.0.tgz#f8b7f365887acc1e3bdb31b17117bb435585dddf" + integrity sha512-0XQ7Llsf9iL/ANtwyZ6G0NGp5Y3EQ8eDQSxv/SRcfJ0awlBY4tHFAvwWbw66FVUaWICH7iE5en+FD9TQsokZ5w== dependencies: - "@commitlint/types" "^19.0.3" + "@commitlint/types" "^19.5.0" semver "^7.6.0" -"@commitlint/lint@^19.2.2": - version "19.2.2" - resolved "https://registry.yarnpkg.com/@commitlint/lint/-/lint-19.2.2.tgz#57f69e24bd832a7dcce8ebf82d11e3bf03ccc2a9" - integrity sha512-xrzMmz4JqwGyKQKTpFzlN0dx0TAiT7Ran1fqEBgEmEj+PU98crOFtysJgY+QdeSagx6EDRigQIXJVnfrI0ratA== - dependencies: - "@commitlint/is-ignored" "^19.2.2" - "@commitlint/parse" "^19.0.3" - "@commitlint/rules" "^19.0.3" - "@commitlint/types" "^19.0.3" - -"@commitlint/load@^19.4.0": - version "19.4.0" - resolved "https://registry.yarnpkg.com/@commitlint/load/-/load-19.4.0.tgz#7df034e226e300fd577d3f63a72d790d5c821f53" - integrity sha512-I4lCWaEZYQJ1y+Y+gdvbGAx9pYPavqZAZ3/7/8BpWh+QjscAn8AjsUpLV2PycBsEx7gupq5gM4BViV9xwTIJuw== - dependencies: - "@commitlint/config-validator" "^19.0.3" - "@commitlint/execute-rule" "^19.0.0" - "@commitlint/resolve-extends" "^19.1.0" - "@commitlint/types" "^19.0.3" +"@commitlint/lint@^19.5.0": + version "19.5.0" + resolved "https://registry.yarnpkg.com/@commitlint/lint/-/lint-19.5.0.tgz#f4e162e7857a1c0694b20b92527704897558ff70" + integrity sha512-cAAQwJcRtiBxQWO0eprrAbOurtJz8U6MgYqLz+p9kLElirzSCc0vGMcyCaA1O7AqBuxo11l1XsY3FhOFowLAAg== + dependencies: + "@commitlint/is-ignored" "^19.5.0" + "@commitlint/parse" "^19.5.0" + "@commitlint/rules" "^19.5.0" + "@commitlint/types" "^19.5.0" + +"@commitlint/load@^19.5.0": + version "19.5.0" + resolved "https://registry.yarnpkg.com/@commitlint/load/-/load-19.5.0.tgz#67f90a294894d1f99b930b6152bed2df44a81794" + integrity sha512-INOUhkL/qaKqwcTUvCE8iIUf5XHsEPCLY9looJ/ipzi7jtGhgmtH7OOFiNvwYgH7mA8osUWOUDV8t4E2HAi4xA== + dependencies: + "@commitlint/config-validator" "^19.5.0" + "@commitlint/execute-rule" "^19.5.0" + "@commitlint/resolve-extends" "^19.5.0" + "@commitlint/types" "^19.5.0" chalk "^5.3.0" cosmiconfig "^9.0.0" cosmiconfig-typescript-loader "^5.0.0" @@ -1195,444 +1231,466 @@ lodash.merge "^4.6.2" lodash.uniq "^4.5.0" -"@commitlint/message@^19.0.0": - version "19.0.0" - resolved "https://registry.yarnpkg.com/@commitlint/message/-/message-19.0.0.tgz#f789dd1b7a1f9c784578e0111f46cc3fecf5a531" - integrity sha512-c9czf6lU+9oF9gVVa2lmKaOARJvt4soRsVmbR7Njwp9FpbBgste5i7l/2l5o8MmbwGh4yE1snfnsy2qyA2r/Fw== +"@commitlint/message@^19.5.0": + version "19.5.0" + resolved "https://registry.yarnpkg.com/@commitlint/message/-/message-19.5.0.tgz#c062d9a1d2b3302c3a8cac25d6d1125ea9c019b2" + integrity sha512-R7AM4YnbxN1Joj1tMfCyBryOC5aNJBdxadTZkuqtWi3Xj0kMdutq16XQwuoGbIzL2Pk62TALV1fZDCv36+JhTQ== -"@commitlint/parse@^19.0.3": - version "19.0.3" - resolved "https://registry.yarnpkg.com/@commitlint/parse/-/parse-19.0.3.tgz#a2d09876d458e17ad0e1695b04f41af8b50a41c2" - integrity sha512-Il+tNyOb8VDxN3P6XoBBwWJtKKGzHlitEuXA5BP6ir/3loWlsSqDr5aecl6hZcC/spjq4pHqNh0qPlfeWu38QA== +"@commitlint/parse@^19.5.0": + version "19.5.0" + resolved "https://registry.yarnpkg.com/@commitlint/parse/-/parse-19.5.0.tgz#b450dad9b5a95ac5ba472d6d0fdab822dce946fc" + integrity sha512-cZ/IxfAlfWYhAQV0TwcbdR1Oc0/r0Ik1GEessDJ3Lbuma/MRO8FRQX76eurcXtmhJC//rj52ZSZuXUg0oIX0Fw== dependencies: - "@commitlint/types" "^19.0.3" + "@commitlint/types" "^19.5.0" conventional-changelog-angular "^7.0.0" conventional-commits-parser "^5.0.0" -"@commitlint/read@^19.4.0": - version "19.4.0" - resolved "https://registry.yarnpkg.com/@commitlint/read/-/read-19.4.0.tgz#3866b1f9a272ef6a388986efa349d24228fc8b00" - integrity sha512-r95jLOEZzKDakXtnQub+zR3xjdnrl2XzerPwm7ch1/cc5JGq04tyaNpa6ty0CRCWdVrk4CZHhqHozb8yZwy2+g== +"@commitlint/read@^19.5.0": + version "19.5.0" + resolved "https://registry.yarnpkg.com/@commitlint/read/-/read-19.5.0.tgz#601f9f1afe69852b0f28aa81cd455b40979fad6b" + integrity sha512-TjS3HLPsLsxFPQj6jou8/CZFAmOP2y+6V4PGYt3ihbQKTY1Jnv0QG28WRKl/d1ha6zLODPZqsxLEov52dhR9BQ== dependencies: - "@commitlint/top-level" "^19.0.0" - "@commitlint/types" "^19.0.3" - execa "^8.0.1" + "@commitlint/top-level" "^19.5.0" + "@commitlint/types" "^19.5.0" git-raw-commits "^4.0.0" minimist "^1.2.8" + tinyexec "^0.3.0" -"@commitlint/resolve-extends@^19.1.0": - version "19.1.0" - resolved "https://registry.yarnpkg.com/@commitlint/resolve-extends/-/resolve-extends-19.1.0.tgz#fa5b8f921e9c8d76f53624c35bf25b9676bd73fa" - integrity sha512-z2riI+8G3CET5CPgXJPlzftH+RiWYLMYv4C9tSLdLXdr6pBNimSKukYP9MS27ejmscqCTVA4almdLh0ODD2KYg== +"@commitlint/resolve-extends@^19.5.0": + version "19.5.0" + resolved "https://registry.yarnpkg.com/@commitlint/resolve-extends/-/resolve-extends-19.5.0.tgz#f3ec33e12d10df90cae0bfad8e593431fb61b18e" + integrity sha512-CU/GscZhCUsJwcKTJS9Ndh3AKGZTNFIOoQB2n8CmFnizE0VnEuJoum+COW+C1lNABEeqk6ssfc1Kkalm4bDklA== dependencies: - "@commitlint/config-validator" "^19.0.3" - "@commitlint/types" "^19.0.3" + "@commitlint/config-validator" "^19.5.0" + "@commitlint/types" "^19.5.0" global-directory "^4.0.1" import-meta-resolve "^4.0.0" lodash.mergewith "^4.6.2" resolve-from "^5.0.0" -"@commitlint/rules@^19.0.3": - version "19.0.3" - resolved "https://registry.yarnpkg.com/@commitlint/rules/-/rules-19.0.3.tgz#de647a9055847cae4f3ae32b4798096b604584f3" - integrity sha512-TspKb9VB6svklxNCKKwxhELn7qhtY1rFF8ls58DcFd0F97XoG07xugPjjbVnLqmMkRjZDbDIwBKt9bddOfLaPw== +"@commitlint/rules@^19.5.0": + version "19.5.0" + resolved "https://registry.yarnpkg.com/@commitlint/rules/-/rules-19.5.0.tgz#2a72ab506d49d7f33eda56f0ae072a3479429e74" + integrity sha512-hDW5TPyf/h1/EufSHEKSp6Hs+YVsDMHazfJ2azIk9tHPXS6UqSz1dIRs1gpqS3eMXgtkT7JH6TW4IShdqOwhAw== dependencies: - "@commitlint/ensure" "^19.0.3" - "@commitlint/message" "^19.0.0" - "@commitlint/to-lines" "^19.0.0" - "@commitlint/types" "^19.0.3" - execa "^8.0.1" + "@commitlint/ensure" "^19.5.0" + "@commitlint/message" "^19.5.0" + "@commitlint/to-lines" "^19.5.0" + "@commitlint/types" "^19.5.0" -"@commitlint/to-lines@^19.0.0": - version "19.0.0" - resolved "https://registry.yarnpkg.com/@commitlint/to-lines/-/to-lines-19.0.0.tgz#aa6618eb371bafbc0cd3b48f0db565c4a40462c6" - integrity sha512-vkxWo+VQU5wFhiP9Ub9Sre0FYe019JxFikrALVoD5UGa8/t3yOJEpEhxC5xKiENKKhUkTpEItMTRAjHw2SCpZw== +"@commitlint/to-lines@^19.5.0": + version "19.5.0" + resolved "https://registry.yarnpkg.com/@commitlint/to-lines/-/to-lines-19.5.0.tgz#e4b7f34f09064568c96a74de4f1fc9f466c4d472" + integrity sha512-R772oj3NHPkodOSRZ9bBVNq224DOxQtNef5Pl8l2M8ZnkkzQfeSTr4uxawV2Sd3ui05dUVzvLNnzenDBO1KBeQ== -"@commitlint/top-level@^19.0.0": - version "19.0.0" - resolved "https://registry.yarnpkg.com/@commitlint/top-level/-/top-level-19.0.0.tgz#9c44d7cec533bb9598bfae9658737e2d6a903605" - integrity sha512-KKjShd6u1aMGNkCkaX4aG1jOGdn7f8ZI8TR1VEuNqUOjWTOdcDSsmglinglJ18JTjuBX5I1PtjrhQCRcixRVFQ== +"@commitlint/top-level@^19.5.0": + version "19.5.0" + resolved "https://registry.yarnpkg.com/@commitlint/top-level/-/top-level-19.5.0.tgz#0017ffe39b5ba3611a1debd62efe28803601a14f" + integrity sha512-IP1YLmGAk0yWrImPRRc578I3dDUI5A2UBJx9FbSOjxe9sTlzFiwVJ+zeMLgAtHMtGZsC8LUnzmW1qRemkFU4ng== dependencies: find-up "^7.0.0" -"@commitlint/types@^19.0.3": - version "19.0.3" - resolved "https://registry.yarnpkg.com/@commitlint/types/-/types-19.0.3.tgz#feff4ecac2b5c359f2a57f9ab094b2ac80ef0266" - integrity sha512-tpyc+7i6bPG9mvaBbtKUeghfyZSDgWquIDfMgqYtTbmZ9Y9VzEm2je9EYcQ0aoz5o7NvGS+rcDec93yO08MHYA== +"@commitlint/types@^19.5.0": + version "19.5.0" + resolved "https://registry.yarnpkg.com/@commitlint/types/-/types-19.5.0.tgz#c5084d1231d4dd50e40bdb656ee7601f691400b3" + integrity sha512-DSHae2obMSMkAtTBSOulg5X7/z+rGLxcXQIkg3OmWvY6wifojge5uVMydfhUvs7yQj+V7jNmRZ2Xzl8GJyqRgg== dependencies: "@types/conventional-commits-parser" "^5.0.0" chalk "^5.3.0" -"@cspell/cspell-bundled-dicts@8.13.3": - version "8.13.3" - resolved "https://registry.yarnpkg.com/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-8.13.3.tgz#7b214ae6eb6442ef6391765f98c1b9294e3c455e" - integrity sha512-OfCxUBMyayxKyeDaUZG3LQpiyH8MFUbg9nbIZCGh2x8U6N0fHaP9uR6R+gPzdi/bJp32Kr+RC/Yebojd+AQCGA== - dependencies: - "@cspell/dict-ada" "^4.0.2" - "@cspell/dict-aws" "^4.0.3" - "@cspell/dict-bash" "^4.1.3" - "@cspell/dict-companies" "^3.1.4" - "@cspell/dict-cpp" "^5.1.12" - "@cspell/dict-cryptocurrencies" "^5.0.0" - "@cspell/dict-csharp" "^4.0.2" - "@cspell/dict-css" "^4.0.12" - "@cspell/dict-dart" "^2.0.3" - "@cspell/dict-django" "^4.1.0" - "@cspell/dict-docker" "^1.1.7" - "@cspell/dict-dotnet" "^5.0.2" - "@cspell/dict-elixir" "^4.0.3" - "@cspell/dict-en-common-misspellings" "^2.0.4" +"@cspell/cspell-bundled-dicts@8.15.6": + version "8.15.6" + resolved "https://registry.yarnpkg.com/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-8.15.6.tgz#a40a3c092c86967dbf022067befcf54a3a2d5e63" + integrity sha512-laYeiG6gDxGALhRRgq1F8+ddrpgb6ZTnhAL0pXXjAlg268bWvZ0nS8etRZYOJ38RZQX4QAoy8+8JpiLJUmKbHw== + dependencies: + "@cspell/dict-ada" "^4.0.5" + "@cspell/dict-al" "^1.0.3" + "@cspell/dict-aws" "^4.0.7" + "@cspell/dict-bash" "^4.1.8" + "@cspell/dict-companies" "^3.1.7" + "@cspell/dict-cpp" "^6.0.0" + "@cspell/dict-cryptocurrencies" "^5.0.3" + "@cspell/dict-csharp" "^4.0.5" + "@cspell/dict-css" "^4.0.16" + "@cspell/dict-dart" "^2.2.4" + "@cspell/dict-django" "^4.1.3" + "@cspell/dict-docker" "^1.1.11" + "@cspell/dict-dotnet" "^5.0.8" + "@cspell/dict-elixir" "^4.0.6" + "@cspell/dict-en-common-misspellings" "^2.0.7" "@cspell/dict-en-gb" "1.1.33" - "@cspell/dict-en_us" "^4.3.23" - "@cspell/dict-filetypes" "^3.0.4" - "@cspell/dict-fonts" "^4.0.0" - "@cspell/dict-fsharp" "^1.0.1" - "@cspell/dict-fullstack" "^3.2.0" - "@cspell/dict-gaming-terms" "^1.0.5" - "@cspell/dict-git" "^3.0.0" - "@cspell/dict-golang" "^6.0.9" - "@cspell/dict-google" "^1.0.1" - "@cspell/dict-haskell" "^4.0.1" - "@cspell/dict-html" "^4.0.5" - "@cspell/dict-html-symbol-entities" "^4.0.0" - "@cspell/dict-java" "^5.0.7" - "@cspell/dict-julia" "^1.0.1" - "@cspell/dict-k8s" "^1.0.6" - "@cspell/dict-latex" "^4.0.0" - "@cspell/dict-lorem-ipsum" "^4.0.0" - "@cspell/dict-lua" "^4.0.3" - "@cspell/dict-makefile" "^1.0.0" - "@cspell/dict-monkeyc" "^1.0.6" - "@cspell/dict-node" "^5.0.1" - "@cspell/dict-npm" "^5.0.18" - "@cspell/dict-php" "^4.0.8" - "@cspell/dict-powershell" "^5.0.5" - "@cspell/dict-public-licenses" "^2.0.7" - "@cspell/dict-python" "^4.2.4" - "@cspell/dict-r" "^2.0.1" - "@cspell/dict-ruby" "^5.0.2" - "@cspell/dict-rust" "^4.0.5" - "@cspell/dict-scala" "^5.0.3" - "@cspell/dict-software-terms" "^4.0.6" - "@cspell/dict-sql" "^2.1.5" - "@cspell/dict-svelte" "^1.0.2" - "@cspell/dict-swift" "^2.0.1" - "@cspell/dict-terraform" "^1.0.0" - "@cspell/dict-typescript" "^3.1.6" - "@cspell/dict-vue" "^3.0.0" - -"@cspell/cspell-json-reporter@8.13.3": - version "8.13.3" - resolved "https://registry.yarnpkg.com/@cspell/cspell-json-reporter/-/cspell-json-reporter-8.13.3.tgz#dbce6f3b1104ba1dca15c645f2a005a228104ef0" - integrity sha512-QrHxWkm0cfD+rTjFOxm5lpE4+wBANDzMIM8NOeQC6v8Dc1L8PUkm6hF6CsEv2tKmuwvdVr+jy6GilDMkPXalCg== - dependencies: - "@cspell/cspell-types" "8.13.3" - -"@cspell/cspell-pipe@8.13.3": - version "8.13.3" - resolved "https://registry.yarnpkg.com/@cspell/cspell-pipe/-/cspell-pipe-8.13.3.tgz#eca94cd30a97fcea6abffd6189db916505acbce0" - integrity sha512-6a9Zd+fDltgXoJ0fosWqEMx0UdXBXZ7iakhslMNPRmv7GhVAoHBoIXzMVilOE4kYT2Mh/9NM/QW/NbNEpneZIQ== - -"@cspell/cspell-resolver@8.13.3": - version "8.13.3" - resolved "https://registry.yarnpkg.com/@cspell/cspell-resolver/-/cspell-resolver-8.13.3.tgz#423c96b7834aa33b8f487412e82083652d05406a" - integrity sha512-vlwtMTEWsPPtWfktzT75eGQ0n+0M+9kN+89eSvUUYdCfvY9XAS6z+bTmhS2ULJgntgWtX6gUjABQK0PYYVedOg== + "@cspell/dict-en_us" "^4.3.26" + "@cspell/dict-filetypes" "^3.0.8" + "@cspell/dict-flutter" "^1.0.3" + "@cspell/dict-fonts" "^4.0.3" + "@cspell/dict-fsharp" "^1.0.4" + "@cspell/dict-fullstack" "^3.2.3" + "@cspell/dict-gaming-terms" "^1.0.8" + "@cspell/dict-git" "^3.0.3" + "@cspell/dict-golang" "^6.0.16" + "@cspell/dict-google" "^1.0.4" + "@cspell/dict-haskell" "^4.0.4" + "@cspell/dict-html" "^4.0.10" + "@cspell/dict-html-symbol-entities" "^4.0.3" + "@cspell/dict-java" "^5.0.10" + "@cspell/dict-julia" "^1.0.4" + "@cspell/dict-k8s" "^1.0.9" + "@cspell/dict-latex" "^4.0.3" + "@cspell/dict-lorem-ipsum" "^4.0.3" + "@cspell/dict-lua" "^4.0.6" + "@cspell/dict-makefile" "^1.0.3" + "@cspell/dict-markdown" "^2.0.7" + "@cspell/dict-monkeyc" "^1.0.9" + "@cspell/dict-node" "^5.0.4" + "@cspell/dict-npm" "^5.1.9" + "@cspell/dict-php" "^4.0.13" + "@cspell/dict-powershell" "^5.0.13" + "@cspell/dict-public-licenses" "^2.0.11" + "@cspell/dict-python" "^4.2.12" + "@cspell/dict-r" "^2.0.4" + "@cspell/dict-ruby" "^5.0.7" + "@cspell/dict-rust" "^4.0.9" + "@cspell/dict-scala" "^5.0.6" + "@cspell/dict-software-terms" "^4.1.12" + "@cspell/dict-sql" "^2.1.8" + "@cspell/dict-svelte" "^1.0.5" + "@cspell/dict-swift" "^2.0.4" + "@cspell/dict-terraform" "^1.0.6" + "@cspell/dict-typescript" "^3.1.11" + "@cspell/dict-vue" "^3.0.3" + +"@cspell/cspell-json-reporter@8.15.6": + version "8.15.6" + resolved "https://registry.yarnpkg.com/@cspell/cspell-json-reporter/-/cspell-json-reporter-8.15.6.tgz#b093848145babd7338921bad4412d49c7d0f9585" + integrity sha512-LKyUugQdz3yJKTW0lDMTtR362feDowFq7+qIfJL3RQmKnzvrl5azZCombeXnfMhNKoge80BGzp22c6XmZV0aeQ== + dependencies: + "@cspell/cspell-types" "8.15.6" + +"@cspell/cspell-pipe@8.15.6": + version "8.15.6" + resolved "https://registry.yarnpkg.com/@cspell/cspell-pipe/-/cspell-pipe-8.15.6.tgz#46ae271160d6c295cf4ef782ca4b2161d9cc1d39" + integrity sha512-jKnHv8cUVHIhDQFiNq3FWD8H3kmsAFaudy+EOEZEZHXBYvqOW7qpFn7Z4RnjMZOYJMY6zLiTEXVjPOX4/tyoYg== + +"@cspell/cspell-resolver@8.15.6": + version "8.15.6" + resolved "https://registry.yarnpkg.com/@cspell/cspell-resolver/-/cspell-resolver-8.15.6.tgz#8f08dcd018b35d1545946b6e5e58f155fcc213fc" + integrity sha512-5TpTmWGSRROldqwaYQ1wyRx5K5NMdDjFmxKEJ8FIVzaek8U7VpVgBEA6Ddb3r9L6dLI1IskfFyrVpRRnswLgfQ== dependencies: global-directory "^4.0.1" -"@cspell/cspell-service-bus@8.13.3": - version "8.13.3" - resolved "https://registry.yarnpkg.com/@cspell/cspell-service-bus/-/cspell-service-bus-8.13.3.tgz#058121ff78c25afc7bc6218233e6b15e660dbfd5" - integrity sha512-mFkeWXwGQSDxRiN6Kez77GaMNGNgG7T6o9UE42jyXEgf/bLJTpefbUy4fY5pU3p2mA0eoMzmnJX8l+TC5YJpbA== +"@cspell/cspell-service-bus@8.15.6": + version "8.15.6" + resolved "https://registry.yarnpkg.com/@cspell/cspell-service-bus/-/cspell-service-bus-8.15.6.tgz#f131af301d56b923be80cacdaf51bee4c0745eba" + integrity sha512-m8i9WB4kRtoAAog68qAN3OCvGC+I1CTkHEI25duc7W9zpBRqxJ1E8W22RBr9xUqcz8ppsrFmLe/VGyA9w1wWSQ== -"@cspell/cspell-types@8.13.3": - version "8.13.3" - resolved "https://registry.yarnpkg.com/@cspell/cspell-types/-/cspell-types-8.13.3.tgz#180fcebfd1ca21cf09986470de0898b8c7893545" - integrity sha512-lA5GbhLOL6FlKCWNMbooRFgNGfTsM6NJnHz60+EEN7XD9OgpFc7w+MBcK4aHsVCxcrIvnejIc8xQDqPnrdmN3w== +"@cspell/cspell-types@8.15.6": + version "8.15.6" + resolved "https://registry.yarnpkg.com/@cspell/cspell-types/-/cspell-types-8.15.6.tgz#eca62eb745c5541855357e02591ec559c37f6603" + integrity sha512-hilaqr4wdU3y32DxXDXX0gYfOIa4KCD9m58qLokGZiyWikKAALlnV25u6gM62uoPCvlyCPpt0AW68wSvMEo/zA== -"@cspell/dict-ada@^4.0.2": - version "4.0.2" - resolved "https://registry.npmjs.org/@cspell/dict-ada/-/dict-ada-4.0.2.tgz" - integrity sha512-0kENOWQeHjUlfyId/aCM/mKXtkEgV0Zu2RhUXCBr4hHo9F9vph+Uu8Ww2b0i5a4ZixoIkudGA+eJvyxrG1jUpA== +"@cspell/dict-ada@^4.0.5": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@cspell/dict-ada/-/dict-ada-4.0.5.tgz#c14aae2faaecbad2d99f0d701e4700a48c68ef60" + integrity sha512-6/RtZ/a+lhFVmrx/B7bfP7rzC4yjEYe8o74EybXcvu4Oue6J4Ey2WSYj96iuodloj1LWrkNCQyX5h4Pmcj0Iag== -"@cspell/dict-aws@^4.0.3": - version "4.0.3" - resolved "https://registry.yarnpkg.com/@cspell/dict-aws/-/dict-aws-4.0.3.tgz#7d36d4d5439d1c39b815e0ae19f79e48a823e047" - integrity sha512-0C0RQ4EM29fH0tIYv+EgDQEum0QI6OrmjENC9u98pB8UcnYxGG/SqinuPxo+TgcEuInj0Q73MsBpJ1l5xUnrsw== +"@cspell/dict-al@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@cspell/dict-al/-/dict-al-1.0.3.tgz#09e288b5ab56b126dce895d3301faf7c0dd732d6" + integrity sha512-V1HClwlfU/qwSq2Kt+MkqRAsonNu3mxjSCDyGRecdLGIHmh7yeEeaxqRiO/VZ4KP+eVSiSIlbwrb5YNFfxYZbw== -"@cspell/dict-bash@^4.1.3": - version "4.1.3" - resolved "https://registry.npmjs.org/@cspell/dict-bash/-/dict-bash-4.1.3.tgz" - integrity sha512-tOdI3QVJDbQSwPjUkOiQFhYcu2eedmX/PtEpVWg0aFps/r6AyjUQINtTgpqMYnYuq8O1QUIQqnpx21aovcgZCw== +"@cspell/dict-aws@^4.0.7": + version "4.0.7" + resolved "https://registry.yarnpkg.com/@cspell/dict-aws/-/dict-aws-4.0.7.tgz#f96f3b70cd52a25b895eb08e297de5a5cc3fc5b6" + integrity sha512-PoaPpa2NXtSkhGIMIKhsJUXB6UbtTt6Ao3x9JdU9kn7fRZkwD4RjHDGqulucIOz7KeEX/dNRafap6oK9xHe4RA== -"@cspell/dict-companies@^3.1.4": - version "3.1.4" - resolved "https://registry.yarnpkg.com/@cspell/dict-companies/-/dict-companies-3.1.4.tgz#2e7094416432b8547ec335683f5aac9a49dce47e" - integrity sha512-y9e0amzEK36EiiKx3VAA+SHQJPpf2Qv5cCt5eTUSggpTkiFkCh6gRKQ97rVlrKh5GJrqinDwYIJtTsxuh2vy2Q== +"@cspell/dict-bash@^4.1.8": + version "4.1.8" + resolved "https://registry.yarnpkg.com/@cspell/dict-bash/-/dict-bash-4.1.8.tgz#26dc898e06eddea069cf1ad475ee0e867c89e632" + integrity sha512-I2CM2pTNthQwW069lKcrVxchJGMVQBzru2ygsHCwgidXRnJL/NTjAPOFTxN58Jc1bf7THWghfEDyKX/oyfc0yg== -"@cspell/dict-cpp@^5.1.12": - version "5.1.12" - resolved "https://registry.yarnpkg.com/@cspell/dict-cpp/-/dict-cpp-5.1.12.tgz#52d5ed8b96268e8282f6d7694ee2434b20bafb21" - integrity sha512-6lXLOFIa+k/qBcu0bjaE/Kc6v3sh9VhsDOXD1Dalm3zgd0QIMjp5XBmkpSdCAK3pWCPV0Se7ysVLDfCea1BuXg== +"@cspell/dict-companies@^3.1.7": + version "3.1.7" + resolved "https://registry.yarnpkg.com/@cspell/dict-companies/-/dict-companies-3.1.7.tgz#c9abd6f5293f103062f54dde01f2bee939189f79" + integrity sha512-ncVs/efuAkP1/tLDhWbXukBjgZ5xOUfe03neHMWsE8zvXXc5+Lw6TX5jaJXZLOoES/f4j4AhRE20jsPCF5pm+A== -"@cspell/dict-cryptocurrencies@^5.0.0": - version "5.0.0" - resolved "https://registry.npmjs.org/@cspell/dict-cryptocurrencies/-/dict-cryptocurrencies-5.0.0.tgz" - integrity sha512-Z4ARIw5+bvmShL+4ZrhDzGhnc9znaAGHOEMaB/GURdS/jdoreEDY34wdN0NtdLHDO5KO7GduZnZyqGdRoiSmYA== +"@cspell/dict-cpp@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@cspell/dict-cpp/-/dict-cpp-6.0.0.tgz#8d0278e789e28e32152e940130dbe47bc9a1cacf" + integrity sha512-z9Mv3/Ool19aw/kl1C+NmLdHF5WN6XCDmP4UkM/sYIfBO4WMUmrhiKiqK/pzSih9jyC5C2AuxujtB/IdHmyfVg== -"@cspell/dict-csharp@^4.0.2": - version "4.0.2" - resolved "https://registry.npmjs.org/@cspell/dict-csharp/-/dict-csharp-4.0.2.tgz" - integrity sha512-1JMofhLK+4p4KairF75D3A924m5ERMgd1GvzhwK2geuYgd2ZKuGW72gvXpIV7aGf52E3Uu1kDXxxGAiZ5uVG7g== +"@cspell/dict-cryptocurrencies@^5.0.3": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@cspell/dict-cryptocurrencies/-/dict-cryptocurrencies-5.0.3.tgz#502f9fffcb2835a3379668ddebdc487678ce6207" + integrity sha512-bl5q+Mk+T3xOZ12+FG37dB30GDxStza49Rmoax95n37MTLksk9wBo1ICOlPJ6PnDUSyeuv4SIVKgRKMKkJJglA== -"@cspell/dict-css@^4.0.12": - version "4.0.12" - resolved "https://registry.npmjs.org/@cspell/dict-css/-/dict-css-4.0.12.tgz" - integrity sha512-vGBgPM92MkHQF5/2jsWcnaahOZ+C6OE/fPvd5ScBP72oFY9tn5GLuomcyO0z8vWCr2e0nUSX1OGimPtcQAlvSw== +"@cspell/dict-csharp@^4.0.5": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@cspell/dict-csharp/-/dict-csharp-4.0.5.tgz#c677c50be09ca5bb3a2cc0be15f3cd05141fd2f7" + integrity sha512-c/sFnNgtRwRJxtC3JHKkyOm+U3/sUrltFeNwml9VsxKBHVmvlg4tk4ar58PdpW9/zTlGUkWi2i85//DN1EsUCA== -"@cspell/dict-dart@^2.0.3": - version "2.0.3" - resolved "https://registry.npmjs.org/@cspell/dict-dart/-/dict-dart-2.0.3.tgz" - integrity sha512-cLkwo1KT5CJY5N5RJVHks2genFkNCl/WLfj+0fFjqNR+tk3tBI1LY7ldr9piCtSFSm4x9pO1x6IV3kRUY1lLiw== +"@cspell/dict-css@^4.0.16": + version "4.0.16" + resolved "https://registry.yarnpkg.com/@cspell/dict-css/-/dict-css-4.0.16.tgz#b7b87b5ea0f1157b023205bdb00070a7d231e367" + integrity sha512-70qu7L9z/JR6QLyJPk38fNTKitlIHnfunx0wjpWQUQ8/jGADIhMCrz6hInBjqPNdtGpYm8d1dNFyF8taEkOgrQ== -"@cspell/dict-data-science@^2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@cspell/dict-data-science/-/dict-data-science-2.0.1.tgz#ef8040821567786d76c6153ac3e4bc265ca65b59" - integrity sha512-xeutkzK0eBe+LFXOFU2kJeAYO6IuFUc1g7iRLr7HeCmlC4rsdGclwGHh61KmttL3+YHQytYStxaRBdGAXWC8Lw== +"@cspell/dict-dart@^2.2.4": + version "2.2.4" + resolved "https://registry.yarnpkg.com/@cspell/dict-dart/-/dict-dart-2.2.4.tgz#8b877161ccdc65cead912b742b71aa55099c1706" + integrity sha512-of/cVuUIZZK/+iqefGln8G3bVpfyN6ZtH+LyLkHMoR5tEj+2vtilGNk9ngwyR8L4lEqbKuzSkOxgfVjsXf5PsQ== -"@cspell/dict-django@^4.1.0": - version "4.1.0" - resolved "https://registry.npmjs.org/@cspell/dict-django/-/dict-django-4.1.0.tgz" - integrity sha512-bKJ4gPyrf+1c78Z0Oc4trEB9MuhcB+Yg+uTTWsvhY6O2ncFYbB/LbEZfqhfmmuK/XJJixXfI1laF2zicyf+l0w== +"@cspell/dict-data-science@^2.0.5": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@cspell/dict-data-science/-/dict-data-science-2.0.5.tgz#816e9b394c2a423d14cdc9a5de5d6fc6141d3900" + integrity sha512-nNSILXmhSJox9/QoXICPQgm8q5PbiSQP4afpbkBqPi/u/b3K9MbNH5HvOOa6230gxcGdbZ9Argl2hY/U8siBlg== -"@cspell/dict-docker@^1.1.7": - version "1.1.7" - resolved "https://registry.npmjs.org/@cspell/dict-docker/-/dict-docker-1.1.7.tgz" - integrity sha512-XlXHAr822euV36GGsl2J1CkBIVg3fZ6879ZOg5dxTIssuhUOCiV2BuzKZmt6aIFmcdPmR14+9i9Xq+3zuxeX0A== +"@cspell/dict-django@^4.1.3": + version "4.1.3" + resolved "https://registry.yarnpkg.com/@cspell/dict-django/-/dict-django-4.1.3.tgz#a02a4a9ef8c9f47344f2d4a0c3964bcb62069ef5" + integrity sha512-yBspeL3roJlO0a1vKKNaWABURuHdHZ9b1L8d3AukX0AsBy9snSggc8xCavPmSzNfeMDXbH+1lgQiYBd3IW03fg== -"@cspell/dict-dotnet@^5.0.2": - version "5.0.2" - resolved "https://registry.yarnpkg.com/@cspell/dict-dotnet/-/dict-dotnet-5.0.2.tgz#d89ca8fa2e546b5e1b1f1288746d26bb627d9f38" - integrity sha512-UD/pO2A2zia/YZJ8Kck/F6YyDSpCMq0YvItpd4YbtDVzPREfTZ48FjZsbYi4Jhzwfvc6o8R56JusAE58P+4sNQ== +"@cspell/dict-docker@^1.1.11": + version "1.1.11" + resolved "https://registry.yarnpkg.com/@cspell/dict-docker/-/dict-docker-1.1.11.tgz#6fce86eb6d86d73f77e18d3e7b9747bad3ca98de" + integrity sha512-s0Yhb16/R+UT1y727ekbR/itWQF3Qz275DR1ahOa66wYtPjHUXmhM3B/LT3aPaX+hD6AWmK23v57SuyfYHUjsw== -"@cspell/dict-elixir@^4.0.3": - version "4.0.3" - resolved "https://registry.npmjs.org/@cspell/dict-elixir/-/dict-elixir-4.0.3.tgz" - integrity sha512-g+uKLWvOp9IEZvrIvBPTr/oaO6619uH/wyqypqvwpmnmpjcfi8+/hqZH8YNKt15oviK8k4CkINIqNhyndG9d9Q== +"@cspell/dict-dotnet@^5.0.8": + version "5.0.8" + resolved "https://registry.yarnpkg.com/@cspell/dict-dotnet/-/dict-dotnet-5.0.8.tgz#8a110ca302946025e0273a9940079483ec33a88a" + integrity sha512-MD8CmMgMEdJAIPl2Py3iqrx3B708MbCIXAuOeZ0Mzzb8YmLmiisY7QEYSZPg08D7xuwARycP0Ki+bb0GAkFSqg== -"@cspell/dict-en-common-misspellings@^2.0.4": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@cspell/dict-en-common-misspellings/-/dict-en-common-misspellings-2.0.4.tgz#725c5b2c83faff71fcd2183dd04a154c78eed674" - integrity sha512-lvOiRjV/FG4pAGZL3PN2GCVHSTCE92cwhfLGGkOsQtxSmef6WCHfHwp9auafkBlX0yFQSKDfq6/TlpQbjbJBtQ== +"@cspell/dict-elixir@^4.0.6": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@cspell/dict-elixir/-/dict-elixir-4.0.6.tgz#3d8965c558d8afd190356e9a900b02c546741feb" + integrity sha512-TfqSTxMHZ2jhiqnXlVKM0bUADtCvwKQv2XZL/DI0rx3doG8mEMS8SGPOmiyyGkHpR/pGOq18AFH3BEm4lViHIw== + +"@cspell/dict-en-common-misspellings@^2.0.7": + version "2.0.7" + resolved "https://registry.yarnpkg.com/@cspell/dict-en-common-misspellings/-/dict-en-common-misspellings-2.0.7.tgz#62861cc9e813c947ebd71c7a50fc720767b4b543" + integrity sha512-qNFo3G4wyabcwnM+hDrMYKN9vNVg/k9QkhqSlSst6pULjdvPyPs1mqz1689xO/v9t8e6sR4IKc3CgUXDMTYOpA== "@cspell/dict-en-gb@1.1.33": version "1.1.33" resolved "https://registry.npmjs.org/@cspell/dict-en-gb/-/dict-en-gb-1.1.33.tgz" integrity sha512-tKSSUf9BJEV+GJQAYGw5e+ouhEe2ZXE620S7BLKe3ZmpnjlNG9JqlnaBhkIMxKnNFkLY2BP/EARzw31AZnOv4g== -"@cspell/dict-en_us@^4.3.23": - version "4.3.23" - resolved "https://registry.yarnpkg.com/@cspell/dict-en_us/-/dict-en_us-4.3.23.tgz#3362b75a5051405816728ea1bb5ce997582ed383" - integrity sha512-l0SoEQBsi3zDSl3OuL4/apBkxjuj4hLIg/oy6+gZ7LWh03rKdF6VNtSZNXWAmMY+pmb1cGA3ouleTiJIglbsIg== +"@cspell/dict-en_us@^4.3.26": + version "4.3.26" + resolved "https://registry.yarnpkg.com/@cspell/dict-en_us/-/dict-en_us-4.3.26.tgz#f0d2c9492715e85b60a78f62f03918525639aa48" + integrity sha512-hDbHYJsi3UgU1J++B0WLiYhWQdsmve3CH53FIaMRAdhrWOHcuw7h1dYkQXHFEP5lOjaq53KUHp/oh5su6VkIZg== -"@cspell/dict-filetypes@^3.0.4": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@cspell/dict-filetypes/-/dict-filetypes-3.0.4.tgz#aca71c7bb8c8805b54f382d98ded5ec75ebc1e36" - integrity sha512-IBi8eIVdykoGgIv5wQhOURi5lmCNJq0we6DvqKoPQJHthXbgsuO1qrHSiUVydMiQl/XvcnUWTMeAlVUlUClnVg== - -"@cspell/dict-fonts@^4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@cspell/dict-fonts/-/dict-fonts-4.0.0.tgz" - integrity sha512-t9V4GeN/m517UZn63kZPUYP3OQg5f0OBLSd3Md5CU3eH1IFogSvTzHHnz4Wqqbv8NNRiBZ3HfdY/pqREZ6br3Q== +"@cspell/dict-filetypes@^3.0.8": + version "3.0.8" + resolved "https://registry.yarnpkg.com/@cspell/dict-filetypes/-/dict-filetypes-3.0.8.tgz#016d523ca2c34dea972ea0ca931255868348d81a" + integrity sha512-D3N8sm/iptzfVwsib/jvpX+K/++rM8SRpLDFUaM4jxm8EyGmSIYRbKZvdIv5BkAWmMlTWoRqlLn7Yb1b11jKJg== -"@cspell/dict-fsharp@^1.0.1": - version "1.0.1" - resolved "https://registry.npmjs.org/@cspell/dict-fsharp/-/dict-fsharp-1.0.1.tgz" - integrity sha512-23xyPcD+j+NnqOjRHgW3IU7Li912SX9wmeefcY0QxukbAxJ/vAN4rBpjSwwYZeQPAn3fxdfdNZs03fg+UM+4yQ== +"@cspell/dict-flutter@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@cspell/dict-flutter/-/dict-flutter-1.0.3.tgz#23e552209ab2238733d30ca3f2a141359756af51" + integrity sha512-52C9aUEU22ptpgYh6gQyIdA4MP6NPwzbEqndfgPh3Sra191/kgs7CVqXiO1qbtZa9gnYHUoVApkoxRE7mrXHfg== -"@cspell/dict-fullstack@^3.2.0": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@cspell/dict-fullstack/-/dict-fullstack-3.2.0.tgz#16dd2bd3f03166c8f48600ef032ae1ce184c7b8e" - integrity sha512-sIGQwU6G3rLTo+nx0GKyirR5dQSFeTIzFTOrURw51ISf+jKG9a3OmvsVtc2OANfvEAOLOC9Wfd8WYhmsO8KRDQ== +"@cspell/dict-fonts@^4.0.3": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@cspell/dict-fonts/-/dict-fonts-4.0.3.tgz#abf578c10a2e7b2bd8f4374002677625288560d9" + integrity sha512-sPd17kV5qgYXLteuHFPn5mbp/oCHKgitNfsZLFC3W2fWEgZlhg4hK+UGig3KzrYhhvQ8wBnmZrAQm0TFKCKzsA== -"@cspell/dict-gaming-terms@^1.0.5": - version "1.0.5" - resolved "https://registry.npmjs.org/@cspell/dict-gaming-terms/-/dict-gaming-terms-1.0.5.tgz" - integrity sha512-C3riccZDD3d9caJQQs1+MPfrUrQ+0KHdlj9iUR1QD92FgTOF6UxoBpvHUUZ9YSezslcmpFQK4xQQ5FUGS7uWfw== +"@cspell/dict-fsharp@^1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@cspell/dict-fsharp/-/dict-fsharp-1.0.4.tgz#19a7263a61ca89cd3ec9c17537e424907b81ef38" + integrity sha512-G5wk0o1qyHUNi9nVgdE1h5wl5ylq7pcBjX8vhjHcO4XBq20D5eMoXjwqMo/+szKAqzJ+WV3BgAL50akLKrT9Rw== -"@cspell/dict-git@^3.0.0": - version "3.0.0" - resolved "https://registry.npmjs.org/@cspell/dict-git/-/dict-git-3.0.0.tgz" - integrity sha512-simGS/lIiXbEaqJu9E2VPoYW1OTC2xrwPPXNXFMa2uo/50av56qOuaxDrZ5eH1LidFXwoc8HROCHYeKoNrDLSw== +"@cspell/dict-fullstack@^3.2.3": + version "3.2.3" + resolved "https://registry.yarnpkg.com/@cspell/dict-fullstack/-/dict-fullstack-3.2.3.tgz#f6fff74eff00c6759cba510168acada0619004cc" + integrity sha512-62PbndIyQPH11mAv0PyiyT0vbwD0AXEocPpHlCHzfb5v9SspzCCbzQ/LIBiFmyRa+q5LMW35CnSVu6OXdT+LKg== -"@cspell/dict-golang@^6.0.9": - version "6.0.9" - resolved "https://registry.yarnpkg.com/@cspell/dict-golang/-/dict-golang-6.0.9.tgz#b26ee13fb34a8cd40fb22380de8a46b25739fcab" - integrity sha512-etDt2WQauyEQDA+qPS5QtkYTb2I9l5IfQftAllVoB1aOrT6bxxpHvMEpJ0Hsn/vezxrCqa/BmtUbRxllIxIuSg== +"@cspell/dict-gaming-terms@^1.0.8": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@cspell/dict-gaming-terms/-/dict-gaming-terms-1.0.8.tgz#fb8a737f61e7cf560b4de7b2aaeae952f2550398" + integrity sha512-7OL0zTl93WFWhhtpXFrtm9uZXItC3ncAs8d0iQDMMFVNU1rBr6raBNxJskxE5wx2Ant12fgI66ZGVagXfN+yfA== -"@cspell/dict-google@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@cspell/dict-google/-/dict-google-1.0.1.tgz#34701471a616011aeaaf480d4834436b6b6b1da5" - integrity sha512-dQr4M3n95uOhtloNSgB9tYYGXGGEGEykkFyRtfcp5pFuEecYUa0BSgtlGKx9RXVtJtKgR+yFT/a5uQSlt8WjqQ== +"@cspell/dict-git@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@cspell/dict-git/-/dict-git-3.0.3.tgz#3a3805ab9902bffc9255ec48f648145b957eb30b" + integrity sha512-LSxB+psZ0qoj83GkyjeEH/ZViyVsGEF/A6BAo8Nqc0w0HjD2qX/QR4sfA6JHUgQ3Yi/ccxdK7xNIo67L2ScW5A== -"@cspell/dict-haskell@^4.0.1": - version "4.0.1" - resolved "https://registry.npmjs.org/@cspell/dict-haskell/-/dict-haskell-4.0.1.tgz" - integrity sha512-uRrl65mGrOmwT7NxspB4xKXFUenNC7IikmpRZW8Uzqbqcu7ZRCUfstuVH7T1rmjRgRkjcIjE4PC11luDou4wEQ== +"@cspell/dict-golang@^6.0.16": + version "6.0.16" + resolved "https://registry.yarnpkg.com/@cspell/dict-golang/-/dict-golang-6.0.16.tgz#b247a801404f9a65e7c8674893bdb5aad42353a2" + integrity sha512-hZOBlgcguv2Hdc93n2zjdAQm1j3grsN9T9WhPnQ1wh2vUDoCLEujg+6gWhjcLb8ECOcwZTWgNyQLWeOxEsAj/w== -"@cspell/dict-html-symbol-entities@^4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@cspell/dict-html-symbol-entities/-/dict-html-symbol-entities-4.0.0.tgz" - integrity sha512-HGRu+48ErJjoweR5IbcixxETRewrBb0uxQBd6xFGcxbEYCX8CnQFTAmKI5xNaIt2PKaZiJH3ijodGSqbKdsxhw== +"@cspell/dict-google@^1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@cspell/dict-google/-/dict-google-1.0.4.tgz#e15a7ea2dee73800231a81840a59d3b50d49346f" + integrity sha512-JThUT9eiguCja1mHHLwYESgxkhk17Gv7P3b1S7ZJzXw86QyVHPrbpVoMpozHk0C9o+Ym764B7gZGKmw9uMGduQ== -"@cspell/dict-html@^4.0.5": - version "4.0.5" - resolved "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-4.0.5.tgz" - integrity sha512-p0brEnRybzSSWi8sGbuVEf7jSTDmXPx7XhQUb5bgG6b54uj+Z0Qf0V2n8b/LWwIPJNd1GygaO9l8k3HTCy1h4w== +"@cspell/dict-haskell@^4.0.4": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@cspell/dict-haskell/-/dict-haskell-4.0.4.tgz#37e9cb9a7f5be337a697bcffd0a0d25e80aab50d" + integrity sha512-EwQsedEEnND/vY6tqRfg9y7tsnZdxNqOxLXSXTsFA6JRhUlr8Qs88iUUAfsUzWc4nNmmzQH2UbtT25ooG9x4nA== -"@cspell/dict-java@^5.0.7": - version "5.0.7" - resolved "https://registry.yarnpkg.com/@cspell/dict-java/-/dict-java-5.0.7.tgz#c0b32d3c208b6419a5eddd010e87196976be2694" - integrity sha512-ejQ9iJXYIq7R09BScU2y5OUGrSqwcD+J5mHFOKbduuQ5s/Eh/duz45KOzykeMLI6KHPVxhBKpUPBWIsfewECpQ== +"@cspell/dict-html-symbol-entities@^4.0.3": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@cspell/dict-html-symbol-entities/-/dict-html-symbol-entities-4.0.3.tgz#bf2887020ca4774413d8b1f27c9b6824ba89e9ef" + integrity sha512-aABXX7dMLNFdSE8aY844X4+hvfK7977sOWgZXo4MTGAmOzR8524fjbJPswIBK7GaD3+SgFZ2yP2o0CFvXDGF+A== -"@cspell/dict-julia@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@cspell/dict-julia/-/dict-julia-1.0.1.tgz#900001417f1c4ea689530adfcc034c848458a0aa" - integrity sha512-4JsCLCRhhLMLiaHpmR7zHFjj1qOauzDI5ZzCNQS31TUMfsOo26jAKDfo0jljFAKgw5M2fEG7sKr8IlPpQAYrmQ== +"@cspell/dict-html@^4.0.10": + version "4.0.10" + resolved "https://registry.yarnpkg.com/@cspell/dict-html/-/dict-html-4.0.10.tgz#7b536b2adca4b58ed92752c9d3c7ffc724dd5991" + integrity sha512-I9uRAcdtHbh0wEtYZlgF0TTcgH0xaw1B54G2CW+tx4vHUwlde/+JBOfIzird4+WcMv4smZOfw+qHf7puFUbI5g== -"@cspell/dict-k8s@^1.0.6": - version "1.0.6" - resolved "https://registry.yarnpkg.com/@cspell/dict-k8s/-/dict-k8s-1.0.6.tgz#d46c97136f1504b65dfb6a188005d4ac81d3f461" - integrity sha512-srhVDtwrd799uxMpsPOQqeDJY+gEocgZpoK06EFrb4GRYGhv7lXo9Fb+xQMyQytzOW9dw4DNOEck++nacDuymg== +"@cspell/dict-java@^5.0.10": + version "5.0.10" + resolved "https://registry.yarnpkg.com/@cspell/dict-java/-/dict-java-5.0.10.tgz#e6383ca645046b9f05a04a2c2e858fcc80c6fc63" + integrity sha512-pVNcOnmoGiNL8GSVq4WbX/Vs2FGS0Nej+1aEeGuUY9CU14X8yAVCG+oih5ZoLt1jaR8YfR8byUF8wdp4qG4XIw== -"@cspell/dict-latex@^4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@cspell/dict-latex/-/dict-latex-4.0.0.tgz" - integrity sha512-LPY4y6D5oI7D3d+5JMJHK/wxYTQa2lJMSNxps2JtuF8hbAnBQb3igoWEjEbIbRRH1XBM0X8dQqemnjQNCiAtxQ== +"@cspell/dict-julia@^1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@cspell/dict-julia/-/dict-julia-1.0.4.tgz#e478c20d742cd6857b6de41dc61a92036dafb4bc" + integrity sha512-bFVgNX35MD3kZRbXbJVzdnN7OuEqmQXGpdOi9jzB40TSgBTlJWA4nxeAKV4CPCZxNRUGnLH0p05T/AD7Aom9/w== -"@cspell/dict-lorem-ipsum@^4.0.0": - version "4.0.0" - resolved "https://registry.npmjs.org/@cspell/dict-lorem-ipsum/-/dict-lorem-ipsum-4.0.0.tgz" - integrity sha512-1l3yjfNvMzZPibW8A7mQU4kTozwVZVw0AvFEdy+NcqtbxH+TvbSkNMqROOFWrkD2PjnKG0+Ea0tHI2Pi6Gchnw== +"@cspell/dict-k8s@^1.0.9": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@cspell/dict-k8s/-/dict-k8s-1.0.9.tgz#e9392a002797c67ffc3e96893156cc15af3774d1" + integrity sha512-Q7GELSQIzo+BERl2ya/nBEnZeQC+zJP19SN1pI6gqDYraM51uYJacbbcWLYYO2Y+5joDjNt/sd/lJtLaQwoSlA== -"@cspell/dict-lua@^4.0.3": +"@cspell/dict-latex@^4.0.3": version "4.0.3" - resolved "https://registry.npmjs.org/@cspell/dict-lua/-/dict-lua-4.0.3.tgz" - integrity sha512-lDHKjsrrbqPaea13+G9s0rtXjMO06gPXPYRjRYawbNmo4E/e3XFfVzeci3OQDQNDmf2cPOwt9Ef5lu2lDmwfJg== + resolved "https://registry.yarnpkg.com/@cspell/dict-latex/-/dict-latex-4.0.3.tgz#a1254c7d9c3a2d70cd6391a9f2f7694431b1b2cb" + integrity sha512-2KXBt9fSpymYHxHfvhUpjUFyzrmN4c4P8mwIzweLyvqntBT3k0YGZJSriOdjfUjwSygrfEwiuPI1EMrvgrOMJw== -"@cspell/dict-makefile@^1.0.0": - version "1.0.0" - resolved "https://registry.npmjs.org/@cspell/dict-makefile/-/dict-makefile-1.0.0.tgz" - integrity sha512-3W9tHPcSbJa6s0bcqWo6VisEDTSN5zOtDbnPabF7rbyjRpNo0uHXHRJQF8gAbFzoTzBBhgkTmrfSiuyQm7vBUQ== - -"@cspell/dict-monkeyc@^1.0.6": - version "1.0.6" - resolved "https://registry.yarnpkg.com/@cspell/dict-monkeyc/-/dict-monkeyc-1.0.6.tgz#042d042fc34a20194c8de032130808f44b241375" - integrity sha512-oO8ZDu/FtZ55aq9Mb67HtaCnsLn59xvhO/t2mLLTHAp667hJFxpp7bCtr2zOrR1NELzFXmKln/2lw/PvxMSvrA== - -"@cspell/dict-node@^5.0.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@cspell/dict-node/-/dict-node-5.0.1.tgz#77e17c576a897a3391fce01c1cc5da60bb4c2268" - integrity sha512-lax/jGz9h3Dv83v8LHa5G0bf6wm8YVRMzbjJPG/9rp7cAGPtdrga+XANFq+B7bY5+jiSA3zvj10LUFCFjnnCCg== - -"@cspell/dict-npm@^5.0.18": - version "5.0.18" - resolved "https://registry.yarnpkg.com/@cspell/dict-npm/-/dict-npm-5.0.18.tgz#7ec5640c97bd25a64de0c9e74eb19dda86fba025" - integrity sha512-weMTyxWpzz19q4wv9n183BtFvdD5fCjtze+bFKpl+4rO/YlPhHL2cXLAeexJz/VDSBecwX4ybTZYoknd1h2J4w== +"@cspell/dict-lorem-ipsum@^4.0.3": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@cspell/dict-lorem-ipsum/-/dict-lorem-ipsum-4.0.3.tgz#c5fc631d934f1daf8b10c88b795278701a2469ec" + integrity sha512-WFpDi/PDYHXft6p0eCXuYnn7mzMEQLVeqpO+wHSUd+kz5ADusZ4cpslAA4wUZJstF1/1kMCQCZM6HLZic9bT8A== -"@cspell/dict-php@^4.0.8": - version "4.0.8" - resolved "https://registry.yarnpkg.com/@cspell/dict-php/-/dict-php-4.0.8.tgz#fedce3109dff13a0f3d8d88ba604d6edd2b9fb70" - integrity sha512-TBw3won4MCBQ2wdu7kvgOCR3dY2Tb+LJHgDUpuquy3WnzGiSDJ4AVelrZdE1xu7mjFJUr4q48aB21YT5uQqPZA== +"@cspell/dict-lua@^4.0.6": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@cspell/dict-lua/-/dict-lua-4.0.6.tgz#7de412bfaead794445e26d566aec222e20ad69ba" + integrity sha512-Jwvh1jmAd9b+SP9e1GkS2ACbqKKRo9E1f9GdjF/ijmooZuHU0hPyqvnhZzUAxO1egbnNjxS/J2T6iUtjAUK2KQ== -"@cspell/dict-powershell@^5.0.5": - version "5.0.5" - resolved "https://registry.yarnpkg.com/@cspell/dict-powershell/-/dict-powershell-5.0.5.tgz#3319d2fbad740e164a78386d711668bfe335c1f2" - integrity sha512-3JVyvMoDJesAATYGOxcUWPbQPUvpZmkinV3m8HL1w1RrjeMVXXuK7U1jhopSneBtLhkU+9HKFwgh9l9xL9mY2Q== +"@cspell/dict-makefile@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@cspell/dict-makefile/-/dict-makefile-1.0.3.tgz#08d3349bf7cbd8f5dacf8641f3d35092ca0b8b38" + integrity sha512-R3U0DSpvTs6qdqfyBATnePj9Q/pypkje0Nj26mQJ8TOBQutCRAJbr2ZFAeDjgRx5EAJU/+8txiyVF97fbVRViw== -"@cspell/dict-public-licenses@^2.0.7": +"@cspell/dict-markdown@^2.0.7": version "2.0.7" - resolved "https://registry.yarnpkg.com/@cspell/dict-public-licenses/-/dict-public-licenses-2.0.7.tgz#ccd67a91a6bd5ed4b5117c2f34e9361accebfcb7" - integrity sha512-KlBXuGcN3LE7tQi/GEqKiDewWGGuopiAD0zRK1QilOx5Co8XAvs044gk4MNIQftc8r0nHeUI+irJKLGcR36DIQ== - -"@cspell/dict-python@^4.2.4": - version "4.2.4" - resolved "https://registry.yarnpkg.com/@cspell/dict-python/-/dict-python-4.2.4.tgz#add81749939c6f123dbd0662385153506be951e0" - integrity sha512-sCtLBqMreb+8zRW2bXvFsfSnRUVU6IFm4mT6Dc4xbz0YajprbaPPh/kOUTw5IJRP8Uh+FFb7Xp2iH03CNWRq/A== - dependencies: - "@cspell/dict-data-science" "^2.0.1" - -"@cspell/dict-r@^2.0.1": - version "2.0.1" - resolved "https://registry.npmjs.org/@cspell/dict-r/-/dict-r-2.0.1.tgz" - integrity sha512-KCmKaeYMLm2Ip79mlYPc8p+B2uzwBp4KMkzeLd5E6jUlCL93Y5Nvq68wV5fRLDRTf7N1LvofkVFWfDcednFOgA== - -"@cspell/dict-ruby@^5.0.2": - version "5.0.2" - resolved "https://registry.npmjs.org/@cspell/dict-ruby/-/dict-ruby-5.0.2.tgz" - integrity sha512-cIh8KTjpldzFzKGgrqUX4bFyav5lC52hXDKo4LbRuMVncs3zg4hcSf4HtURY+f2AfEZzN6ZKzXafQpThq3dl2g== - -"@cspell/dict-rust@^4.0.5": - version "4.0.5" - resolved "https://registry.yarnpkg.com/@cspell/dict-rust/-/dict-rust-4.0.5.tgz#41f3e26fdd3d121c3a24c122d4a703abbb48c4c3" - integrity sha512-DIvlPRDemjKQy8rCqftAgGNZxY5Bg+Ps7qAIJjxkSjmMETyDgl0KTVuaJPt7EK4jJt6uCZ4ILy96npsHDPwoXA== + resolved "https://registry.yarnpkg.com/@cspell/dict-markdown/-/dict-markdown-2.0.7.tgz#15d6f9eed6bd1b33921b4332426ff387961163f1" + integrity sha512-F9SGsSOokFn976DV4u/1eL4FtKQDSgJHSZ3+haPRU5ki6OEqojxKa8hhj4AUrtNFpmBaJx/WJ4YaEzWqG7hgqg== -"@cspell/dict-scala@^5.0.3": - version "5.0.3" - resolved "https://registry.yarnpkg.com/@cspell/dict-scala/-/dict-scala-5.0.3.tgz#85a469b2d139766b6307befc89243928e3d82b39" - integrity sha512-4yGb4AInT99rqprxVNT9TYb1YSpq58Owzq7zi3ZS5T0u899Y4VsxsBiOgHnQ/4W+ygi+sp+oqef8w8nABR2lkg== - -"@cspell/dict-software-terms@^4.0.6": - version "4.0.6" - resolved "https://registry.yarnpkg.com/@cspell/dict-software-terms/-/dict-software-terms-4.0.6.tgz#df1efb809a50ae4292f85a90d1481dafcc13b414" - integrity sha512-UDhUzNSf7GN529a0Ip9hlSoGbpscz0YlUYBEJmZBXi8otpkrbCJqs50T74Ppd+SWqNil04De8urv4af2c6SY5Q== - -"@cspell/dict-sql@^2.1.5": - version "2.1.5" - resolved "https://registry.yarnpkg.com/@cspell/dict-sql/-/dict-sql-2.1.5.tgz#068c7a8840d75418fd46a0b062c0ed2d5742f2b8" - integrity sha512-FmxanytHXss7GAWAXmgaxl3icTCW7YxlimyOSPNfm+njqeUDjw3kEv4mFNDDObBJv8Ec5AWCbUDkWIpkE3IpKg== +"@cspell/dict-monkeyc@^1.0.9": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@cspell/dict-monkeyc/-/dict-monkeyc-1.0.9.tgz#58b5f6f15fc7c11ce0eeffd0742fba4b39fc0b8b" + integrity sha512-Jvf6g5xlB4+za3ThvenYKREXTEgzx5gMUSzrAxIiPleVG4hmRb/GBSoSjtkGaibN3XxGx5x809gSTYCA/IHCpA== + +"@cspell/dict-node@^5.0.4": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@cspell/dict-node/-/dict-node-5.0.4.tgz#dfe1f159a1ffb1c4f389ec43b15f705123113658" + integrity sha512-Hz5hiuOvZTd7Cp1IBqUZ7/ChwJeQpD5BJuwCaDn4mPNq4iMcQ1iWBYMThvNVqCEDgKv63X52nT8RAWacss98qg== + +"@cspell/dict-npm@^5.1.9": + version "5.1.9" + resolved "https://registry.yarnpkg.com/@cspell/dict-npm/-/dict-npm-5.1.9.tgz#3638b7b5d068e93c884d827fa704a17fdf8bef81" + integrity sha512-ZH10gepefFee03SYQXH4mMmYbI3iwwEg5+B0ujsUohGJR789SyieelY0xPiDeh1y9gcQAp62k1QxbUwpcCYe5A== + +"@cspell/dict-php@^4.0.13": + version "4.0.13" + resolved "https://registry.yarnpkg.com/@cspell/dict-php/-/dict-php-4.0.13.tgz#86f1e6fb2174b2b0fa012baf86c448b2730f04f9" + integrity sha512-P6sREMZkhElzz/HhXAjahnICYIqB/HSGp1EhZh+Y6IhvC15AzgtDP8B8VYCIsQof6rPF1SQrFwunxOv8H1e2eg== + +"@cspell/dict-powershell@^5.0.13": + version "5.0.13" + resolved "https://registry.yarnpkg.com/@cspell/dict-powershell/-/dict-powershell-5.0.13.tgz#f557aa04ee9bda4fe091308a0bcaea09ed12fa76" + integrity sha512-0qdj0XZIPmb77nRTynKidRJKTU0Fl+10jyLbAhFTuBWKMypVY06EaYFnwhsgsws/7nNX8MTEQuewbl9bWFAbsg== + +"@cspell/dict-public-licenses@^2.0.11": + version "2.0.11" + resolved "https://registry.yarnpkg.com/@cspell/dict-public-licenses/-/dict-public-licenses-2.0.11.tgz#37550c4e0cd445991caba528bf4ba58ce7a935c3" + integrity sha512-rR5KjRUSnVKdfs5G+gJ4oIvQvm8+NJ6cHWY2N+GE69/FSGWDOPHxulCzeGnQU/c6WWZMSimG9o49i9r//lUQyA== + +"@cspell/dict-python@^4.2.12": + version "4.2.12" + resolved "https://registry.yarnpkg.com/@cspell/dict-python/-/dict-python-4.2.12.tgz#ea6298bb72a6bcf2c188d5c55142e0afab8a6c1c" + integrity sha512-U25eOFu+RE0aEcF2AsxZmq3Lic7y9zspJ9SzjrC0mfJz+yr3YmSCw4E0blMD3mZoNcf7H/vMshuKIY5AY36U+Q== + dependencies: + "@cspell/dict-data-science" "^2.0.5" + +"@cspell/dict-r@^2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@cspell/dict-r/-/dict-r-2.0.4.tgz#31b5abd91cc12aebfffdde4be4d2902668789311" + integrity sha512-cBpRsE/U0d9BRhiNRMLMH1PpWgw+N+1A2jumgt1if9nBGmQw4MUpg2u9I0xlFVhstTIdzXiLXMxP45cABuiUeQ== -"@cspell/dict-svelte@^1.0.2": - version "1.0.2" - resolved "https://registry.npmjs.org/@cspell/dict-svelte/-/dict-svelte-1.0.2.tgz" - integrity sha512-rPJmnn/GsDs0btNvrRBciOhngKV98yZ9SHmg8qI6HLS8hZKvcXc0LMsf9LLuMK1TmS2+WQFAan6qeqg6bBxL2Q== +"@cspell/dict-ruby@^5.0.7": + version "5.0.7" + resolved "https://registry.yarnpkg.com/@cspell/dict-ruby/-/dict-ruby-5.0.7.tgz#3593a955baaffe3c5d28fb178b72fdf93c7eec71" + integrity sha512-4/d0hcoPzi5Alk0FmcyqlzFW9lQnZh9j07MJzPcyVO62nYJJAGKaPZL2o4qHeCS/od/ctJC5AHRdoUm0ktsw6Q== + +"@cspell/dict-rust@^4.0.9": + version "4.0.9" + resolved "https://registry.yarnpkg.com/@cspell/dict-rust/-/dict-rust-4.0.9.tgz#8af5e405f3280afffe41f212da3ae0e777243842" + integrity sha512-Dhr6TIZsMV92xcikKIWei6p/qswS4M+gTkivpWwz4/1oaVk2nRrxJmCdRoVkJlZkkAc17rjxrS12mpnJZI0iWw== + +"@cspell/dict-scala@^5.0.6": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@cspell/dict-scala/-/dict-scala-5.0.6.tgz#5e925def2fe6dc27ee2ad1c452941c3d6790fb6d" + integrity sha512-tl0YWAfjUVb4LyyE4JIMVE8DlLzb1ecHRmIWc4eT6nkyDqQgHKzdHsnusxFEFMVLIQomgSg0Zz6hJ5S1E4W4ww== + +"@cspell/dict-software-terms@^4.1.12": + version "4.1.12" + resolved "https://registry.yarnpkg.com/@cspell/dict-software-terms/-/dict-software-terms-4.1.12.tgz#ea6e6afde34e73fd233aecfe450f4081345e0d10" + integrity sha512-MHDAK/WlEdMJiDQ6lJ3SF7VogdfJcRXGYWfO4v90rxW8HDVfKDXVk3zpJhjoZGq56ujR1qmeYkQsM6MlB69uJA== + +"@cspell/dict-sql@^2.1.8": + version "2.1.8" + resolved "https://registry.yarnpkg.com/@cspell/dict-sql/-/dict-sql-2.1.8.tgz#45ea53b3e57fd2cc5f839f49b644aa743dac4990" + integrity sha512-dJRE4JV1qmXTbbGm6WIcg1knmR6K5RXnQxF4XHs5HA3LAjc/zf77F95i5LC+guOGppVF6Hdl66S2UyxT+SAF3A== + +"@cspell/dict-svelte@^1.0.5": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@cspell/dict-svelte/-/dict-svelte-1.0.5.tgz#09752e01ff6667e737566d9dfc704c8dcc9a6492" + integrity sha512-sseHlcXOqWE4Ner9sg8KsjxwSJ2yssoJNqFHR9liWVbDV+m7kBiUtn2EB690TihzVsEmDr/0Yxrbb5Bniz70mA== -"@cspell/dict-swift@^2.0.1": - version "2.0.1" - resolved "https://registry.npmjs.org/@cspell/dict-swift/-/dict-swift-2.0.1.tgz" - integrity sha512-gxrCMUOndOk7xZFmXNtkCEeroZRnS2VbeaIPiymGRHj5H+qfTAzAKxtv7jJbVA3YYvEzWcVE2oKDP4wcbhIERw== +"@cspell/dict-swift@^2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@cspell/dict-swift/-/dict-swift-2.0.4.tgz#bc19522418ed68cf914736b612c4e4febbf07e8d" + integrity sha512-CsFF0IFAbRtYNg0yZcdaYbADF5F3DsM8C4wHnZefQy8YcHP/qjAF/GdGfBFBLx+XSthYuBlo2b2XQVdz3cJZBw== -"@cspell/dict-terraform@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@cspell/dict-terraform/-/dict-terraform-1.0.0.tgz#c7b073bb3a03683f64cc70ccaa55ce9742c46086" - integrity sha512-Ak+vy4HP/bOgzf06BAMC30+ZvL9mzv21xLM2XtfnBLTDJGdxlk/nK0U6QT8VfFLqJ0ZZSpyOxGsUebWDCTr/zQ== +"@cspell/dict-terraform@^1.0.6": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@cspell/dict-terraform/-/dict-terraform-1.0.6.tgz#f67b7363d0cf08c820818980bbe8c927332ad0b8" + integrity sha512-Sqm5vGbXuI9hCFcr4w6xWf4Y25J9SdleE/IqfM6RySPnk8lISEmVdax4k6+Kinv9qaxyvnIbUUN4WFLWcBPQAg== -"@cspell/dict-typescript@^3.1.6": - version "3.1.6" - resolved "https://registry.yarnpkg.com/@cspell/dict-typescript/-/dict-typescript-3.1.6.tgz#2d5351786787bf3609da65ba17d9bc345995a36d" - integrity sha512-1beC6O4P/j23VuxX+i0+F7XqPVc3hhiAzGJHEKqnWf5cWAXQtg0xz3xQJ5MvYx2a7iLaSa+lu7+05vG9UHyu9Q== +"@cspell/dict-typescript@^3.1.11": + version "3.1.11" + resolved "https://registry.yarnpkg.com/@cspell/dict-typescript/-/dict-typescript-3.1.11.tgz#40586f13b0337bd9cba958e0661b35888580b249" + integrity sha512-FwvK5sKbwrVpdw0e9+1lVTl8FPoHYvfHRuQRQz2Ql5XkC0gwPPkpoyD1zYImjIyZRoYXk3yp9j8ss4iz7A7zoQ== -"@cspell/dict-vue@^3.0.0": - version "3.0.0" - resolved "https://registry.npmjs.org/@cspell/dict-vue/-/dict-vue-3.0.0.tgz" - integrity sha512-niiEMPWPV9IeRBRzZ0TBZmNnkK3olkOPYxC1Ny2AX4TGlYRajcW0WUtoSHmvvjZNfWLSg2L6ruiBeuPSbjnG6A== +"@cspell/dict-vue@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@cspell/dict-vue/-/dict-vue-3.0.3.tgz#295c288f6fd363879898223202ec3be048663b98" + integrity sha512-akmYbrgAGumqk1xXALtDJcEcOMYBYMnkjpmGzH13Ozhq1mkPF4VgllFQlm1xYde+BUKNnzMgPEzxrL2qZllgYA== -"@cspell/dynamic-import@8.13.3": - version "8.13.3" - resolved "https://registry.yarnpkg.com/@cspell/dynamic-import/-/dynamic-import-8.13.3.tgz#6809ae3c5f46766145ae7f615889a1e1686d59ac" - integrity sha512-YN83CFWnMkt9B0q0RBadfEoptUaDRqBikh8b91MOQ0haEnUo6t57j4jAaLnbIEP4ynzMhgruWFKpIC/QaEtCuA== +"@cspell/dynamic-import@8.15.6": + version "8.15.6" + resolved "https://registry.yarnpkg.com/@cspell/dynamic-import/-/dynamic-import-8.15.6.tgz#2f01d1b1848be5f79e35b3895add7cdf69a34297" + integrity sha512-lc19eM0DBJWFAYq6zgneCKIU9jM3qOY4YQ0owRFcHqlSM+AZjBvoe5AsmW6VuCuEkQ5o8CFW+Kow0oEsrEH72A== dependencies: import-meta-resolve "^4.1.0" -"@cspell/strong-weak-map@8.13.3": - version "8.13.3" - resolved "https://registry.yarnpkg.com/@cspell/strong-weak-map/-/strong-weak-map-8.13.3.tgz#069f5e95f70d884415feec1bf53a1c20854b33f9" - integrity sha512-/QYUEthesPuDarOHa6kcWKJmVq0HIotjPrmAWQ5QpH+dDik1Qin4G/9QdnWX75ueR4DC4WFjBNBU14C4TVSwHQ== +"@cspell/filetypes@8.15.6": + version "8.15.6" + resolved "https://registry.yarnpkg.com/@cspell/filetypes/-/filetypes-8.15.6.tgz#43935e0ff6f8d0182c6ef4049fb42b030f050a17" + integrity sha512-I+K31DjFMmHSBJoL7/kpokj2c3wz9BEz4A4l19j4x4sfKGLr0Oczyu5ULEm7neKU0cqO6pg/oFMr935jZSzv8A== + +"@cspell/strong-weak-map@8.15.6": + version "8.15.6" + resolved "https://registry.yarnpkg.com/@cspell/strong-weak-map/-/strong-weak-map-8.15.6.tgz#a81dc63334f6343c26b90a6844f6e50504679fba" + integrity sha512-WXVfboLWGk2fa5QzfiSdFxzhHvSN/cxUGWVkcJtKS0qtVY9gRq7zB/JGvPka2CUrWxzgLerE1ej4BPNERoEk3Q== -"@cspell/url@8.13.3": - version "8.13.3" - resolved "https://registry.yarnpkg.com/@cspell/url/-/url-8.13.3.tgz#2c400e581570fe2fb95197a2e6c64ceeeb6ceb8b" - integrity sha512-hsxoTnZHwtdR2x9QEE6yfDBB1LUwAj67o1GyKTvI8A2OE/AfzAttirZs+9sxgOGWoBdTOxM9sMLtqB3SxtDB3A== +"@cspell/url@8.15.6": + version "8.15.6" + resolved "https://registry.yarnpkg.com/@cspell/url/-/url-8.15.6.tgz#2eab9e5946fb040288a9228ad77c53e424ef0421" + integrity sha512-/O6iaINTu4bfusLupHd3kDHA3ZA50phIcyZDEjF33g4IFVAyNrQzHc40eY5UtApu2yZfuPrDM3nEGVokjdd2+A== "@cspotcode/source-map-support@^0.8.0": version "0.8.1" @@ -1660,20 +1718,25 @@ resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63" integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== -"@eslint-community/regexpp@^4.11.0": - version "4.11.0" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.0.tgz#b0ffd0312b4a3fd2d6f77237e7248a5ad3a680ae" - integrity sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A== +"@eslint-community/regexpp@^4.12.1": + version "4.12.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" + integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== -"@eslint/config-array@^0.17.1": - version "0.17.1" - resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.17.1.tgz#d9b8b8b6b946f47388f32bedfd3adf29ca8f8910" - integrity sha512-BlYOpej8AQ8Ev9xVqroV7a02JK3SkBAaN9GfMMH9W6Ch8FlQlkjGw4Ir7+FgYwfirivAf4t+GtzuAxqfukmISA== +"@eslint/config-array@^0.18.0": + version "0.18.0" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.18.0.tgz#37d8fe656e0d5e3dbaea7758ea56540867fd074d" + integrity sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw== dependencies: "@eslint/object-schema" "^2.1.4" debug "^4.3.1" minimatch "^3.1.2" +"@eslint/core@^0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.7.0.tgz#a1bb4b6a4e742a5ff1894b7ee76fbf884ec72bd3" + integrity sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw== + "@eslint/eslintrc@^3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.1.0.tgz#dbd3482bfd91efa663cbe7aa1f506839868207b6" @@ -1689,76 +1752,45 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@9.9.0", "@eslint/js@^9.9.0": - version "9.9.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.9.0.tgz#d8437adda50b3ed4401964517b64b4f59b0e2638" - integrity sha512-hhetes6ZHP3BlXLxmd8K2SNgkhNSi+UcecbnwWKwpP7kyi/uC75DJ1lOOBO3xrC4jyojtGE3YxKZPHfk4yrgug== +"@eslint/js@9.14.0", "@eslint/js@^9.14.0": + version "9.14.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.14.0.tgz#2347a871042ebd11a00fd8c2d3d56a265ee6857e" + integrity sha512-pFoEtFWCPyDOl+C6Ift+wC7Ro89otjigCf5vcuWqWgqNSQbRrpjSvdeE6ofLz4dHmyxD5f7gIdGT4+p36L6Twg== "@eslint/object-schema@^2.1.4": version "2.1.4" resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.4.tgz#9e69f8bb4031e11df79e03db09f9dbbae1740843" integrity sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ== -"@faker-js/faker@^8.4.1": - version "8.4.1" - resolved "https://registry.npmjs.org/@faker-js/faker/-/faker-8.4.1.tgz" - integrity sha512-XQ3cU+Q8Uqmrbf2e0cIC/QN43sTBSC8KF12u29Mb47tWrt2hAgBXSgpZMj4Ao8Uk0iJcU99QsOCaIL8934obCg== +"@eslint/plugin-kit@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.0.tgz#8712dccae365d24e9eeecb7b346f85e750ba343d" + integrity sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig== + dependencies: + levn "^0.4.1" -"@golevelup/ts-jest@^0.5.2": - version "0.5.2" - resolved "https://registry.yarnpkg.com/@golevelup/ts-jest/-/ts-jest-0.5.2.tgz#39ebe5d0f50b7838b4985aebccecc737832aa1a2" - integrity sha512-4uJl1RYETwBoKrxVxA+BOrkS0ex4W+XnUKvfccSZpt2sLS6GyYrVl9x+C3tFd6tne7bG34ppzrWd2AT6+D5IqA== +"@faker-js/faker@^9.1.0": + version "9.1.0" + resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-9.1.0.tgz#5d7957df87e2fb0eee5dcfd311ba83b34ec8eead" + integrity sha512-GJvX9iM9PBtKScJVlXQ0tWpihK3i0pha/XAhzQa1hPK/ILLa1Wq3I63Ij7lRtqTwmdTxRCyrUhLC5Sly9SLbug== -"@graphql-tools/merge@9.0.0": - version "9.0.0" - resolved "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.0.0.tgz" - integrity sha512-J7/xqjkGTTwOJmaJQJ2C+VDBDOWJL3lKrHJN4yMaRLAJH3PosB7GiPRaSDZdErs0+F77sH2MKs2haMMkywzx7Q== - dependencies: - "@graphql-tools/utils" "^10.0.0" - tslib "^2.4.0" +"@golevelup/ts-jest@^0.5.6": + version "0.5.6" + resolved "https://registry.yarnpkg.com/@golevelup/ts-jest/-/ts-jest-0.5.6.tgz#e63e3d746417de07cbd5d45208de380cd185346a" + integrity sha512-QnxP42Qu9M2UogdrF2kxpZcgWeW9R3WoUr+LdpcsbkvX3LjEoDYgrJ/PnV/QUCbxB1gaKbhR0ZxlDxE1cPkpDg== -"@graphql-tools/merge@^9.0.0": - version "9.0.1" - resolved "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.0.1.tgz" - integrity sha512-hIEExWO9fjA6vzsVjJ3s0cCQ+Q/BEeMVJZtMXd7nbaVefVy0YDyYlEkeoYYNV3NVVvu1G9lr6DM1Qd0DGo9Caw== - dependencies: - "@graphql-tools/utils" "^10.0.10" - tslib "^2.4.0" +"@humanfs/core@^0.19.1": + version "0.19.1" + resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77" + integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== -"@graphql-tools/schema@10.0.0": - version "10.0.0" - resolved "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.0.tgz" - integrity sha512-kf3qOXMFcMs2f/S8Y3A8fm/2w+GaHAkfr3Gnhh2LOug/JgpY/ywgFVxO3jOeSpSEdoYcDKLcXVjMigNbY4AdQg== - dependencies: - "@graphql-tools/merge" "^9.0.0" - "@graphql-tools/utils" "^10.0.0" - tslib "^2.4.0" - value-or-promise "^1.0.12" - -"@graphql-tools/utils@10.0.8": - version "10.0.8" - resolved "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.0.8.tgz" - integrity sha512-yjyA8ycSa1WRlJqyX/aLqXeE5DvF/H02+zXMUFnCzIDrj0UvLMUrxhmVFnMK0Q2n3bh4uuTeY3621m5za9ovXw== - dependencies: - "@graphql-typed-document-node/core" "^3.1.1" - cross-inspect "1.0.0" - dset "^3.1.2" - tslib "^2.4.0" - -"@graphql-tools/utils@^10.0.0", "@graphql-tools/utils@^10.0.10": - version "10.0.13" - resolved "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.0.13.tgz" - integrity sha512-fMILwGr5Dm2zefNItjQ6C2rauigklv69LIwppccICuGTnGaOp3DspLt/6Lxj72cbg5d9z60Sr+Egco3CJKLsNg== - dependencies: - "@graphql-typed-document-node/core" "^3.1.1" - cross-inspect "1.0.0" - dset "^3.1.2" - tslib "^2.4.0" - -"@graphql-typed-document-node/core@^3.1.1": - version "3.2.0" - resolved "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz" - integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ== +"@humanfs/node@^0.16.6": + version "0.16.6" + resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.6.tgz#ee2a10eaabd1131987bf0488fd9b820174cd765e" + integrity sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw== + dependencies: + "@humanfs/core" "^0.19.1" + "@humanwhocodes/retry" "^0.3.0" "@humanwhocodes/module-importer@^1.0.1": version "1.0.1" @@ -1770,6 +1802,11 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.0.tgz#6d86b8cb322660f03d3f0aa94b99bdd8e172d570" integrity sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew== +"@humanwhocodes/retry@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.0.tgz#b57438cab2a2381b4b597b0ab17339be381bd755" + integrity sha512-xnRgu9DxZbkWak/te3fcytNyp8MTbuiZIaueg2rgEvBuN55n04nwLYLU9TX/VVlusc9L2ZNXi99nUFNkHXtr5g== + "@ioredis/commands@^1.1.1": version "1.2.0" resolved "https://registry.yarnpkg.com/@ioredis/commands/-/commands-1.2.0.tgz#6d61b3097470af1fdbbe622795b8921d42018e11" @@ -1849,6 +1886,13 @@ slash "^3.0.0" strip-ansi "^6.0.0" +"@jest/create-cache-key-function@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz#793be38148fab78e65f40ae30c36785f4ad859f0" + integrity sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA== + dependencies: + "@jest/types" "^29.6.3" + "@jest/environment@^29.7.0": version "29.7.0" resolved "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz" @@ -2154,6 +2198,20 @@ resolved "https://registry.yarnpkg.com/@microsoft/tsdoc/-/tsdoc-0.15.0.tgz#f29a55df17cb6e87cfbabce33ff6a14a9f85076d" integrity sha512-HZpPoABogPvjeJOdzCOSJsXeL/SMCBgBZMVC3X3d7YYp2gf31MfxhUoYUNwf1ERPJOnQc0wkFn9trqI6ZEdZuA== +"@mole-inc/bin-wrapper@^8.0.1": + version "8.0.1" + resolved "https://registry.yarnpkg.com/@mole-inc/bin-wrapper/-/bin-wrapper-8.0.1.tgz#d7fd0ceb1cfa8a855293a3ed9d7d135f4d442f0e" + integrity sha512-sTGoeZnjI8N4KS+sW2AN95gDBErhAguvkw/tWdCjeM8bvxpz5lqrnd0vOJABA1A+Ic3zED7PYoLP/RANLgVotA== + dependencies: + bin-check "^4.1.0" + bin-version-check "^5.0.0" + content-disposition "^0.5.4" + ext-name "^5.0.0" + file-type "^17.1.6" + filenamify "^5.0.2" + got "^11.8.5" + os-filter-obj "^2.0.0" + "@mongodb-js/saslprep@^1.1.5": version "1.1.5" resolved "https://registry.yarnpkg.com/@mongodb-js/saslprep/-/saslprep-1.1.5.tgz#0c48a96c8d799e81fae311b7251aa5c1dc7c6e95" @@ -2191,35 +2249,137 @@ resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz#0aa5502d547b57abfc4ac492de68e2006e417242" integrity sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ== -"@nestjs/axios@^3.0.2": - version "3.0.2" - resolved "https://registry.npmjs.org/@nestjs/axios/-/axios-3.0.2.tgz" - integrity sha512-Z6GuOUdNQjP7FX+OuV2Ybyamse+/e0BFdTWBX5JxpBDKA+YkdLynDgG6HTF04zy6e9zPa19UX0WA2VDoehwhXQ== +"@napi-rs/nice-android-arm-eabi@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@napi-rs/nice-android-arm-eabi/-/nice-android-arm-eabi-1.0.1.tgz#9a0cba12706ff56500df127d6f4caf28ddb94936" + integrity sha512-5qpvOu5IGwDo7MEKVqqyAxF90I6aLj4n07OzpARdgDRfz8UbBztTByBp0RC59r3J1Ij8uzYi6jI7r5Lws7nn6w== -"@nestjs/bull-shared@^10.2.0": - version "10.2.0" - resolved "https://registry.yarnpkg.com/@nestjs/bull-shared/-/bull-shared-10.2.0.tgz#a94a756424429e1c50662633805ced1cacd21f2f" - integrity sha512-cSi6CyPECHDFumnHWWfwLCnbc6hm5jXt7FqzJ0Id6EhGqdz5ja0FmgRwXoS4xoMA2RRjlxn2vGXr4YOaHBAeig== +"@napi-rs/nice-android-arm64@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@napi-rs/nice-android-arm64/-/nice-android-arm64-1.0.1.tgz#32fc32e9649bd759d2a39ad745e95766f6759d2f" + integrity sha512-GqvXL0P8fZ+mQqG1g0o4AO9hJjQaeYG84FRfZaYjyJtZZZcMjXW5TwkL8Y8UApheJgyE13TQ4YNUssQaTgTyvA== + +"@napi-rs/nice-darwin-arm64@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@napi-rs/nice-darwin-arm64/-/nice-darwin-arm64-1.0.1.tgz#d3c44c51b94b25a82d45803e2255891e833e787b" + integrity sha512-91k3HEqUl2fsrz/sKkuEkscj6EAj3/eZNCLqzD2AA0TtVbkQi8nqxZCZDMkfklULmxLkMxuUdKe7RvG/T6s2AA== + +"@napi-rs/nice-darwin-x64@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@napi-rs/nice-darwin-x64/-/nice-darwin-x64-1.0.1.tgz#f1b1365a8370c6a6957e90085a9b4873d0e6a957" + integrity sha512-jXnMleYSIR/+TAN/p5u+NkCA7yidgswx5ftqzXdD5wgy/hNR92oerTXHc0jrlBisbd7DpzoaGY4cFD7Sm5GlgQ== + +"@napi-rs/nice-freebsd-x64@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@napi-rs/nice-freebsd-x64/-/nice-freebsd-x64-1.0.1.tgz#4280f081efbe0b46c5165fdaea8b286e55a8f89e" + integrity sha512-j+iJ/ezONXRQsVIB/FJfwjeQXX7A2tf3gEXs4WUGFrJjpe/z2KB7sOv6zpkm08PofF36C9S7wTNuzHZ/Iiccfw== + +"@napi-rs/nice-linux-arm-gnueabihf@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@napi-rs/nice-linux-arm-gnueabihf/-/nice-linux-arm-gnueabihf-1.0.1.tgz#07aec23a9467ed35eb7602af5e63d42c5d7bd473" + integrity sha512-G8RgJ8FYXYkkSGQwywAUh84m946UTn6l03/vmEXBYNJxQJcD+I3B3k5jmjFG/OPiU8DfvxutOP8bi+F89MCV7Q== + +"@napi-rs/nice-linux-arm64-gnu@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@napi-rs/nice-linux-arm64-gnu/-/nice-linux-arm64-gnu-1.0.1.tgz#038a77134cc6df3c48059d5a5e199d6f50fb9a90" + integrity sha512-IMDak59/W5JSab1oZvmNbrms3mHqcreaCeClUjwlwDr0m3BoR09ZiN8cKFBzuSlXgRdZ4PNqCYNeGQv7YMTjuA== + +"@napi-rs/nice-linux-arm64-musl@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@napi-rs/nice-linux-arm64-musl/-/nice-linux-arm64-musl-1.0.1.tgz#715d0906582ba0cff025109f42e5b84ea68c2bcc" + integrity sha512-wG8fa2VKuWM4CfjOjjRX9YLIbysSVV1S3Kgm2Fnc67ap/soHBeYZa6AGMeR5BJAylYRjnoVOzV19Cmkco3QEPw== + +"@napi-rs/nice-linux-ppc64-gnu@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@napi-rs/nice-linux-ppc64-gnu/-/nice-linux-ppc64-gnu-1.0.1.tgz#ac1c8f781c67b0559fa7a1cd4ae3ca2299dc3d06" + integrity sha512-lxQ9WrBf0IlNTCA9oS2jg/iAjQyTI6JHzABV664LLrLA/SIdD+I1i3Mjf7TsnoUbgopBcCuDztVLfJ0q9ubf6Q== + +"@napi-rs/nice-linux-riscv64-gnu@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@napi-rs/nice-linux-riscv64-gnu/-/nice-linux-riscv64-gnu-1.0.1.tgz#b0a430549acfd3920ffd28ce544e2fe17833d263" + integrity sha512-3xs69dO8WSWBb13KBVex+yvxmUeEsdWexxibqskzoKaWx9AIqkMbWmE2npkazJoopPKX2ULKd8Fm9veEn0g4Ig== + +"@napi-rs/nice-linux-s390x-gnu@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@napi-rs/nice-linux-s390x-gnu/-/nice-linux-s390x-gnu-1.0.1.tgz#5b95caf411ad72a965885217db378c4d09733e97" + integrity sha512-lMFI3i9rlW7hgToyAzTaEybQYGbQHDrpRkg+1gJWEpH0PLAQoZ8jiY0IzakLfNWnVda1eTYYlxxFYzW8Rqczkg== + +"@napi-rs/nice-linux-x64-gnu@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@napi-rs/nice-linux-x64-gnu/-/nice-linux-x64-gnu-1.0.1.tgz#a98cdef517549f8c17a83f0236a69418a90e77b7" + integrity sha512-XQAJs7DRN2GpLN6Fb+ZdGFeYZDdGl2Fn3TmFlqEL5JorgWKrQGRUrpGKbgZ25UeZPILuTKJ+OowG2avN8mThBA== + +"@napi-rs/nice-linux-x64-musl@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@napi-rs/nice-linux-x64-musl/-/nice-linux-x64-musl-1.0.1.tgz#5e26843eafa940138aed437c870cca751c8a8957" + integrity sha512-/rodHpRSgiI9o1faq9SZOp/o2QkKQg7T+DK0R5AkbnI/YxvAIEHf2cngjYzLMQSQgUhxym+LFr+UGZx4vK4QdQ== + +"@napi-rs/nice-win32-arm64-msvc@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@napi-rs/nice-win32-arm64-msvc/-/nice-win32-arm64-msvc-1.0.1.tgz#bd62617d02f04aa30ab1e9081363856715f84cd8" + integrity sha512-rEcz9vZymaCB3OqEXoHnp9YViLct8ugF+6uO5McifTedjq4QMQs3DHz35xBEGhH3gJWEsXMUbzazkz5KNM5YUg== + +"@napi-rs/nice-win32-ia32-msvc@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@napi-rs/nice-win32-ia32-msvc/-/nice-win32-ia32-msvc-1.0.1.tgz#b8b7aad552a24836027473d9b9f16edaeabecf18" + integrity sha512-t7eBAyPUrWL8su3gDxw9xxxqNwZzAqKo0Szv3IjVQd1GpXXVkb6vBBQUuxfIYaXMzZLwlxRQ7uzM2vdUE9ULGw== + +"@napi-rs/nice-win32-x64-msvc@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@napi-rs/nice-win32-x64-msvc/-/nice-win32-x64-msvc-1.0.1.tgz#37d8718b8f722f49067713e9f1e85540c9a3dd09" + integrity sha512-JlF+uDcatt3St2ntBG8H02F1mM45i5SF9W+bIKiReVE6wiy3o16oBP/yxt+RZ+N6LbCImJXJ6bXNO2kn9AXicg== + +"@napi-rs/nice@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@napi-rs/nice/-/nice-1.0.1.tgz#483d3ff31e5661829a1efb4825591a135c3bfa7d" + integrity sha512-zM0mVWSXE0a0h9aKACLwKmD6nHcRiKrPpCfvaKqG1CqDEyjEawId0ocXxVzPMCAm6kkWr2P025msfxXEnt8UGQ== + optionalDependencies: + "@napi-rs/nice-android-arm-eabi" "1.0.1" + "@napi-rs/nice-android-arm64" "1.0.1" + "@napi-rs/nice-darwin-arm64" "1.0.1" + "@napi-rs/nice-darwin-x64" "1.0.1" + "@napi-rs/nice-freebsd-x64" "1.0.1" + "@napi-rs/nice-linux-arm-gnueabihf" "1.0.1" + "@napi-rs/nice-linux-arm64-gnu" "1.0.1" + "@napi-rs/nice-linux-arm64-musl" "1.0.1" + "@napi-rs/nice-linux-ppc64-gnu" "1.0.1" + "@napi-rs/nice-linux-riscv64-gnu" "1.0.1" + "@napi-rs/nice-linux-s390x-gnu" "1.0.1" + "@napi-rs/nice-linux-x64-gnu" "1.0.1" + "@napi-rs/nice-linux-x64-musl" "1.0.1" + "@napi-rs/nice-win32-arm64-msvc" "1.0.1" + "@napi-rs/nice-win32-ia32-msvc" "1.0.1" + "@napi-rs/nice-win32-x64-msvc" "1.0.1" + +"@nestjs/axios@^3.1.1": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@nestjs/axios/-/axios-3.1.1.tgz#565160180c81b580e4b192792fe506f5db9e1bcc" + integrity sha512-ySoxrzqX80P1q6LKLKGcgyBd2utg4gbC+4FsJNpXYvILorMlxss/ECNogD9EXLCE4JS5exVFD5ez0nK5hXcNTQ== + +"@nestjs/bull-shared@^10.2.2": + version "10.2.2" + resolved "https://registry.yarnpkg.com/@nestjs/bull-shared/-/bull-shared-10.2.2.tgz#bfa127b6e6e5f884d9570d5ecad7add1fcb173cf" + integrity sha512-bMIEILYYovQWfdz6fCSTgqb/zuKyGmNSc7guB56MiZVW84JloUHb8330nNh3VWaamJKGtUzawbEoG2VR3uVeOg== dependencies: - tslib "2.6.3" + tslib "2.8.0" -"@nestjs/bullmq@^10.2.0": - version "10.2.0" - resolved "https://registry.yarnpkg.com/@nestjs/bullmq/-/bullmq-10.2.0.tgz#f548a191bcbb6febcc3a50a6aaa7d81db09516c3" - integrity sha512-lHXWDocXh1Yl6unsUzGFEKmK02mu0DdI35cdBp3Fq/9D5V3oLuWjwAPFnTztedshIjlFmNW6x5mdaT5WZ0AV1Q== +"@nestjs/bullmq@^10.2.2": + version "10.2.2" + resolved "https://registry.yarnpkg.com/@nestjs/bullmq/-/bullmq-10.2.2.tgz#359a5ebba9ca7268f135c040db3de61579f5ccc5" + integrity sha512-1RXhR7+XK6uXaw9uNH5hP9bcW5Vzkpc4lX7t7sUC23N9XH2CMH6uUm0I14T5KkvMKkj0VXj0GY+Ulh3pCtdwbA== dependencies: - "@nestjs/bull-shared" "^10.2.0" - tslib "2.6.3" + "@nestjs/bull-shared" "^10.2.2" + tslib "2.8.0" -"@nestjs/cache-manager@^2.2.2": - version "2.2.2" - resolved "https://registry.yarnpkg.com/@nestjs/cache-manager/-/cache-manager-2.2.2.tgz#4b0e7c4112c7b8c2a869d64f998aaf8a1bf0040d" - integrity sha512-+n7rpU1QABeW2WV17Dl1vZCG3vWjJU1MaamWgZvbGxYE9EeCM0lVLfw3z7acgDTNwOy+K68xuQPoIMxD0bhjlA== +"@nestjs/cache-manager@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@nestjs/cache-manager/-/cache-manager-2.3.0.tgz#e318ba56856c89b34f158efe9ca749fe3886dfa9" + integrity sha512-pxeBp9w/s99HaW2+pezM1P3fLiWmUEnTUoUMLa9UYViCtjj0E0A19W/vaT5JFACCzFIeNrwH4/16jkpAhQ25Vw== -"@nestjs/cli@^10.4.4": - version "10.4.4" - resolved "https://registry.yarnpkg.com/@nestjs/cli/-/cli-10.4.4.tgz#6fed6fa3259417ea3dc0b62a3888e4aedbaa1642" - integrity sha512-WKERbSZJGof0+9XeeMmWnb/9FpNxogcB5eTJTHjc9no0ymdTw3jTzT+KZL9iC/hGqBpuomDLaNFCYbAOt29nBw== +"@nestjs/cli@^10.4.5": + version "10.4.5" + resolved "https://registry.yarnpkg.com/@nestjs/cli/-/cli-10.4.5.tgz#d6563b87e8ca1d0f256c19a7847dbcc96c76a88e" + integrity sha512-FP7Rh13u8aJbHe+zZ7hM0CC4785g9Pw4lz4r2TTgRtf0zTxSWMkJaPEwyjX8SK9oWK2GsYxl+fKpwVZNbmnj9A== dependencies: "@angular-devkit/core" "17.3.8" "@angular-devkit/schematics" "17.3.8" @@ -2238,58 +2398,38 @@ tsconfig-paths "4.2.0" tsconfig-paths-webpack-plugin "4.1.0" typescript "5.3.3" - webpack "5.93.0" + webpack "5.94.0" webpack-node-externals "3.0.0" -"@nestjs/common@^10.4.0": - version "10.4.0" - resolved "https://registry.yarnpkg.com/@nestjs/common/-/common-10.4.0.tgz#f0507aa6d079b696a090e233780eb764c64f1c71" - integrity sha512-cGQJBMypG1qf0h31dvIYSffr/8+JhFd7qScJ4mqgF5HKT69WveW14zQcxavXzXI/LOE4vUvCu3QBeqcRBIs/9A== +"@nestjs/common@^10.4.6": + version "10.4.6" + resolved "https://registry.yarnpkg.com/@nestjs/common/-/common-10.4.6.tgz#952e8fd0ceafeffcc4eaf47effd67fb395844ae0" + integrity sha512-KkezkZvU9poWaNq4L+lNvx+386hpOxPJkfXBBeSMrcqBOx8kVr36TGN2uYkF4Ta4zNu1KbCjmZbc0rhHSg296g== dependencies: uid "2.0.2" iterare "1.2.1" - tslib "2.6.3" + tslib "2.7.0" -"@nestjs/config@^3.2.3": - version "3.2.3" - resolved "https://registry.yarnpkg.com/@nestjs/config/-/config-3.2.3.tgz#569888a33ada50b0f182002015e152e054990016" - integrity sha512-p6yv/CvoBewJ72mBq4NXgOAi2rSQNWx3a+IMJLVKS2uiwFCOQQuiIatGwq6MRjXV3Jr+B41iUO8FIf4xBrZ4/w== +"@nestjs/config@^3.3.0": + version "3.3.0" + resolved "https://registry.yarnpkg.com/@nestjs/config/-/config-3.3.0.tgz#ddc520ba26a8453ee5e690e18fb7b35e9bac7974" + integrity sha512-pdGTp8m9d0ZCrjTpjkUbZx6gyf2IKf+7zlkrPNMsJzYZ4bFRRTpXrnj+556/5uiI6AfL5mMrJc2u7dB6bvM+VA== dependencies: dotenv "16.4.5" dotenv-expand "10.0.0" lodash "4.17.21" -"@nestjs/core@^10.4.0": - version "10.4.0" - resolved "https://registry.yarnpkg.com/@nestjs/core/-/core-10.4.0.tgz#f7b6e359c105ec54a861962f6085d494b72488cc" - integrity sha512-vNVJ0H8n3FyIxrFibgV2tRbjKsVm90u//kinE0m7s6ygv+KhnGMrQvWGX0kk9wbsZwRMW5JMpnBWDUS4wu4yPg== +"@nestjs/core@^10.4.6": + version "10.4.6" + resolved "https://registry.yarnpkg.com/@nestjs/core/-/core-10.4.6.tgz#797b381f12bd62d2e425897058fa219da4c3689d" + integrity sha512-zXVPxCNRfO6gAy0yvEDjUxE/8gfZICJFpsl2lZAUH31bPb6m+tXuhUq2mVCTEltyMYQ+DYtRe+fEYM2v152N1g== dependencies: uid "2.0.2" "@nuxtjs/opencollective" "0.3.2" fast-safe-stringify "2.1.1" iterare "1.2.1" - path-to-regexp "3.2.0" - tslib "2.6.3" - -"@nestjs/graphql@~12.0.11": - version "12.0.11" - resolved "https://registry.npmjs.org/@nestjs/graphql/-/graphql-12.0.11.tgz" - integrity sha512-iCyVs9+utCQt9ehMhUjQcEdjRN/MrcTBINd7P44O1fzGENuWMbt1Z8RCoZbeGi5iVPBY63HgYik+BnnICqmxZw== - dependencies: - "@graphql-tools/merge" "9.0.0" - "@graphql-tools/schema" "10.0.0" - "@graphql-tools/utils" "10.0.8" - "@nestjs/mapped-types" "2.0.2" - chokidar "3.5.3" - fast-glob "3.3.2" - graphql-tag "2.12.6" - graphql-ws "5.14.2" - lodash "4.17.21" - normalize-path "3.0.0" - subscriptions-transport-ws "0.11.0" - tslib "2.6.2" - uuid "9.0.1" - ws "8.14.2" + path-to-regexp "3.3.0" + tslib "2.7.0" "@nestjs/jwt@^10.2.0": version "10.2.0" @@ -2299,36 +2439,31 @@ "@types/jsonwebtoken" "9.0.5" jsonwebtoken "9.0.2" -"@nestjs/mapped-types@2.0.2": - version "2.0.2" - resolved "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.2.tgz" - integrity sha512-V0izw6tWs6fTp9+KiiPUbGHWALy563Frn8X6Bm87ANLRuE46iuBMD5acKBDP5lKL/75QFvrzSJT7HkCbB0jTpg== - "@nestjs/mapped-types@2.0.5": version "2.0.5" resolved "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.5.tgz" integrity sha512-bSJv4pd6EY99NX9CjBIyn4TVDoSit82DUZlL4I3bqNfy5Gt+gXTa86i3I/i0iIV9P4hntcGM5GyO+FhZAhxtyg== -"@nestjs/mongoose@^10.0.10": - version "10.0.10" - resolved "https://registry.yarnpkg.com/@nestjs/mongoose/-/mongoose-10.0.10.tgz#1206df06b6f744f62b0bd7502074c13f17177739" - integrity sha512-3Ff60ock8nwlAJC823TG91Qy+Qc6av+ddIb6n6wlFsTK0akDF/aTcagX8cF8uI8mWxCWjEwEsgv99vo6p0yJ+w== +"@nestjs/mongoose@^10.1.0": + version "10.1.0" + resolved "https://registry.yarnpkg.com/@nestjs/mongoose/-/mongoose-10.1.0.tgz#4785219cae6792341da4a1a26727ca39208ef08d" + integrity sha512-1ExAnZUfh2QffEaGjqYGgVPy/sYBQCVLCLqVgkcClKx/BCd0QNgND8MB70lwyobp3nm/+nbGQqBpu9F3/hgOCw== "@nestjs/passport@^10.0.3": version "10.0.3" resolved "https://registry.npmjs.org/@nestjs/passport/-/passport-10.0.3.tgz" integrity sha512-znJ9Y4S8ZDVY+j4doWAJ8EuuVO7SkQN3yOBmzxbGaXbvcSwFDAdGJ+OMCg52NdzIO4tQoN4pYKx8W6M0ArfFRQ== -"@nestjs/platform-express@^10.4.0": - version "10.4.0" - resolved "https://registry.yarnpkg.com/@nestjs/platform-express/-/platform-express-10.4.0.tgz#d08f1df6790a5a108676cb3bdd0a991388289276" - integrity sha512-DxrNsqywNVRs+4tmEXKNotumXEEGw+EvG2f9MyvDnHYU7tCZAT9ZsVnT6waM3lrjSmyjMaae8JuiMI8bnZj44g== +"@nestjs/platform-express@^10.4.6": + version "10.4.6" + resolved "https://registry.yarnpkg.com/@nestjs/platform-express/-/platform-express-10.4.6.tgz#6c39c522fa66036b4256714fea203fbeb49fc4de" + integrity sha512-HcyCpAKccAasrLSGRTGWv5BKRs0rwTIFOSsk6laNyqfqvgvYcJQAedarnm4jmaemtmSJ0PFI9PmtEZADd2ahCg== dependencies: - body-parser "1.20.2" + body-parser "1.20.3" cors "2.8.5" - express "4.19.2" + express "4.21.1" multer "1.4.4-lts.1" - tslib "2.6.3" + tslib "2.7.0" "@nestjs/schematics@^10.0.1": version "10.0.1" @@ -2341,27 +2476,27 @@ jsonc-parser "3.2.0" pluralize "8.0.0" -"@nestjs/schematics@^10.1.3": - version "10.1.3" - resolved "https://registry.yarnpkg.com/@nestjs/schematics/-/schematics-10.1.3.tgz#8bd80ab9fab6a02586524bd2c545b0ea787cf62c" - integrity sha512-aLJ4Nl/K/u6ZlgLa0NjKw5CuBOIgc6vudF42QvmGueu5FaMGM6IJrAuEvB5T2kr0PAfVwYmDFBBHCWdYhTw4Tg== +"@nestjs/schematics@^10.2.3": + version "10.2.3" + resolved "https://registry.yarnpkg.com/@nestjs/schematics/-/schematics-10.2.3.tgz#6053f43c5065b9e825cd08c4db1bf6bcbc9a6a62" + integrity sha512-4e8gxaCk7DhBxVUly2PjYL4xC2ifDFexCqq1/u4TtivLGXotVk0wHdYuPYe1tHTHuR1lsOkRbfOCpkdTnigLVg== dependencies: - "@angular-devkit/core" "17.3.8" - "@angular-devkit/schematics" "17.3.8" - comment-json "4.2.3" + "@angular-devkit/core" "17.3.11" + "@angular-devkit/schematics" "17.3.11" + comment-json "4.2.5" jsonc-parser "3.3.1" pluralize "8.0.0" -"@nestjs/swagger@^7.4.0": - version "7.4.0" - resolved "https://registry.yarnpkg.com/@nestjs/swagger/-/swagger-7.4.0.tgz#e61dbefdfc1d4011327a256896953c74e511c850" - integrity sha512-dCiwKkRxcR7dZs5jtrGspBAe/nqJd1AYzOBTzw9iCdbq3BGrLpwokelk6lFZPe4twpTsPQqzNKBwKzVbI6AR/g== +"@nestjs/swagger@^8.0.1": + version "8.0.1" + resolved "https://registry.yarnpkg.com/@nestjs/swagger/-/swagger-8.0.1.tgz#902ffd0fd9f3f32a4ad0fc7526a6df87fb5a87cd" + integrity sha512-kW0dlsZXXWQgSSJHvk0fzg6kHvLcJ6trpbfvj5UN8DWIyCdCS/MGNshDE3P82xxKcg/pLZH7z41qYpFiawkGvQ== dependencies: "@microsoft/tsdoc" "^0.15.0" "@nestjs/mapped-types" "2.0.5" js-yaml "4.1.0" lodash "4.17.21" - path-to-regexp "3.2.0" + path-to-regexp "3.3.0" swagger-ui-dist "5.17.14" "@nestjs/terminus@^10.2.3": @@ -2372,17 +2507,17 @@ boxen "5.1.2" check-disk-space "3.4.0" -"@nestjs/testing@^10.4.0": - version "10.4.0" - resolved "https://registry.yarnpkg.com/@nestjs/testing/-/testing-10.4.0.tgz#9902dbe557acfd460973ced4b380591d8c470fbf" - integrity sha512-oAQe3Yb4/JlHtsBcKmueEvPZDoONp7LsNwGnMAeyhoBLuPBXDhZnNgMY2UtT4FfNmudBQBKR/vq/fOQRax/4Hg== +"@nestjs/testing@^10.4.6": + version "10.4.6" + resolved "https://registry.yarnpkg.com/@nestjs/testing/-/testing-10.4.6.tgz#3797a40c0628788e381f299d3c72acac364ca4ef" + integrity sha512-aiDicKhlGibVGNYuew399H5qZZXaseOBT/BS+ERJxxCmco7ZdAqaujsNjSaSbTK9ojDPf27crLT0C4opjqJe3A== dependencies: - tslib "2.6.3" + tslib "2.7.0" -"@nestjs/throttler@^6.1.0": - version "6.1.0" - resolved "https://registry.yarnpkg.com/@nestjs/throttler/-/throttler-6.1.0.tgz#ce78f5d24078862809e94a55015e74285df2f53a" - integrity sha512-MzwameXplM8FhQiN79U2zEIZQNEM0BM9kbOigriFSzdNCEfyIoNKHhGFMfsTvMxP119Ydi0Zvkt3W2CQUSIH5Q== +"@nestjs/throttler@^6.2.1": + version "6.2.1" + resolved "https://registry.yarnpkg.com/@nestjs/throttler/-/throttler-6.2.1.tgz#c241788a8b195e6c7c0cf94b1808c4cb940ac2fd" + integrity sha512-vdt6VjhKC6vcLBJRUb97IuR6Htykn5kokZzmT8+S5XFOLLjUF7rzRpr+nUOhK9pi1L0hhbzSf2v2FJl4v64EJA== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -2397,7 +2532,7 @@ resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== -"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": +"@nodelib/fs.walk@^1.2.3": version "1.2.8" resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== @@ -2405,13 +2540,6 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@ntegral/nestjs-sentry@^4.0.1": - version "4.0.1" - resolved "https://registry.npmjs.org/@ntegral/nestjs-sentry/-/nestjs-sentry-4.0.1.tgz" - integrity sha512-GQUL0Bm0T+FhTNJXUbnF5mZc2u5YuvUV2H6naXxrnw8tY0b9eE/DGj+GUyHNL7V2DuHHFzsYP2c30O5FoGoYfQ== - optionalDependencies: - "@nestjs/graphql" "~12.0.11" - "@nuxtjs/opencollective@0.3.2": version "0.3.2" resolved "https://registry.npmjs.org/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz" @@ -2421,13 +2549,6 @@ consola "^2.15.0" node-fetch "^2.6.1" -"@opentelemetry/api-logs@0.52.0": - version "0.52.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.52.0.tgz#b117c1fc6fc457249739bbe21571cefc55e5092c" - integrity sha512-HxjD7xH9iAE4OyhNaaSec65i1H6QZYBWSwWkowFfsc5YAcDvJG30/J1sRKXEQqdmUcKTXEAnA66UciqZha/4+Q== - dependencies: - "@opentelemetry/api" "^1.0.0" - "@opentelemetry/api-logs@0.52.1": version "0.52.1" resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.52.1.tgz#52906375da4d64c206b0c4cb8ffa209214654ecc" @@ -2435,12 +2556,26 @@ dependencies: "@opentelemetry/api" "^1.0.0" +"@opentelemetry/api-logs@0.53.0": + version "0.53.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.53.0.tgz#c478cbd8120ec2547b64edfa03a552cfe42170be" + integrity sha512-8HArjKx+RaAI8uEIgcORbZIPklyh1YLjPSBus8hjRmvLi6DeFzgOcdZ7KwPabKj8mXF8dX0hyfAyGfycz0DbFw== + dependencies: + "@opentelemetry/api" "^1.0.0" + +"@opentelemetry/api-logs@0.54.0": + version "0.54.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.54.0.tgz#a8e09ae22f6d318b6202765dbc2cc0b05e3377be" + integrity sha512-9HhEh5GqFrassUndqJsyW7a0PzfyWr2eV2xwzHLIS+wX3125+9HE9FMRAKmJRwxZhgZGwH3HNQQjoMGZqmOeVA== + dependencies: + "@opentelemetry/api" "^1.3.0" + "@opentelemetry/api@^1.0.0", "@opentelemetry/api@^1.8": version "1.8.0" resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.8.0.tgz#5aa7abb48f23f693068ed2999ae627d2f7d902ec" integrity sha512-I/s6F7yKUDdtMsoBWXJe8Qz40Tui5vsuKCWJEWVL+5q9sSWRzzx6v2KeNsOBEwd94j0eWkpWCH4yB6rZg9Mf0w== -"@opentelemetry/api@^1.9.0": +"@opentelemetry/api@^1.3.0", "@opentelemetry/api@^1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.9.0.tgz#d03eba68273dc0f7509e2a3d5cba21eae10379fe" integrity sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg== @@ -2457,180 +2592,242 @@ dependencies: "@opentelemetry/semantic-conventions" "1.24.1" -"@opentelemetry/core@1.25.1", "@opentelemetry/core@^1.25.1": +"@opentelemetry/core@1.26.0": + version "1.26.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.26.0.tgz#7d84265aaa850ed0ca5813f97d831155be42b328" + integrity sha512-1iKxXXE8415Cdv0yjG3G6hQnB5eVEsJce3QaawX8SjDn0mAS0ZM8fAbZZJD4ajvhC15cePvosSCut404KrIIvQ== + dependencies: + "@opentelemetry/semantic-conventions" "1.27.0" + +"@opentelemetry/core@^1.25.1": version "1.25.1" resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.25.1.tgz#ff667d939d128adfc7c793edae2f6bca177f829d" integrity sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ== dependencies: "@opentelemetry/semantic-conventions" "1.25.1" -"@opentelemetry/instrumentation-connect@0.38.0": - version "0.38.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.38.0.tgz#1f4aa27894eac2538fb3c8fce7b1be92cae0217e" - integrity sha512-2/nRnx3pjYEmdPIaBwtgtSviTKHWnDZN3R+TkRUnhIVrvBKVcq+I5B2rtd6mr6Fe9cHlZ9Ojcuh7pkNh/xdWWg== +"@opentelemetry/instrumentation-amqplib@^0.42.0": + version "0.42.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.42.0.tgz#b3cab5a7207736a30d769962eed3af3838f986c4" + integrity sha512-fiuU6OKsqHJiydHWgTRQ7MnIrJ2lEqsdgFtNIH4LbAUJl/5XmrIeoDzDnox+hfkgWK65jsleFuQDtYb5hW1koQ== + dependencies: + "@opentelemetry/core" "^1.8.0" + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/semantic-conventions" "^1.27.0" + +"@opentelemetry/instrumentation-connect@0.40.0": + version "0.40.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.40.0.tgz#cb151b860ad8a711ebce4d7e025dcde95e4ba2c5" + integrity sha512-3aR/3YBQ160siitwwRLjwqrv2KBT16897+bo6yz8wIfel6nWOxTZBJudcbsK3p42pTC7qrbotJ9t/1wRLpv79Q== dependencies: "@opentelemetry/core" "^1.8.0" - "@opentelemetry/instrumentation" "^0.52.0" - "@opentelemetry/semantic-conventions" "^1.22.0" + "@opentelemetry/instrumentation" "^0.54.0" + "@opentelemetry/semantic-conventions" "^1.27.0" "@types/connect" "3.4.36" -"@opentelemetry/instrumentation-express@0.41.1": - version "0.41.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-express/-/instrumentation-express-0.41.1.tgz#658561df6ffbae86f5ad33e8d7ef2abb7b4967fc" - integrity sha512-uRx0V3LPGzjn2bxAnV8eUsDT82vT7NTwI0ezEuPMBOTOsnPpGhWdhcdNdhH80sM4TrWrOfXm9HGEdfWE3TRIww== +"@opentelemetry/instrumentation-dataloader@0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.12.0.tgz#de03a3948dec4f15fed80aa424d6bd5d6a8d10c7" + integrity sha512-pnPxatoFE0OXIZDQhL2okF//dmbiWFzcSc8pUg9TqofCLYZySSxDCgQc69CJBo5JnI3Gz1KP+mOjS4WAeRIH4g== + dependencies: + "@opentelemetry/instrumentation" "^0.53.0" + +"@opentelemetry/instrumentation-express@0.44.0": + version "0.44.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-express/-/instrumentation-express-0.44.0.tgz#51dc11e3152ffbee1c4e389298aac30231c8270a" + integrity sha512-GWgibp6Q0wxyFaaU8ERIgMMYgzcHmGrw3ILUtGchLtLncHNOKk0SNoWGqiylXWWT4HTn5XdV8MGawUgpZh80cA== dependencies: "@opentelemetry/core" "^1.8.0" - "@opentelemetry/instrumentation" "^0.52.0" - "@opentelemetry/semantic-conventions" "^1.22.0" + "@opentelemetry/instrumentation" "^0.54.0" + "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-fastify@0.38.0": - version "0.38.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-fastify/-/instrumentation-fastify-0.38.0.tgz#0cb02ee1156197075e8a90e4fd18a6b6c94221ba" - integrity sha512-HBVLpTSYpkQZ87/Df3N0gAw7VzYZV3n28THIBrJWfuqw3Or7UqdhnjeuMIPQ04BKk3aZc0cWn2naSQObbh5vXw== +"@opentelemetry/instrumentation-fastify@0.40.0": + version "0.40.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-fastify/-/instrumentation-fastify-0.40.0.tgz#0c57608ac202337d56b53338f1fc9369d224306b" + integrity sha512-74qj4nG3zPtU7g2x4sm2T4R3/pBMyrYstTsqSZwdlhQk1SD4l8OSY9sPRX1qkhfxOuW3U4KZQAV/Cymb3fB6hg== dependencies: "@opentelemetry/core" "^1.8.0" - "@opentelemetry/instrumentation" "^0.52.0" - "@opentelemetry/semantic-conventions" "^1.22.0" + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-graphql@0.42.0": - version "0.42.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.42.0.tgz#588a18c39e3b3f655bc09243566172ab0b638d35" - integrity sha512-N8SOwoKL9KQSX7z3gOaw5UaTeVQcfDO1c21csVHnmnmGUoqsXbArK2B8VuwPWcv6/BC/i3io+xTo7QGRZ/z28Q== +"@opentelemetry/instrumentation-fs@0.16.0": + version "0.16.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.16.0.tgz#aa1cc3aa81011ad9843a0156b200f06f31ffa03e" + integrity sha512-hMDRUxV38ln1R3lNz6osj3YjlO32ykbHqVrzG7gEhGXFQfu7LJUx8t9tEwE4r2h3CD4D0Rw4YGDU4yF4mP3ilg== dependencies: - "@opentelemetry/instrumentation" "^0.52.0" + "@opentelemetry/core" "^1.8.0" + "@opentelemetry/instrumentation" "^0.54.0" -"@opentelemetry/instrumentation-hapi@0.40.0": - version "0.40.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.40.0.tgz#ae11190f0f57cdb4dc8d792cb8bca61e5343684c" - integrity sha512-8U/w7Ifumtd2bSN1OLaSwAAFhb9FyqWUki3lMMB0ds+1+HdSxYBe9aspEJEgvxAqOkrQnVniAPTEGf1pGM7SOw== +"@opentelemetry/instrumentation-generic-pool@0.39.0": + version "0.39.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.39.0.tgz#2b9af16ad82d5cbe67125c0125753cecd162a728" + integrity sha512-y4v8Y+tSfRB3NNBvHjbjrn7rX/7sdARG7FuK6zR8PGb28CTa0kHpEGCJqvL9L8xkTNvTXo+lM36ajFGUaK1aNw== + dependencies: + "@opentelemetry/instrumentation" "^0.53.0" + +"@opentelemetry/instrumentation-graphql@0.43.0": + version "0.43.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.43.0.tgz#71bb94ea775c70dbd388c739b397ec1418f3f170" + integrity sha512-aI3YMmC2McGd8KW5du1a2gBA0iOMOGLqg4s9YjzwbjFwjlmMNFSK1P3AIg374GWg823RPUGfVTIgZ/juk9CVOA== + dependencies: + "@opentelemetry/instrumentation" "^0.53.0" + +"@opentelemetry/instrumentation-hapi@0.41.0": + version "0.41.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.41.0.tgz#de8711907256d8fae1b5faf71fc825cef4a7ddbb" + integrity sha512-jKDrxPNXDByPlYcMdZjNPYCvw0SQJjN+B1A+QH+sx+sAHsKSAf9hwFiJSrI6C4XdOls43V/f/fkp9ITkHhKFbQ== dependencies: "@opentelemetry/core" "^1.8.0" - "@opentelemetry/instrumentation" "^0.52.0" - "@opentelemetry/semantic-conventions" "^1.22.0" + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-http@0.52.1": - version "0.52.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-http/-/instrumentation-http-0.52.1.tgz#12061501601838d1c912f9c29bdd40a13a7e44cf" - integrity sha512-dG/aevWhaP+7OLv4BQQSEKMJv8GyeOp3Wxl31NHqE8xo9/fYMfEljiZphUHIfyg4gnZ9swMyWjfOQs5GUQe54Q== +"@opentelemetry/instrumentation-http@0.53.0": + version "0.53.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-http/-/instrumentation-http-0.53.0.tgz#0d806adf1b3aba036bc46e16162e3c0dbb8a6b60" + integrity sha512-H74ErMeDuZfj7KgYCTOFGWF5W9AfaPnqLQQxeFq85+D29wwV2yqHbz2IKLYpkOh7EI6QwDEl7rZCIxjJLyc/CQ== dependencies: - "@opentelemetry/core" "1.25.1" - "@opentelemetry/instrumentation" "0.52.1" - "@opentelemetry/semantic-conventions" "1.25.1" + "@opentelemetry/core" "1.26.0" + "@opentelemetry/instrumentation" "0.53.0" + "@opentelemetry/semantic-conventions" "1.27.0" semver "^7.5.2" -"@opentelemetry/instrumentation-ioredis@0.42.0": - version "0.42.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.42.0.tgz#0f488ffc68af3caa474e2f67861759075170729c" - integrity sha512-P11H168EKvBB9TUSasNDOGJCSkpT44XgoM6d3gRIWAa9ghLpYhl0uRkS8//MqPzcJVHr3h3RmfXIpiYLjyIZTw== +"@opentelemetry/instrumentation-ioredis@0.43.0": + version "0.43.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.43.0.tgz#dbadabaeefc4cb47c406f878444f1bcac774fa89" + integrity sha512-i3Dke/LdhZbiUAEImmRG3i7Dimm/BD7t8pDDzwepSvIQ6s2X6FPia7561gw+64w+nx0+G9X14D7rEfaMEmmjig== dependencies: - "@opentelemetry/instrumentation" "^0.52.0" + "@opentelemetry/instrumentation" "^0.53.0" "@opentelemetry/redis-common" "^0.36.2" - "@opentelemetry/semantic-conventions" "^1.23.0" + "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-koa@0.42.0": - version "0.42.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.42.0.tgz#1c180f3605448c2e57a4ba073b69ffba7b2970b3" - integrity sha512-H1BEmnMhho8o8HuNRq5zEI4+SIHDIglNB7BPKohZyWG4fWNuR7yM4GTlR01Syq21vODAS7z5omblScJD/eZdKw== +"@opentelemetry/instrumentation-kafkajs@0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.4.0.tgz#c1fe0de45a65a66581be0d7422f6828cc806b3bb" + integrity sha512-I9VwDG314g7SDL4t8kD/7+1ytaDBRbZQjhVaQaVIDR8K+mlsoBhLsWH79yHxhHQKvwCSZwqXF+TiTOhoQVUt7A== + dependencies: + "@opentelemetry/instrumentation" "^0.54.0" + "@opentelemetry/semantic-conventions" "^1.27.0" + +"@opentelemetry/instrumentation-koa@0.43.0": + version "0.43.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.43.0.tgz#963fd192a1b5f6cbae5dabf4ec82e3105cbb23b1" + integrity sha512-lDAhSnmoTIN6ELKmLJBplXzT/Jqs5jGZehuG22EdSMaTwgjMpxMDI1YtlKEhiWPWkrz5LUsd0aOO0ZRc9vn3AQ== dependencies: "@opentelemetry/core" "^1.8.0" - "@opentelemetry/instrumentation" "^0.52.0" - "@opentelemetry/semantic-conventions" "^1.22.0" + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/semantic-conventions" "^1.27.0" + +"@opentelemetry/instrumentation-lru-memoizer@0.40.0": + version "0.40.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.40.0.tgz#dc60d7fdfd2a0c681cb23e7ed4f314d1506ccdc0" + integrity sha512-21xRwZsEdMPnROu/QsaOIODmzw59IYpGFmuC4aFWvMj6stA8+Ei1tX67nkarJttlNjoM94um0N4X26AD7ff54A== + dependencies: + "@opentelemetry/instrumentation" "^0.53.0" -"@opentelemetry/instrumentation-mongodb@0.46.0": - version "0.46.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.46.0.tgz#e3720e8ca3ca9f228fbf02f0812f7518c030b05e" - integrity sha512-VF/MicZ5UOBiXrqBslzwxhN7TVqzu1/LN/QDpkskqM0Zm0aZ4CVRbUygL8d7lrjLn15x5kGIe8VsSphMfPJzlA== +"@opentelemetry/instrumentation-mongodb@0.47.0": + version "0.47.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.47.0.tgz#f8107d878281433905e717f223fb4c0f10356a7b" + integrity sha512-yqyXRx2SulEURjgOQyJzhCECSh5i1uM49NUaq9TqLd6fA7g26OahyJfsr9NE38HFqGRHpi4loyrnfYGdrsoVjQ== dependencies: - "@opentelemetry/instrumentation" "^0.52.0" + "@opentelemetry/instrumentation" "^0.53.0" "@opentelemetry/sdk-metrics" "^1.9.1" - "@opentelemetry/semantic-conventions" "^1.22.0" + "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-mongoose@0.40.0": - version "0.40.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.40.0.tgz#9c888312e524c381bfdf56a094c799150332dd51" - integrity sha512-niRi5ZUnkgzRhIGMOozTyoZIvJKNJyhijQI4nF4iFSb+FUx2v5fngfR+8XLmdQAO7xmsD8E5vEGdDVYVtKbZew== +"@opentelemetry/instrumentation-mongoose@0.42.0": + version "0.42.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.42.0.tgz#375afd21adfcd897a8f521c1ffd2d91e6a428705" + integrity sha512-AnWv+RaR86uG3qNEMwt3plKX1ueRM7AspfszJYVkvkehiicC3bHQA6vWdb6Zvy5HAE14RyFbu9+2hUUjR2NSyg== dependencies: "@opentelemetry/core" "^1.8.0" - "@opentelemetry/instrumentation" "^0.52.0" - "@opentelemetry/semantic-conventions" "^1.22.0" + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-mysql2@0.40.0": - version "0.40.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.40.0.tgz#fa2992c36d54427dccea68e5c69fff01103dabe6" - integrity sha512-0xfS1xcqUmY7WE1uWjlmI67Xg3QsSUlNT+AcXHeA4BDUPwZtWqF4ezIwLgpVZfHOnkAEheqGfNSWd1PIu3Wnfg== +"@opentelemetry/instrumentation-mysql2@0.41.0": + version "0.41.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.41.0.tgz#6377b6e2d2487fd88e1d79aa03658db6c8d51651" + integrity sha512-REQB0x+IzVTpoNgVmy5b+UnH1/mDByrneimP6sbDHkp1j8QOl1HyWOrBH/6YWR0nrbU3l825Em5PlybjT3232g== dependencies: - "@opentelemetry/instrumentation" "^0.52.0" - "@opentelemetry/semantic-conventions" "^1.22.0" + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/semantic-conventions" "^1.27.0" "@opentelemetry/sql-common" "^0.40.1" -"@opentelemetry/instrumentation-mysql@0.40.0": - version "0.40.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.40.0.tgz#bde5894c8eb447a4b8e940b030b2b73898da03fa" - integrity sha512-d7ja8yizsOCNMYIJt5PH/fKZXjb/mS48zLROO4BzZTtDfhNCl2UM/9VIomP2qkGIFVouSJrGr/T00EzY7bPtKA== +"@opentelemetry/instrumentation-mysql@0.41.0": + version "0.41.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.41.0.tgz#2d50691ead5219774bd36d66c35d5b4681485dd7" + integrity sha512-jnvrV6BsQWyHS2qb2fkfbfSb1R/lmYwqEZITwufuRl37apTopswu9izc0b1CYRp/34tUG/4k/V39PND6eyiNvw== dependencies: - "@opentelemetry/instrumentation" "^0.52.0" - "@opentelemetry/semantic-conventions" "^1.22.0" - "@types/mysql" "2.15.22" + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/semantic-conventions" "^1.27.0" + "@types/mysql" "2.15.26" -"@opentelemetry/instrumentation-nestjs-core@0.39.0": - version "0.39.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.39.0.tgz#733fef4306c796951d7ea1951b45f9df0aed234d" - integrity sha512-mewVhEXdikyvIZoMIUry8eb8l3HUjuQjSjVbmLVTt4NQi35tkpnHQrG9bTRBrl3403LoWZ2njMPJyg4l6HfKvA== +"@opentelemetry/instrumentation-nestjs-core@0.40.0": + version "0.40.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.40.0.tgz#2c0e6405b56caaec32747d55c57ff9a034668ea8" + integrity sha512-WF1hCUed07vKmf5BzEkL0wSPinqJgH7kGzOjjMAiTGacofNXjb/y4KQ8loj2sNsh5C/NN7s1zxQuCgbWbVTGKg== dependencies: - "@opentelemetry/instrumentation" "^0.52.0" - "@opentelemetry/semantic-conventions" "^1.23.0" + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-pg@0.43.0": - version "0.43.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.43.0.tgz#3cd94ad5144e1fd326a921280fa8bb7b49005eb5" - integrity sha512-og23KLyoxdnAeFs1UWqzSonuCkePUzCX30keSYigIzJe/6WSYA8rnEI5lobcxPEzg+GcU06J7jzokuEHbjVJNw== +"@opentelemetry/instrumentation-pg@0.44.0": + version "0.44.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.44.0.tgz#1e97a0aeb2dca068ee23ce75884a0a0063a7ce3f" + integrity sha512-oTWVyzKqXud1BYEGX1loo2o4k4vaU1elr3vPO8NZolrBtFvQ34nx4HgUaexUDuEog00qQt+MLR5gws/p+JXMLQ== dependencies: - "@opentelemetry/instrumentation" "^0.52.0" - "@opentelemetry/semantic-conventions" "^1.22.0" + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/semantic-conventions" "^1.27.0" "@opentelemetry/sql-common" "^0.40.1" "@types/pg" "8.6.1" - "@types/pg-pool" "2.0.4" + "@types/pg-pool" "2.0.6" -"@opentelemetry/instrumentation-redis-4@0.41.0": - version "0.41.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-redis-4/-/instrumentation-redis-4-0.41.0.tgz#6c1b1a37c18478887f346a3bc7ef309ee9f726c0" - integrity sha512-H7IfGTqW2reLXqput4yzAe8YpDC0fmVNal95GHMLOrS89W+qWUKIqxolSh63hJyfmwPSFwXASzj7wpSk8Az+Dg== +"@opentelemetry/instrumentation-redis-4@0.42.0": + version "0.42.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-redis-4/-/instrumentation-redis-4-0.42.0.tgz#fc01104cfe884c7546385eaae03c57a47edd19d1" + integrity sha512-NaD+t2JNcOzX/Qa7kMy68JbmoVIV37fT/fJYzLKu2Wwd+0NCxt+K2OOsOakA8GVg8lSpFdbx4V/suzZZ2Pvdjg== dependencies: - "@opentelemetry/instrumentation" "^0.52.0" + "@opentelemetry/instrumentation" "^0.53.0" "@opentelemetry/redis-common" "^0.36.2" - "@opentelemetry/semantic-conventions" "^1.22.0" + "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation@0.52.1", "@opentelemetry/instrumentation@^0.49 || ^0.50 || ^0.51 || ^0.52.0", "@opentelemetry/instrumentation@^0.52.1": - version "0.52.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.52.1.tgz#2e7e46a38bd7afbf03cf688c862b0b43418b7f48" - integrity sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw== +"@opentelemetry/instrumentation-undici@0.6.0": + version "0.6.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.6.0.tgz#9436ee155c8dcb0b760b66947c0e0f347688a5ef" + integrity sha512-ABJBhm5OdhGmbh0S/fOTE4N69IZ00CsHC5ijMYfzbw3E5NwLgpQk5xsljaECrJ8wz1SfXbO03FiSuu5AyRAkvQ== dependencies: - "@opentelemetry/api-logs" "0.52.1" - "@types/shimmer" "^1.0.2" + "@opentelemetry/core" "^1.8.0" + "@opentelemetry/instrumentation" "^0.53.0" + +"@opentelemetry/instrumentation@0.53.0", "@opentelemetry/instrumentation@^0.53.0": + version "0.53.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.53.0.tgz#e6369e4015eb5112468a4d45d38dcada7dad892d" + integrity sha512-DMwg0hy4wzf7K73JJtl95m/e0boSoWhH07rfvHvYzQtBD3Bmv0Wc1x733vyZBqmFm8OjJD0/pfiUg1W3JjFX0A== + dependencies: + "@opentelemetry/api-logs" "0.53.0" + "@types/shimmer" "^1.2.0" import-in-the-middle "^1.8.1" require-in-the-middle "^7.1.1" semver "^7.5.2" shimmer "^1.2.1" -"@opentelemetry/instrumentation@^0.46.0": - version "0.46.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.46.0.tgz#a8a252306f82e2eace489312798592a14eb9830e" - integrity sha512-a9TijXZZbk0vI5TGLZl+0kxyFfrXHhX6Svtz7Pp2/VBlCSKrazuULEyoJQrOknJyFWNMEmbbJgOciHCCpQcisw== +"@opentelemetry/instrumentation@^0.49 || ^0.50 || ^0.51 || ^0.52.0": + version "0.52.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.52.1.tgz#2e7e46a38bd7afbf03cf688c862b0b43418b7f48" + integrity sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw== dependencies: + "@opentelemetry/api-logs" "0.52.1" "@types/shimmer" "^1.0.2" - import-in-the-middle "1.7.1" + import-in-the-middle "^1.8.1" require-in-the-middle "^7.1.1" semver "^7.5.2" shimmer "^1.2.1" -"@opentelemetry/instrumentation@^0.52.0": - version "0.52.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.52.0.tgz#f8b790bfb1c61c27e0ba846bc6d0e377da195d1e" - integrity sha512-LPwSIrw+60cheWaXsfGL8stBap/AppKQJFE+qqRvzYrgttXFH2ofoIMxWadeqPTq4BYOXM/C7Bdh/T+B60xnlQ== +"@opentelemetry/instrumentation@^0.54.0": + version "0.54.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.54.0.tgz#3fa9df964d3b157ea7ef2270168d343331d6448e" + integrity sha512-B0Ydo9g9ehgNHwtpc97XivEzjz0XBKR6iQ83NTENIxEEf5NHE0otZQuZLgDdey1XNk+bP1cfRpIkSFWM5YlSyg== dependencies: - "@opentelemetry/api-logs" "0.52.0" - "@types/shimmer" "^1.0.2" - import-in-the-middle "1.8.0" + "@opentelemetry/api-logs" "0.54.0" + "@types/shimmer" "^1.2.0" + import-in-the-middle "^1.8.1" require-in-the-middle "^7.1.1" semver "^7.5.2" shimmer "^1.2.1" @@ -2648,13 +2845,13 @@ "@opentelemetry/core" "1.24.1" "@opentelemetry/semantic-conventions" "1.24.1" -"@opentelemetry/resources@1.25.1", "@opentelemetry/resources@^1.25.1": - version "1.25.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.25.1.tgz#bb9a674af25a1a6c30840b755bc69da2796fefbb" - integrity sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ== +"@opentelemetry/resources@1.26.0", "@opentelemetry/resources@^1.26.0": + version "1.26.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.26.0.tgz#da4c7366018bd8add1f3aa9c91c6ac59fd503cef" + integrity sha512-CPNYchBE7MBecCSVy0HKpUISEeJOniWqcHaAHpmasZ3j9o6V3AyBzhRc90jdmemq0HOxDr6ylhUbDhBqqPpeNw== dependencies: - "@opentelemetry/core" "1.25.1" - "@opentelemetry/semantic-conventions" "1.25.1" + "@opentelemetry/core" "1.26.0" + "@opentelemetry/semantic-conventions" "1.27.0" "@opentelemetry/sdk-metrics@^1.9.1": version "1.24.1" @@ -2674,25 +2871,30 @@ "@opentelemetry/resources" "1.24.1" "@opentelemetry/semantic-conventions" "1.24.1" -"@opentelemetry/sdk-trace-base@^1.25.1": - version "1.25.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz#cbc1e60af255655d2020aa14cde17b37bd13df37" - integrity sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw== +"@opentelemetry/sdk-trace-base@^1.26.0": + version "1.26.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.26.0.tgz#0c913bc6d2cfafd901de330e4540952269ae579c" + integrity sha512-olWQldtvbK4v22ymrKLbIcBi9L2SpMO84sCPY54IVsJhP9fRsxJT194C/AVaAuJzLE30EdhhM1VmvVYR7az+cw== dependencies: - "@opentelemetry/core" "1.25.1" - "@opentelemetry/resources" "1.25.1" - "@opentelemetry/semantic-conventions" "1.25.1" + "@opentelemetry/core" "1.26.0" + "@opentelemetry/resources" "1.26.0" + "@opentelemetry/semantic-conventions" "1.27.0" -"@opentelemetry/semantic-conventions@1.24.1", "@opentelemetry/semantic-conventions@^1.17.0", "@opentelemetry/semantic-conventions@^1.22.0", "@opentelemetry/semantic-conventions@^1.23.0": +"@opentelemetry/semantic-conventions@1.24.1": version "1.24.1" resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.24.1.tgz#d4bcebda1cb5146d47a2a53daaa7922f8e084dfb" integrity sha512-VkliWlS4/+GHLLW7J/rVBA00uXus1SWvwFvcUDxDwmFxYfg/2VI6ekwdXS28cjI8Qz2ky2BzG8OUHo+WeYIWqw== -"@opentelemetry/semantic-conventions@1.25.1", "@opentelemetry/semantic-conventions@^1.25.1": +"@opentelemetry/semantic-conventions@1.25.1": version "1.25.1" resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz#0deecb386197c5e9c2c28f2f89f51fb8ae9f145e" integrity sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ== +"@opentelemetry/semantic-conventions@1.27.0", "@opentelemetry/semantic-conventions@^1.27.0": + version "1.27.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.27.0.tgz#1a857dcc95a5ab30122e04417148211e6f945e6c" + integrity sha512-sAay1RrB+ONOem0OZanAR1ZI/k7yDpnOQSQmTMuGImUQb2y8EbSaCJ94FQluM74xoU03vlb2d2U90hZluL6nQg== + "@opentelemetry/sql-common@^0.40.1": version "0.40.1" resolved "https://registry.yarnpkg.com/@opentelemetry/sql-common/-/sql-common-0.40.1.tgz#93fbc48d8017449f5b3c3274f2268a08af2b83b6" @@ -2705,10 +2907,10 @@ resolved "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== -"@prisma/instrumentation@5.17.0": - version "5.17.0" - resolved "https://registry.yarnpkg.com/@prisma/instrumentation/-/instrumentation-5.17.0.tgz#f741ff517f54b1a896fb8605e0d702f29855c6cb" - integrity sha512-c1Sle4ji8aasMcYfBBHFM56We4ljfenVtRmS8aY06BllS7SoU6SmJBwG7vil+GHiR0Yrh+t9iBwt4AY0Jr4KNQ== +"@prisma/instrumentation@5.19.1": + version "5.19.1" + resolved "https://registry.yarnpkg.com/@prisma/instrumentation/-/instrumentation-5.19.1.tgz#146319cf85f22b7a43296f0f40cfeac55516e66e" + integrity sha512-VLnzMQq7CWroL5AeaW0Py2huiNKeoMfCH3SUxstdzPrlWQi6UQ9UrfcbUkNHlVFqOMacqy8X/8YtE0kuKDpD9w== dependencies: "@opentelemetry/api" "^1.8" "@opentelemetry/instrumentation" "^0.49 || ^0.50 || ^0.51 || ^0.52.0" @@ -2746,72 +2948,99 @@ "@redis/time-series@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@redis/time-series/-/time-series-1.1.0.tgz#cba454c05ec201bd5547aaf55286d44682ac8eb5" - integrity sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g== - -"@sentry/core@8.25.0": - version "8.25.0" - resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.25.0.tgz#f64e50b88ee5b13f1d52b543638b2eb5c8e326d8" - integrity sha512-7KtglbrW1eX4DOHkf6i4rRIExEf2CgtQ99qZ8gn5FUaAmNMg0rK7bb1yZMx0RZtp5G1TSz/S0jQQgxHWebaEig== - dependencies: - "@sentry/types" "8.25.0" - "@sentry/utils" "8.25.0" - -"@sentry/node@^8.25.0": - version "8.25.0" - resolved "https://registry.yarnpkg.com/@sentry/node/-/node-8.25.0.tgz#1642e065082aba7e9dca5506a18366b2dbb4ce58" - integrity sha512-KFeJpYU/7CKi/v8D72ztniA+QqH0yBv2wzEP0PUe3DWZ/Fwl0OQSVWNNuDfJBQUvk3NrytCH5A6klZjU0/rwlw== - dependencies: - "@opentelemetry/api" "^1.9.0" - "@opentelemetry/context-async-hooks" "^1.25.1" - "@opentelemetry/core" "^1.25.1" - "@opentelemetry/instrumentation" "^0.52.1" - "@opentelemetry/instrumentation-connect" "0.38.0" - "@opentelemetry/instrumentation-express" "0.41.1" - "@opentelemetry/instrumentation-fastify" "0.38.0" - "@opentelemetry/instrumentation-graphql" "0.42.0" - "@opentelemetry/instrumentation-hapi" "0.40.0" - "@opentelemetry/instrumentation-http" "0.52.1" - "@opentelemetry/instrumentation-ioredis" "0.42.0" - "@opentelemetry/instrumentation-koa" "0.42.0" - "@opentelemetry/instrumentation-mongodb" "0.46.0" - "@opentelemetry/instrumentation-mongoose" "0.40.0" - "@opentelemetry/instrumentation-mysql" "0.40.0" - "@opentelemetry/instrumentation-mysql2" "0.40.0" - "@opentelemetry/instrumentation-nestjs-core" "0.39.0" - "@opentelemetry/instrumentation-pg" "0.43.0" - "@opentelemetry/instrumentation-redis-4" "0.41.0" - "@opentelemetry/resources" "^1.25.1" - "@opentelemetry/sdk-trace-base" "^1.25.1" - "@opentelemetry/semantic-conventions" "^1.25.1" - "@prisma/instrumentation" "5.17.0" - "@sentry/core" "8.25.0" - "@sentry/opentelemetry" "8.25.0" - "@sentry/types" "8.25.0" - "@sentry/utils" "8.25.0" - import-in-the-middle "^1.11.0" - optionalDependencies: - opentelemetry-instrumentation-fetch-node "1.2.3" + integrity sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g== -"@sentry/opentelemetry@8.25.0": - version "8.25.0" - resolved "https://registry.yarnpkg.com/@sentry/opentelemetry/-/opentelemetry-8.25.0.tgz#888425f08668c288611d4783346d16e364d964d2" - integrity sha512-6g4TXwQMHtvmlu2i1OKqvFD2W2RTrGBxDtJ1tBQmqCfHKyiqQ37gy6AozuwrQ3po1KKbawaQGIFNEzb4wnSrfA== +"@sentry/core@8.36.0": + version "8.36.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.36.0.tgz#34276354f0cd2298803c2f8d86ba571473435ff1" + integrity sha512-cbq1WQyRqc/+YpPhjwQxfniUM3ZxmO3Pm1oisTB8dw6mlbgQfGD6aznEIjXWWJY6k6acewJlMUx09N7DnprtBw== dependencies: - "@sentry/core" "8.25.0" - "@sentry/types" "8.25.0" - "@sentry/utils" "8.25.0" + "@sentry/types" "8.36.0" + "@sentry/utils" "8.36.0" -"@sentry/types@8.25.0": - version "8.25.0" - resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.25.0.tgz#cde3d900efe7fb7614a670f0af2634a2cbd92693" - integrity sha512-ojim0gDcRhGJPguYrtms4FsprX4xZz3LGNk9Z0hwTbSVEdlhQIInsQ7CYcdM3sjUs+qT7kfpxTRZGUeZNRRJcA== +"@sentry/nestjs@^8.36.0": + version "8.36.0" + resolved "https://registry.yarnpkg.com/@sentry/nestjs/-/nestjs-8.36.0.tgz#429f5c640edbec6de690461ec4a7fab552745f92" + integrity sha512-rnykgvU0QXEGV1Ea1Ctv2t/D/sES8lcKJx2Odjq7EDXQusn6RsOk4wuU8N/vZ3fMNHQrqo1KwXJtWetWmcp56g== + dependencies: + "@sentry/core" "8.36.0" + "@sentry/node" "8.36.0" + "@sentry/types" "8.36.0" + "@sentry/utils" "8.36.0" -"@sentry/utils@8.25.0": - version "8.25.0" - resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-8.25.0.tgz#708ccf8b953f64e1a5915e09d4cb33105b29e436" - integrity sha512-mVlkV7S62ZZ2jM38/kOwWx2xoW8fUv2cjw2IwFKoAIPyLBh3mo1WJtvfdtN/rXGjQWZJBKW53EWaWnD00rkjyA== +"@sentry/node@8.36.0": + version "8.36.0" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-8.36.0.tgz#6b4b6714c6c925e353e7c7eff0e45f8675bb530e" + integrity sha512-2RRbSck90TGpVz8F3OaNbq5Q9RXgeRlq5leGWHU7NfQOl3LmkG+vkzTbOqPDPZLtiYcw5KQ3G5G+vybrDS6AGg== dependencies: - "@sentry/types" "8.25.0" + "@opentelemetry/api" "^1.9.0" + "@opentelemetry/context-async-hooks" "^1.25.1" + "@opentelemetry/core" "^1.25.1" + "@opentelemetry/instrumentation" "^0.53.0" + "@opentelemetry/instrumentation-amqplib" "^0.42.0" + "@opentelemetry/instrumentation-connect" "0.40.0" + "@opentelemetry/instrumentation-dataloader" "0.12.0" + "@opentelemetry/instrumentation-express" "0.44.0" + "@opentelemetry/instrumentation-fastify" "0.40.0" + "@opentelemetry/instrumentation-fs" "0.16.0" + "@opentelemetry/instrumentation-generic-pool" "0.39.0" + "@opentelemetry/instrumentation-graphql" "0.43.0" + "@opentelemetry/instrumentation-hapi" "0.41.0" + "@opentelemetry/instrumentation-http" "0.53.0" + "@opentelemetry/instrumentation-ioredis" "0.43.0" + "@opentelemetry/instrumentation-kafkajs" "0.4.0" + "@opentelemetry/instrumentation-koa" "0.43.0" + "@opentelemetry/instrumentation-lru-memoizer" "0.40.0" + "@opentelemetry/instrumentation-mongodb" "0.47.0" + "@opentelemetry/instrumentation-mongoose" "0.42.0" + "@opentelemetry/instrumentation-mysql" "0.41.0" + "@opentelemetry/instrumentation-mysql2" "0.41.0" + "@opentelemetry/instrumentation-nestjs-core" "0.40.0" + "@opentelemetry/instrumentation-pg" "0.44.0" + "@opentelemetry/instrumentation-redis-4" "0.42.0" + "@opentelemetry/instrumentation-undici" "0.6.0" + "@opentelemetry/resources" "^1.26.0" + "@opentelemetry/sdk-trace-base" "^1.26.0" + "@opentelemetry/semantic-conventions" "^1.27.0" + "@prisma/instrumentation" "5.19.1" + "@sentry/core" "8.36.0" + "@sentry/opentelemetry" "8.36.0" + "@sentry/types" "8.36.0" + "@sentry/utils" "8.36.0" + import-in-the-middle "^1.11.2" + +"@sentry/opentelemetry@8.36.0": + version "8.36.0" + resolved "https://registry.yarnpkg.com/@sentry/opentelemetry/-/opentelemetry-8.36.0.tgz#4e4330fa67cfeb7f2e23d4e078659ac61806b3b0" + integrity sha512-pMKMphH0j1Mh8zknLWEEUaaaxeYn76rniGOxKLoQVk1pCUhhzkFEJdxKC41aR8yin/uN8X3CGWQb9vp/przwSg== + dependencies: + "@sentry/core" "8.36.0" + "@sentry/types" "8.36.0" + "@sentry/utils" "8.36.0" + +"@sentry/profiling-node@^8.36.0": + version "8.36.0" + resolved "https://registry.yarnpkg.com/@sentry/profiling-node/-/profiling-node-8.36.0.tgz#81b15ac874e9770642517f0d840f1e3a4b40c68c" + integrity sha512-cIuj0noGk5EunzogsMc8iUFfigfhMBcfjvMZLKV/DAJx/6PcP4EbStAysFpvgqhMduf0UX5DIN/vuxk7ulrJSQ== + dependencies: + "@sentry/core" "8.36.0" + "@sentry/node" "8.36.0" + "@sentry/types" "8.36.0" + "@sentry/utils" "8.36.0" + detect-libc "^2.0.2" + node-abi "^3.61.0" + +"@sentry/types@8.36.0": + version "8.36.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.36.0.tgz#b58397eb672d896b65b06103feb59dba74da9d39" + integrity sha512-K1pVFfdGHw115RzGHpwSOqoEPeayn4N1F9IfM0kxrYpQSbFT1X29eak88GBfC8gPiLEF0iFGlSaQ4ERmF7oRcA== + +"@sentry/utils@8.36.0": + version "8.36.0" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-8.36.0.tgz#e733042ae231fdeeafe6970e49283dcd9ac9700f" + integrity sha512-oJ3EDPj0I00z+AwC3EWBpSidXYUoKW0Id8MfMQP5Hflniz3gif7UEReblT+FJgPEVo6+6uNzAncY0MuNMxmDKQ== + dependencies: + "@sentry/types" "8.36.0" "@sinclair/typebox@^0.24.1": version "0.24.19" @@ -2823,6 +3052,11 @@ resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz" integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== +"@sindresorhus/is@^4.0.0": + version "4.6.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" + integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== + "@sinonjs/commons@^2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz" @@ -2837,12 +3071,12 @@ dependencies: "@sinonjs/commons" "^2.0.0" -"@smithy/abort-controller@^3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@smithy/abort-controller/-/abort-controller-3.1.1.tgz#291210611ff6afecfc198d0ca72d5771d8461d16" - integrity sha512-MBJBiidoe+0cTFhyxT8g+9g7CeVccLM0IOKKUMCNQ1CNMJ/eIfoo0RTfVrXOONEI1UCN1W+zkiHSbzUNE9dZtQ== +"@smithy/abort-controller@^3.1.5": + version "3.1.5" + resolved "https://registry.yarnpkg.com/@smithy/abort-controller/-/abort-controller-3.1.5.tgz#ca7a86a3c6b20fabe59667143f58d9e198616d14" + integrity sha512-DhNPnqTqPoG8aZ5dWkFOgsuY+i0GQ3CI6hMmvCoduNsnU9gUZWZBwGfDQsTTB7NvFPkom1df7jMIJWU90kuXXg== dependencies: - "@smithy/types" "^3.3.0" + "@smithy/types" "^3.5.0" tslib "^2.6.2" "@smithy/chunked-blob-reader-native@^3.0.0": @@ -2860,133 +3094,135 @@ dependencies: tslib "^2.6.2" -"@smithy/config-resolver@^3.0.5": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@smithy/config-resolver/-/config-resolver-3.0.5.tgz#727978bba7ace754c741c259486a19d3083431fd" - integrity sha512-SkW5LxfkSI1bUC74OtfBbdz+grQXYiPYolyu8VfpLIjEoN/sHVBlLeGXMQ1vX4ejkgfv6sxVbQJ32yF2cl1veA== +"@smithy/config-resolver@^3.0.9": + version "3.0.9" + resolved "https://registry.yarnpkg.com/@smithy/config-resolver/-/config-resolver-3.0.9.tgz#dcf4b7747ca481866f9bfac21469ebe2031a599e" + integrity sha512-5d9oBf40qC7n2xUoHmntKLdqsyTMMo/r49+eqSIjJ73eDfEtljAxEhzIQ3bkgXJtR3xiv7YzMT/3FF3ORkjWdg== dependencies: - "@smithy/node-config-provider" "^3.1.4" - "@smithy/types" "^3.3.0" + "@smithy/node-config-provider" "^3.1.8" + "@smithy/types" "^3.5.0" "@smithy/util-config-provider" "^3.0.0" - "@smithy/util-middleware" "^3.0.3" + "@smithy/util-middleware" "^3.0.7" tslib "^2.6.2" -"@smithy/core@^2.3.2": - version "2.3.2" - resolved "https://registry.yarnpkg.com/@smithy/core/-/core-2.3.2.tgz#4a1e3da41d2a3a494cbc6bd1fc6eeb26b2e27184" - integrity sha512-in5wwt6chDBcUv1Lw1+QzZxN9fBffi+qOixfb65yK4sDuKG7zAUO9HAFqmVzsZM3N+3tTyvZjtnDXePpvp007Q== - dependencies: - "@smithy/middleware-endpoint" "^3.1.0" - "@smithy/middleware-retry" "^3.0.14" - "@smithy/middleware-serde" "^3.0.3" - "@smithy/protocol-http" "^4.1.0" - "@smithy/smithy-client" "^3.1.12" - "@smithy/types" "^3.3.0" - "@smithy/util-middleware" "^3.0.3" +"@smithy/core@^2.4.8": + version "2.4.8" + resolved "https://registry.yarnpkg.com/@smithy/core/-/core-2.4.8.tgz#397ac17dfa8ad658b77f96f19484f0eeaf22d397" + integrity sha512-x4qWk7p/a4dcf7Vxb2MODIf4OIcqNbK182WxRvZ/3oKPrf/6Fdic5sSElhO1UtXpWKBazWfqg0ZEK9xN1DsuHA== + dependencies: + "@smithy/middleware-endpoint" "^3.1.4" + "@smithy/middleware-retry" "^3.0.23" + "@smithy/middleware-serde" "^3.0.7" + "@smithy/protocol-http" "^4.1.4" + "@smithy/smithy-client" "^3.4.0" + "@smithy/types" "^3.5.0" + "@smithy/util-body-length-browser" "^3.0.0" + "@smithy/util-middleware" "^3.0.7" + "@smithy/util-utf8" "^3.0.0" tslib "^2.6.2" -"@smithy/credential-provider-imds@^3.2.0": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.0.tgz#0e0e7ddaff1a8633cb927aee1056c0ab506b7ecf" - integrity sha512-0SCIzgd8LYZ9EJxUjLXBmEKSZR/P/w6l7Rz/pab9culE/RWuqelAKGJvn5qUOl8BgX8Yj5HWM50A5hiB/RzsgA== +"@smithy/credential-provider-imds@^3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.4.tgz#e1a2bfc8a0066f673756ad8735247cf284b9735c" + integrity sha512-S9bb0EIokfYEuar4kEbLta+ivlKCWOCFsLZuilkNy9i0uEUEHSi47IFLPaxqqCl+0ftKmcOTHayY5nQhAuq7+w== dependencies: - "@smithy/node-config-provider" "^3.1.4" - "@smithy/property-provider" "^3.1.3" - "@smithy/types" "^3.3.0" - "@smithy/url-parser" "^3.0.3" + "@smithy/node-config-provider" "^3.1.8" + "@smithy/property-provider" "^3.1.7" + "@smithy/types" "^3.5.0" + "@smithy/url-parser" "^3.0.7" tslib "^2.6.2" -"@smithy/eventstream-codec@^3.1.2": - version "3.1.2" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-codec/-/eventstream-codec-3.1.2.tgz#4a1c72b34400631b829241151984a1ad8c4f963c" - integrity sha512-0mBcu49JWt4MXhrhRAlxASNy0IjDRFU+aWNDRal9OtUJvJNiwDuyKMUONSOjLjSCeGwZaE0wOErdqULer8r7yw== +"@smithy/eventstream-codec@^3.1.6": + version "3.1.6" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-codec/-/eventstream-codec-3.1.6.tgz#70ca95aad82d5140522eb883fbc140f1f22dcb27" + integrity sha512-SBiOYPBH+5wOyPS7lfI150ePfGLhnp/eTu5RnV9xvhGvRiKfnl6HzRK9wehBph+il8FxS9KTeadx7Rcmf1GLPQ== dependencies: "@aws-crypto/crc32" "5.2.0" - "@smithy/types" "^3.3.0" + "@smithy/types" "^3.5.0" "@smithy/util-hex-encoding" "^3.0.0" tslib "^2.6.2" -"@smithy/eventstream-serde-browser@^3.0.6": - version "3.0.6" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.6.tgz#a4ab4f7cfbd137bcaa54c375276f9214e568fd8f" - integrity sha512-2hM54UWQUOrki4BtsUI1WzmD13/SeaqT/AB3EUJKbcver/WgKNaiJ5y5F5XXuVe6UekffVzuUDrBZVAA3AWRpQ== +"@smithy/eventstream-serde-browser@^3.0.10": + version "3.0.10" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.10.tgz#ffca366a4edee5097be5a710f87627a5b2da5dec" + integrity sha512-1i9aMY6Pl/SmA6NjvidxnfBLHMPzhKu2BP148pEt5VwhMdmXn36PE2kWKGa9Hj8b0XGtCTRucpCncylevCtI7g== dependencies: - "@smithy/eventstream-serde-universal" "^3.0.5" - "@smithy/types" "^3.3.0" + "@smithy/eventstream-serde-universal" "^3.0.9" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@smithy/eventstream-serde-config-resolver@^3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.0.3.tgz#f852e096d0ad112363b4685e1d441088d1fce67a" - integrity sha512-NVTYjOuYpGfrN/VbRQgn31x73KDLfCXCsFdad8DiIc3IcdxL+dYA9zEQPyOP7Fy2QL8CPy2WE4WCUD+ZsLNfaQ== +"@smithy/eventstream-serde-config-resolver@^3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.0.7.tgz#1f352f384665f322e024a1396a7a2cca52fce9e3" + integrity sha512-eVzhGQBPEqXXYHvIUku0jMTxd4gDvenRzUQPTmKVWdRvp9JUCKrbAXGQRYiGxUYq9+cqQckRm0wq3kTWnNtDhw== dependencies: - "@smithy/types" "^3.3.0" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@smithy/eventstream-serde-node@^3.0.5": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.5.tgz#2bbf5c9312a28f23bc55ae284efa9499f8b8f982" - integrity sha512-+upXvnHNyZP095s11jF5dhGw/Ihzqwl5G+/KtMnoQOpdfC3B5HYCcDVG9EmgkhJMXJlM64PyN5gjJl0uXFQehQ== +"@smithy/eventstream-serde-node@^3.0.9": + version "3.0.9" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.9.tgz#e985340093c2ca6587ae2fdd0663e6845fbe9463" + integrity sha512-JE0Guqvt0xsmfQ5y1EI342/qtJqznBv8cJqkHZV10PwC8GWGU5KNgFbQnsVCcX+xF+qIqwwfRmeWoJCjuOLmng== dependencies: - "@smithy/eventstream-serde-universal" "^3.0.5" - "@smithy/types" "^3.3.0" + "@smithy/eventstream-serde-universal" "^3.0.9" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@smithy/eventstream-serde-universal@^3.0.5": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.5.tgz#e1cc2f71f4d174a03e00ce4b563395a81dd17bec" - integrity sha512-5u/nXbyoh1s4QxrvNre9V6vfyoLWuiVvvd5TlZjGThIikc3G+uNiG9uOTCWweSRjv1asdDIWK7nOmN7le4RYHQ== +"@smithy/eventstream-serde-universal@^3.0.9": + version "3.0.9" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.9.tgz#1832b190a3018204e33487ba1f7f0f6e2fb0da34" + integrity sha512-bydfgSisfepCufw9kCEnWRxqxJFzX/o8ysXWv+W9F2FIyiaEwZ/D8bBKINbh4ONz3i05QJ1xE7A5OKYvgJsXaw== dependencies: - "@smithy/eventstream-codec" "^3.1.2" - "@smithy/types" "^3.3.0" + "@smithy/eventstream-codec" "^3.1.6" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@smithy/fetch-http-handler@^3.2.4": - version "3.2.4" - resolved "https://registry.yarnpkg.com/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.4.tgz#c754de7e0ff2541b73ac9ba7cc955940114b3d62" - integrity sha512-kBprh5Gs5h7ug4nBWZi1FZthdqSM+T7zMmsZxx0IBvWUn7dK3diz2SHn7Bs4dQGFDk8plDv375gzenDoNwrXjg== +"@smithy/fetch-http-handler@^3.2.9": + version "3.2.9" + resolved "https://registry.yarnpkg.com/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.9.tgz#8d5199c162a37caa37a8b6848eefa9ca58221a0b" + integrity sha512-hYNVQOqhFQ6vOpenifFME546f0GfJn2OiQ3M0FDmuUu8V/Uiwy2wej7ZXxFBNqdx0R5DZAqWM1l6VRhGz8oE6A== dependencies: - "@smithy/protocol-http" "^4.1.0" - "@smithy/querystring-builder" "^3.0.3" - "@smithy/types" "^3.3.0" + "@smithy/protocol-http" "^4.1.4" + "@smithy/querystring-builder" "^3.0.7" + "@smithy/types" "^3.5.0" "@smithy/util-base64" "^3.0.0" tslib "^2.6.2" -"@smithy/hash-blob-browser@^3.1.2": - version "3.1.2" - resolved "https://registry.yarnpkg.com/@smithy/hash-blob-browser/-/hash-blob-browser-3.1.2.tgz#90281c1f183d93686fb4f26107f1819644d68829" - integrity sha512-hAbfqN2UbISltakCC2TP0kx4LqXBttEv2MqSPE98gVuDFMf05lU+TpC41QtqGP3Ff5A3GwZMPfKnEy0VmEUpmg== +"@smithy/hash-blob-browser@^3.1.6": + version "3.1.6" + resolved "https://registry.yarnpkg.com/@smithy/hash-blob-browser/-/hash-blob-browser-3.1.6.tgz#d61de344aa3cef0bc83e3ab8166558256262dfcd" + integrity sha512-BKNcMIaeZ9lB67sgo88iCF4YB35KT8X2dNJ8DqrtZNTgN6tUDYBKThzfGtos/mnZkGkW91AYHisESHmSiYQmKw== dependencies: "@smithy/chunked-blob-reader" "^3.0.0" "@smithy/chunked-blob-reader-native" "^3.0.0" - "@smithy/types" "^3.3.0" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@smithy/hash-node@^3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@smithy/hash-node/-/hash-node-3.0.3.tgz#82c5cb7b0f1a29ee7319081853d2d158c07dff24" - integrity sha512-2ctBXpPMG+B3BtWSGNnKELJ7SH9e4TNefJS0cd2eSkOOROeBnnVBnAy9LtJ8tY4vUEoe55N4CNPxzbWvR39iBw== +"@smithy/hash-node@^3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@smithy/hash-node/-/hash-node-3.0.7.tgz#03b5a382fb588b8c2bac11b4fe7300aaf1661c88" + integrity sha512-SAGHN+QkrwcHFjfWzs/czX94ZEjPJ0CrWJS3M43WswDXVEuP4AVy9gJ3+AF6JQHZD13bojmuf/Ap/ItDeZ+Qfw== dependencies: - "@smithy/types" "^3.3.0" + "@smithy/types" "^3.5.0" "@smithy/util-buffer-from" "^3.0.0" "@smithy/util-utf8" "^3.0.0" tslib "^2.6.2" -"@smithy/hash-stream-node@^3.1.2": - version "3.1.2" - resolved "https://registry.yarnpkg.com/@smithy/hash-stream-node/-/hash-stream-node-3.1.2.tgz#89f0290ae44b113863878e75b10c484ff48af71c" - integrity sha512-PBgDMeEdDzi6JxKwbfBtwQG9eT9cVwsf0dZzLXoJF4sHKHs5HEo/3lJWpn6jibfJwT34I1EBXpBnZE8AxAft6g== +"@smithy/hash-stream-node@^3.1.6": + version "3.1.6" + resolved "https://registry.yarnpkg.com/@smithy/hash-stream-node/-/hash-stream-node-3.1.6.tgz#854ad354a865a1334baa2abc2f2247f2723de688" + integrity sha512-sFSSt7cmCpFWZPfVx7k80Bgb1K2VJ27VmMxH8X+dDhp7Wv8IBgID4K2VK5ehMJROF8hQgcj4WywnkHIwX/xlwQ== dependencies: - "@smithy/types" "^3.3.0" + "@smithy/types" "^3.5.0" "@smithy/util-utf8" "^3.0.0" tslib "^2.6.2" -"@smithy/invalid-dependency@^3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@smithy/invalid-dependency/-/invalid-dependency-3.0.3.tgz#8d9fd70e3a94b565a4eba4ffbdc95238e1930528" - integrity sha512-ID1eL/zpDULmHJbflb864k72/SNOZCADRc9i7Exq3RUNJw6raWUSlFEQ+3PX3EYs++bTxZB2dE9mEHTQLv61tw== +"@smithy/invalid-dependency@^3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@smithy/invalid-dependency/-/invalid-dependency-3.0.7.tgz#b36f258d94498f3c72ab6020091a66fc7cc16eda" + integrity sha512-Bq00GsAhHeYSuZX8Kpu4sbI9agH2BNYnqUmmbTGWOhki9NVsWn2jFr896vvoTMH8KAjNX/ErC/8t5QHuEXG+IA== dependencies: - "@smithy/types" "^3.3.0" + "@smithy/types" "^3.5.0" tslib "^2.6.2" "@smithy/is-array-buffer@^2.2.0": @@ -3003,177 +3239,177 @@ dependencies: tslib "^2.6.2" -"@smithy/md5-js@^3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@smithy/md5-js/-/md5-js-3.0.3.tgz#55ee40aa24075b096c39f7910590c18ff7660c98" - integrity sha512-O/SAkGVwpWmelpj/8yDtsaVe6sINHLB1q8YE/+ZQbDxIw3SRLbTZuRaI10K12sVoENdnHqzPp5i3/H+BcZ3m3Q== +"@smithy/md5-js@^3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@smithy/md5-js/-/md5-js-3.0.7.tgz#0a645dd9c139254353fd6e6a6b65154baeab7d2e" + integrity sha512-+wco9IN9uOW4tNGkZIqTR6IXyfO7Z8A+IOq82QCRn/f/xcmt7H1fXwmQVbfDSvbeFwfNnhv7s+u0G9PzPG6o2w== dependencies: - "@smithy/types" "^3.3.0" + "@smithy/types" "^3.5.0" "@smithy/util-utf8" "^3.0.0" tslib "^2.6.2" -"@smithy/middleware-content-length@^3.0.5": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@smithy/middleware-content-length/-/middleware-content-length-3.0.5.tgz#1680aa4fb2a1c0505756103c9a5c2916307d9035" - integrity sha512-ILEzC2eyxx6ncej3zZSwMpB5RJ0zuqH7eMptxC4KN3f+v9bqT8ohssKbhNR78k/2tWW+KS5Spw+tbPF4Ejyqvw== +"@smithy/middleware-content-length@^3.0.9": + version "3.0.9" + resolved "https://registry.yarnpkg.com/@smithy/middleware-content-length/-/middleware-content-length-3.0.9.tgz#fb613d1a6b8c91e828d11c0d7a0a8576dba89b8b" + integrity sha512-t97PidoGElF9hTtLCrof32wfWMqC5g2SEJNxaVH3NjlatuNGsdxXRYO/t+RPnxA15RpYiS0f+zG7FuE2DeGgjA== dependencies: - "@smithy/protocol-http" "^4.1.0" - "@smithy/types" "^3.3.0" + "@smithy/protocol-http" "^4.1.4" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@smithy/middleware-endpoint@^3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.0.tgz#9b8a496d87a68ec43f3f1a0139868d6765a88119" - integrity sha512-5y5aiKCEwg9TDPB4yFE7H6tYvGFf1OJHNczeY10/EFF8Ir8jZbNntQJxMWNfeQjC1mxPsaQ6mR9cvQbf+0YeMw== - dependencies: - "@smithy/middleware-serde" "^3.0.3" - "@smithy/node-config-provider" "^3.1.4" - "@smithy/shared-ini-file-loader" "^3.1.4" - "@smithy/types" "^3.3.0" - "@smithy/url-parser" "^3.0.3" - "@smithy/util-middleware" "^3.0.3" +"@smithy/middleware-endpoint@^3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.4.tgz#222c9fa49c8af6ebf8bea8ab220d92d9b8c90d3d" + integrity sha512-/ChcVHekAyzUbyPRI8CzPPLj6y8QRAfJngWcLMgsWxKVzw/RzBV69mSOzJYDD3pRwushA1+5tHtPF8fjmzBnrQ== + dependencies: + "@smithy/middleware-serde" "^3.0.7" + "@smithy/node-config-provider" "^3.1.8" + "@smithy/shared-ini-file-loader" "^3.1.8" + "@smithy/types" "^3.5.0" + "@smithy/url-parser" "^3.0.7" + "@smithy/util-middleware" "^3.0.7" tslib "^2.6.2" -"@smithy/middleware-retry@^3.0.14": - version "3.0.14" - resolved "https://registry.yarnpkg.com/@smithy/middleware-retry/-/middleware-retry-3.0.14.tgz#739e8bac6e465e0cda26446999db614418e79da3" - integrity sha512-7ZaWZJOjUxa5hgmuMspyt8v/zVsh0GXYuF7OvCmdcbVa/xbnKQoYC+uYKunAqRGTkxjOyuOCw9rmFUFOqqC0eQ== - dependencies: - "@smithy/node-config-provider" "^3.1.4" - "@smithy/protocol-http" "^4.1.0" - "@smithy/service-error-classification" "^3.0.3" - "@smithy/smithy-client" "^3.1.12" - "@smithy/types" "^3.3.0" - "@smithy/util-middleware" "^3.0.3" - "@smithy/util-retry" "^3.0.3" +"@smithy/middleware-retry@^3.0.23": + version "3.0.23" + resolved "https://registry.yarnpkg.com/@smithy/middleware-retry/-/middleware-retry-3.0.23.tgz#ce5574e278dd14a7995afd5a4ed2a6c9891da8ed" + integrity sha512-x9PbGXxkcXIpm6L26qRSCC+eaYcHwybRmqU8LO/WM2RRlW0g8lz6FIiKbKgGvHuoK3dLZRiQVSQJveiCzwnA5A== + dependencies: + "@smithy/node-config-provider" "^3.1.8" + "@smithy/protocol-http" "^4.1.4" + "@smithy/service-error-classification" "^3.0.7" + "@smithy/smithy-client" "^3.4.0" + "@smithy/types" "^3.5.0" + "@smithy/util-middleware" "^3.0.7" + "@smithy/util-retry" "^3.0.7" tslib "^2.6.2" uuid "^9.0.1" -"@smithy/middleware-serde@^3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@smithy/middleware-serde/-/middleware-serde-3.0.3.tgz#74d974460f74d99f38c861e6862984543a880a66" - integrity sha512-puUbyJQBcg9eSErFXjKNiGILJGtiqmuuNKEYNYfUD57fUl4i9+mfmThtQhvFXU0hCVG0iEJhvQUipUf+/SsFdA== +"@smithy/middleware-serde@^3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@smithy/middleware-serde/-/middleware-serde-3.0.7.tgz#03f0dda75edffc4cc90ea422349cbfb82368efa7" + integrity sha512-VytaagsQqtH2OugzVTq4qvjkLNbWehHfGcGr0JLJmlDRrNCeZoWkWsSOw1nhS/4hyUUWF/TLGGml4X/OnEep5g== dependencies: - "@smithy/types" "^3.3.0" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@smithy/middleware-stack@^3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@smithy/middleware-stack/-/middleware-stack-3.0.3.tgz#91845c7e61e6f137fa912b623b6def719a4f6ce7" - integrity sha512-r4klY9nFudB0r9UdSMaGSyjyQK5adUyPnQN/ZM6M75phTxOdnc/AhpvGD1fQUvgmqjQEBGCwpnPbDm8pH5PapA== +"@smithy/middleware-stack@^3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@smithy/middleware-stack/-/middleware-stack-3.0.7.tgz#813fa7b47895ce0d085eac89c056d21b1e46e771" + integrity sha512-EyTbMCdqS1DoeQsO4gI7z2Gzq1MoRFAeS8GkFYIwbedB7Lp5zlLHJdg+56tllIIG5Hnf9ZWX48YKSHlsKvugGA== dependencies: - "@smithy/types" "^3.3.0" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@smithy/node-config-provider@^3.1.4": - version "3.1.4" - resolved "https://registry.yarnpkg.com/@smithy/node-config-provider/-/node-config-provider-3.1.4.tgz#05647bed666aa8036a1ad72323c1942e5d421be1" - integrity sha512-YvnElQy8HR4vDcAjoy7Xkx9YT8xZP4cBXcbJSgm/kxmiQu08DwUwj8rkGnyoJTpfl/3xYHH+d8zE+eHqoDCSdQ== +"@smithy/node-config-provider@^3.1.8": + version "3.1.8" + resolved "https://registry.yarnpkg.com/@smithy/node-config-provider/-/node-config-provider-3.1.8.tgz#2c1092040b4062eae0f7c9e121cc00ac6a77efee" + integrity sha512-E0rU0DglpeJn5ge64mk8wTGEXcQwmpUTY5Zr7IzTpDLmHKiIamINERNZYrPQjg58Ck236sEKSwRSHA4CwshU6Q== dependencies: - "@smithy/property-provider" "^3.1.3" - "@smithy/shared-ini-file-loader" "^3.1.4" - "@smithy/types" "^3.3.0" + "@smithy/property-provider" "^3.1.7" + "@smithy/shared-ini-file-loader" "^3.1.8" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@smithy/node-http-handler@^3.1.4": - version "3.1.4" - resolved "https://registry.yarnpkg.com/@smithy/node-http-handler/-/node-http-handler-3.1.4.tgz#be4195e45639e690d522cd5f11513ea822ff9d5f" - integrity sha512-+UmxgixgOr/yLsUxcEKGH0fMNVteJFGkmRltYFHnBMlogyFdpzn2CwqWmxOrfJELhV34v0WSlaqG1UtE1uXlJg== +"@smithy/node-http-handler@^3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@smithy/node-http-handler/-/node-http-handler-3.2.4.tgz#3c57c40d082c3bacac1e49955bd1240e8ccc40b2" + integrity sha512-49reY3+JgLMFNm7uTAKBWiKCA6XSvkNp9FqhVmusm2jpVnHORYFeFZ704LShtqWfjZW/nhX+7Iexyb6zQfXYIQ== dependencies: - "@smithy/abort-controller" "^3.1.1" - "@smithy/protocol-http" "^4.1.0" - "@smithy/querystring-builder" "^3.0.3" - "@smithy/types" "^3.3.0" + "@smithy/abort-controller" "^3.1.5" + "@smithy/protocol-http" "^4.1.4" + "@smithy/querystring-builder" "^3.0.7" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@smithy/property-provider@^3.1.3": - version "3.1.3" - resolved "https://registry.yarnpkg.com/@smithy/property-provider/-/property-provider-3.1.3.tgz#afd57ea82a3f6c79fbda95e3cb85c0ee0a79f39a" - integrity sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g== +"@smithy/property-provider@^3.1.7": + version "3.1.7" + resolved "https://registry.yarnpkg.com/@smithy/property-provider/-/property-provider-3.1.7.tgz#8a304a4b9110a067a93c784e4c11e175f82da379" + integrity sha512-QfzLi1GPMisY7bAM5hOUqBdGYnY5S2JAlr201pghksrQv139f8iiiMalXtjczIP5f6owxFn3MINLNUNvUkgtPw== dependencies: - "@smithy/types" "^3.3.0" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@smithy/protocol-http@^4.1.0": - version "4.1.0" - resolved "https://registry.yarnpkg.com/@smithy/protocol-http/-/protocol-http-4.1.0.tgz#23519d8f45bf4f33960ea5415847bc2b620a010b" - integrity sha512-dPVoHYQ2wcHooGXg3LQisa1hH0e4y0pAddPMeeUPipI1tEOqL6A4N0/G7abeq+K8wrwSgjk4C0wnD1XZpJm5aA== +"@smithy/protocol-http@^4.1.4": + version "4.1.4" + resolved "https://registry.yarnpkg.com/@smithy/protocol-http/-/protocol-http-4.1.4.tgz#6940d652b1825bda2422163ec9baab552669a338" + integrity sha512-MlWK8eqj0JlpZBnWmjQLqmFp71Ug00P+m72/1xQB3YByXD4zZ+y9N4hYrR0EDmrUCZIkyATWHOXFgtavwGDTzQ== dependencies: - "@smithy/types" "^3.3.0" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@smithy/querystring-builder@^3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@smithy/querystring-builder/-/querystring-builder-3.0.3.tgz#6b0e566f885bb84938d077c69e8f8555f686af13" - integrity sha512-vyWckeUeesFKzCDaRwWLUA1Xym9McaA6XpFfAK5qI9DKJ4M33ooQGqvM4J+LalH4u/Dq9nFiC8U6Qn1qi0+9zw== +"@smithy/querystring-builder@^3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@smithy/querystring-builder/-/querystring-builder-3.0.7.tgz#8c443c65f4249ff1637088db1166d18411d41555" + integrity sha512-65RXGZZ20rzqqxTsChdqSpbhA6tdt5IFNgG6o7e1lnPVLCe6TNWQq4rTl4N87hTDD8mV4IxJJnvyE7brbnRkQw== dependencies: - "@smithy/types" "^3.3.0" + "@smithy/types" "^3.5.0" "@smithy/util-uri-escape" "^3.0.0" tslib "^2.6.2" -"@smithy/querystring-parser@^3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@smithy/querystring-parser/-/querystring-parser-3.0.3.tgz#272a6b83f88dfcbbec8283d72a6bde850cc00091" - integrity sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ== +"@smithy/querystring-parser@^3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@smithy/querystring-parser/-/querystring-parser-3.0.7.tgz#936206d1e6da9d862384dae730b4bad042d6a948" + integrity sha512-Fouw4KJVWqqUVIu1gZW8BH2HakwLz6dvdrAhXeXfeymOBrZw+hcqaWs+cS1AZPVp4nlbeIujYrKA921ZW2WMPA== dependencies: - "@smithy/types" "^3.3.0" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@smithy/service-error-classification@^3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@smithy/service-error-classification/-/service-error-classification-3.0.3.tgz#73484255060a094aa9372f6cd972dcaf97e3ce80" - integrity sha512-Jn39sSl8cim/VlkLsUhRFq/dKDnRUFlfRkvhOJaUbLBXUsLRLNf9WaxDv/z9BjuQ3A6k/qE8af1lsqcwm7+DaQ== +"@smithy/service-error-classification@^3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@smithy/service-error-classification/-/service-error-classification-3.0.7.tgz#5bab4ad802d30bd3fa52b8134f6c171582358226" + integrity sha512-91PRkTfiBf9hxkIchhRKJfl1rsplRDyBnmyFca3y0Z3x/q0JJN480S83LBd8R6sBCkm2bBbqw2FHp0Mbh+ecSA== dependencies: - "@smithy/types" "^3.3.0" + "@smithy/types" "^3.5.0" -"@smithy/shared-ini-file-loader@^3.1.4": - version "3.1.4" - resolved "https://registry.yarnpkg.com/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.4.tgz#7dceaf5a5307a2ee347ace8aba17312a1a3ede15" - integrity sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ== +"@smithy/shared-ini-file-loader@^3.1.8": + version "3.1.8" + resolved "https://registry.yarnpkg.com/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.8.tgz#7a0bf5f20cfe8e0c4a36d8dcab8194d0d2ee958e" + integrity sha512-0NHdQiSkeGl0ICQKcJQ2lCOKH23Nb0EaAa7RDRId6ZqwXkw4LJyIyZ0t3iusD4bnKYDPLGy2/5e2rfUhrt0Acw== dependencies: - "@smithy/types" "^3.3.0" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@smithy/signature-v4@^4.1.0": - version "4.1.0" - resolved "https://registry.yarnpkg.com/@smithy/signature-v4/-/signature-v4-4.1.0.tgz#251ff43dc1f4ad66776122732fea9e56efc56443" - integrity sha512-aRryp2XNZeRcOtuJoxjydO6QTaVhxx/vjaR+gx7ZjaFgrgPRyZ3HCTbfwqYj6ZWEBHkCSUfcaymKPURaByukag== +"@smithy/signature-v4@^4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@smithy/signature-v4/-/signature-v4-4.2.0.tgz#291f5a0e756cc251377e1e8af2a1f494e6173029" + integrity sha512-LafbclHNKnsorMgUkKm7Tk7oJ7xizsZ1VwqhGKqoCIrXh4fqDDp73fK99HOEEgcsQbtemmeY/BPv0vTVYYUNEQ== dependencies: "@smithy/is-array-buffer" "^3.0.0" - "@smithy/protocol-http" "^4.1.0" - "@smithy/types" "^3.3.0" + "@smithy/protocol-http" "^4.1.4" + "@smithy/types" "^3.5.0" "@smithy/util-hex-encoding" "^3.0.0" - "@smithy/util-middleware" "^3.0.3" + "@smithy/util-middleware" "^3.0.7" "@smithy/util-uri-escape" "^3.0.0" "@smithy/util-utf8" "^3.0.0" tslib "^2.6.2" -"@smithy/smithy-client@^3.1.12": - version "3.1.12" - resolved "https://registry.yarnpkg.com/@smithy/smithy-client/-/smithy-client-3.1.12.tgz#fb6386816ff8a5c50eab7503d4ee3ba2e4ebac63" - integrity sha512-wtm8JtsycthkHy1YA4zjIh2thJgIQ9vGkoR639DBx5lLlLNU0v4GARpQZkr2WjXue74nZ7MiTSWfVrLkyD8RkA== - dependencies: - "@smithy/middleware-endpoint" "^3.1.0" - "@smithy/middleware-stack" "^3.0.3" - "@smithy/protocol-http" "^4.1.0" - "@smithy/types" "^3.3.0" - "@smithy/util-stream" "^3.1.3" +"@smithy/smithy-client@^3.4.0": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@smithy/smithy-client/-/smithy-client-3.4.0.tgz#ceffb92108a4ad60cbede3baf44ed224dc70b333" + integrity sha512-nOfJ1nVQsxiP6srKt43r2My0Gp5PLWCW2ASqUioxIiGmu6d32v4Nekidiv5qOmmtzIrmaD+ADX5SKHUuhReeBQ== + dependencies: + "@smithy/middleware-endpoint" "^3.1.4" + "@smithy/middleware-stack" "^3.0.7" + "@smithy/protocol-http" "^4.1.4" + "@smithy/types" "^3.5.0" + "@smithy/util-stream" "^3.1.9" tslib "^2.6.2" -"@smithy/types@^3.3.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/@smithy/types/-/types-3.3.0.tgz#fae037c733d09bc758946a01a3de0ef6e210b16b" - integrity sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA== +"@smithy/types@^3.5.0": + version "3.5.0" + resolved "https://registry.yarnpkg.com/@smithy/types/-/types-3.5.0.tgz#9589e154c50d9c5d00feb7d818112ef8fc285d6e" + integrity sha512-QN0twHNfe8mNJdH9unwsCK13GURU7oEAZqkBI+rsvpv1jrmserO+WnLE7jidR9W/1dxwZ0u/CB01mV2Gms/K2Q== dependencies: tslib "^2.6.2" -"@smithy/url-parser@^3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@smithy/url-parser/-/url-parser-3.0.3.tgz#e8a060d9810b24b1870385fc2b02485b8a6c5955" - integrity sha512-pw3VtZtX2rg+s6HMs6/+u9+hu6oY6U7IohGhVNnjbgKy86wcIsSZwgHrFR+t67Uyxvp4Xz3p3kGXXIpTNisq8A== +"@smithy/url-parser@^3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@smithy/url-parser/-/url-parser-3.0.7.tgz#9d7d7e4e38514bf75ade6e8a30d2300f3db17d1b" + integrity sha512-70UbSSR8J97c1rHZOWhl+VKiZDqHWxs/iW8ZHrHp5fCCPLSBE7GcUlUvKSle3Ca+J9LLbYCj/A79BxztBvAfpA== dependencies: - "@smithy/querystring-parser" "^3.0.3" - "@smithy/types" "^3.3.0" + "@smithy/querystring-parser" "^3.0.7" + "@smithy/types" "^3.5.0" tslib "^2.6.2" "@smithy/util-base64@^3.0.0": @@ -3222,37 +3458,37 @@ dependencies: tslib "^2.6.2" -"@smithy/util-defaults-mode-browser@^3.0.14": - version "3.0.14" - resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.14.tgz#21f3ebcb07b9d6ae1274b9d655c38bdac59e5c06" - integrity sha512-0iwTgKKmAIf+vFLV8fji21Jb2px11ktKVxbX6LIDPAUJyWQqGqBVfwba7xwa1f2FZUoolYQgLvxQEpJycXuQ5w== +"@smithy/util-defaults-mode-browser@^3.0.23": + version "3.0.23" + resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.23.tgz#6920b473126ae8857a04dd6941793bbda12adc8b" + integrity sha512-Y07qslyRtXDP/C5aWKqxTPBl4YxplEELG3xRrz2dnAQ6Lq/FgNrcKWmV561nNaZmFH+EzeGOX3ZRMbU8p1T6Nw== dependencies: - "@smithy/property-provider" "^3.1.3" - "@smithy/smithy-client" "^3.1.12" - "@smithy/types" "^3.3.0" + "@smithy/property-provider" "^3.1.7" + "@smithy/smithy-client" "^3.4.0" + "@smithy/types" "^3.5.0" bowser "^2.11.0" tslib "^2.6.2" -"@smithy/util-defaults-mode-node@^3.0.14": - version "3.0.14" - resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.14.tgz#6bb9e837282e84bbf5093dbcd120fcd296593f7a" - integrity sha512-e9uQarJKfXApkTMMruIdxHprhcXivH1flYCe8JRDTzkkLx8dA3V5J8GZlST9yfDiRWkJpZJlUXGN9Rc9Ade3OQ== - dependencies: - "@smithy/config-resolver" "^3.0.5" - "@smithy/credential-provider-imds" "^3.2.0" - "@smithy/node-config-provider" "^3.1.4" - "@smithy/property-provider" "^3.1.3" - "@smithy/smithy-client" "^3.1.12" - "@smithy/types" "^3.3.0" +"@smithy/util-defaults-mode-node@^3.0.23": + version "3.0.23" + resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.23.tgz#d03d21816e8b2f586ccf4a87cd0b1cc55b4d75e0" + integrity sha512-9Y4WH7f0vnDGuHUa4lGX9e2p+sMwODibsceSV6rfkZOvMC+BY3StB2LdO1NHafpsyHJLpwAgChxQ38tFyd6vkg== + dependencies: + "@smithy/config-resolver" "^3.0.9" + "@smithy/credential-provider-imds" "^3.2.4" + "@smithy/node-config-provider" "^3.1.8" + "@smithy/property-provider" "^3.1.7" + "@smithy/smithy-client" "^3.4.0" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@smithy/util-endpoints@^2.0.5": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@smithy/util-endpoints/-/util-endpoints-2.0.5.tgz#e3a7a4d1c41250bfd2b2d890d591273a7d8934be" - integrity sha512-ReQP0BWihIE68OAblC/WQmDD40Gx+QY1Ez8mTdFMXpmjfxSyz2fVQu3A4zXRfQU9sZXtewk3GmhfOHswvX+eNg== +"@smithy/util-endpoints@^2.1.3": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@smithy/util-endpoints/-/util-endpoints-2.1.3.tgz#7498151e9dc714bdd0c6339314dd2350fa4d250a" + integrity sha512-34eACeKov6jZdHqS5hxBMJ4KyWKztTMulhuQ2UdOoP6vVxMLrOKUqIXAwJe/wiWMhXhydLW664B02CNpQBQ4Aw== dependencies: - "@smithy/node-config-provider" "^3.1.4" - "@smithy/types" "^3.3.0" + "@smithy/node-config-provider" "^3.1.8" + "@smithy/types" "^3.5.0" tslib "^2.6.2" "@smithy/util-hex-encoding@^3.0.0": @@ -3262,31 +3498,31 @@ dependencies: tslib "^2.6.2" -"@smithy/util-middleware@^3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@smithy/util-middleware/-/util-middleware-3.0.3.tgz#07bf9602682f5a6c55bc2f0384303f85fc68c87e" - integrity sha512-l+StyYYK/eO3DlVPbU+4Bi06Jjal+PFLSMmlWM1BEwyLxZ3aKkf1ROnoIakfaA7mC6uw3ny7JBkau4Yc+5zfWw== +"@smithy/util-middleware@^3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@smithy/util-middleware/-/util-middleware-3.0.7.tgz#770d09749b6d170a1641384a2e961487447446fa" + integrity sha512-OVA6fv/3o7TMJTpTgOi1H5OTwnuUa8hzRzhSFDtZyNxi6OZ70L/FHattSmhE212I7b6WSOJAAmbYnvcjTHOJCA== dependencies: - "@smithy/types" "^3.3.0" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@smithy/util-retry@^3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@smithy/util-retry/-/util-retry-3.0.3.tgz#9b2ac0dbb1c81f69812a8affa4d772bebfc0e049" - integrity sha512-AFw+hjpbtVApzpNDhbjNG5NA3kyoMs7vx0gsgmlJF4s+yz1Zlepde7J58zpIRIsdjc+emhpAITxA88qLkPF26w== +"@smithy/util-retry@^3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@smithy/util-retry/-/util-retry-3.0.7.tgz#694e0667574ffe9772f620b35d3c7286aced35e9" + integrity sha512-nh1ZO1vTeo2YX1plFPSe/OXaHkLAHza5jpokNiiKX2M5YpNUv6RxGJZhpfmiR4jSvVHCjIDmILjrxKmP+/Ghug== dependencies: - "@smithy/service-error-classification" "^3.0.3" - "@smithy/types" "^3.3.0" + "@smithy/service-error-classification" "^3.0.7" + "@smithy/types" "^3.5.0" tslib "^2.6.2" -"@smithy/util-stream@^3.1.3": - version "3.1.3" - resolved "https://registry.yarnpkg.com/@smithy/util-stream/-/util-stream-3.1.3.tgz#699ee2397cc1d474e46d2034039d5263812dca64" - integrity sha512-FIv/bRhIlAxC0U7xM1BCnF2aDRPq0UaelqBHkM2lsCp26mcBbgI0tCVTv+jGdsQLUmAMybua/bjDsSu8RQHbmw== +"@smithy/util-stream@^3.1.9": + version "3.1.9" + resolved "https://registry.yarnpkg.com/@smithy/util-stream/-/util-stream-3.1.9.tgz#d39656eae27696bdc5a3ec7c2f6b89c32dccd1ca" + integrity sha512-7YAR0Ub3MwTMjDfjnup4qa6W8gygZMxikBhFMPESi6ASsl/rZJhwLpF/0k9TuezScCojsM0FryGdz4LZtjKPPQ== dependencies: - "@smithy/fetch-http-handler" "^3.2.4" - "@smithy/node-http-handler" "^3.1.4" - "@smithy/types" "^3.3.0" + "@smithy/fetch-http-handler" "^3.2.9" + "@smithy/node-http-handler" "^3.2.4" + "@smithy/types" "^3.5.0" "@smithy/util-base64" "^3.0.0" "@smithy/util-buffer-from" "^3.0.0" "@smithy/util-hex-encoding" "^3.0.0" @@ -3316,15 +3552,132 @@ "@smithy/util-buffer-from" "^3.0.0" tslib "^2.6.2" -"@smithy/util-waiter@^3.1.2": - version "3.1.2" - resolved "https://registry.yarnpkg.com/@smithy/util-waiter/-/util-waiter-3.1.2.tgz#2d40c3312f3537feee763459a19acafab4c75cf3" - integrity sha512-4pP0EV3iTsexDx+8PPGAKCQpd/6hsQBaQhqWzU4hqKPHN5epPsxKbvUTIiYIHTxaKt6/kEaqPBpu/ufvfbrRzw== +"@smithy/util-waiter@^3.1.6": + version "3.1.6" + resolved "https://registry.yarnpkg.com/@smithy/util-waiter/-/util-waiter-3.1.6.tgz#c65870d0c802e33b96112fac5c4471b3bf2eeecb" + integrity sha512-xs/KAwWOeCklq8aMlnpk25LgxEYHKOEodfjfKclDMLcBJEVEKzDLxZxBQyztcuPJ7F54213NJS8PxoiHNMdItQ== dependencies: - "@smithy/abort-controller" "^3.1.1" - "@smithy/types" "^3.3.0" + "@smithy/abort-controller" "^3.1.5" + "@smithy/types" "^3.5.0" tslib "^2.6.2" +"@swc/cli@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@swc/cli/-/cli-0.5.0.tgz#9bb515304abbced57c406c54f4ddc3a53999f8ad" + integrity sha512-eFsrNt85SbHTeX6svpBNcA5DQLP/wrSyCs3KVZjbuEHWD7JGpajZOIwH74lVhyrmrXOcGxgbnxXEbDIfRlLcSw== + dependencies: + "@mole-inc/bin-wrapper" "^8.0.1" + "@swc/counter" "^0.1.3" + commander "^8.3.0" + fast-glob "^3.2.5" + minimatch "^9.0.3" + piscina "^4.3.0" + semver "^7.3.8" + slash "3.0.0" + source-map "^0.7.3" + +"@swc/core-darwin-arm64@1.7.42": + version "1.7.42" + resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.7.42.tgz#a3c59cb3e487f660ca87d6b78e81b2d7193bada9" + integrity sha512-fWhaCs2+8GDRIcjExVDEIfbptVrxDqG8oHkESnXgymmvqTWzWei5SOnPNMS8Q+MYsn/b++Y2bDxkcwmq35Bvxg== + +"@swc/core-darwin-x64@1.7.42": + version "1.7.42" + resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.7.42.tgz#a4ce0e1ff52a238b6b00e2f73428474a65fa2f7c" + integrity sha512-ZaVHD2bijrlkCyD7NDzLmSK849Jgcx+6DdL4x1dScoz1slJ8GTvLtEu0JOUaaScQwA+cVlhmrmlmi9ssjbRLGQ== + +"@swc/core-linux-arm-gnueabihf@1.7.42": + version "1.7.42" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.7.42.tgz#cb70c3f26e020ebf1efecbde508936a6e90cdcf9" + integrity sha512-iF0BJj7hVTbY/vmbvyzVTh/0W80+Q4fbOYschdUM3Bsud39TA+lSaPOefOHywkNH58EQ1z3EAxYcJOWNES7GFQ== + +"@swc/core-linux-arm64-gnu@1.7.42": + version "1.7.42" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.7.42.tgz#93a18284c29788ec2ce7fb0ef821b864a6df122e" + integrity sha512-xGu8j+DOLYTLkVmsfZPJbNPW1EkiWgSucT0nOlz77bLxImukt/0+HVm2hOwHSKuArQ8C3cjahAMY3b/s4VH2ww== + +"@swc/core-linux-arm64-musl@1.7.42": + version "1.7.42" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.7.42.tgz#c1346dbaf75c72704b0ab7d0cb9db2f1fa060f24" + integrity sha512-qtW3JNO7i1yHEko59xxz+jY38+tYmB96JGzj6XzygMbYJYZDYbrOpXQvKbMGNG3YeTDan7Fp2jD0dlKf7NgDPA== + +"@swc/core-linux-x64-gnu@1.7.42": + version "1.7.42" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.7.42.tgz#a3c09eaf389030f1317c48511914de9aee6b85fb" + integrity sha512-F9WY1TN+hhhtiEzZjRQziNLt36M5YprMeOBHjsLVNqwgflzleSI7ulgnlQECS8c8zESaXj3ksGduAoJYtPC1cA== + +"@swc/core-linux-x64-musl@1.7.42": + version "1.7.42" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.7.42.tgz#99a5c4f1e2ec41707dd5c263c286704856527ecd" + integrity sha512-7YMdOaYKLMQ8JGfnmRDwidpLFs/6ka+80zekeM0iCVO48yLrJR36G0QGXzMjKsXI0BPhq+mboZRRENK4JfQnEA== + +"@swc/core-win32-arm64-msvc@1.7.42": + version "1.7.42" + resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.7.42.tgz#6803ff002a931604b31260052841a1bf94707fda" + integrity sha512-C5CYWaIZEyqPl5W/EwcJ/mLBJFHVoUEa/IwWi0b4q2fCXcSCktQGwKXOQ+d67GneiZoiq0HasgcdMmMpGS9YRQ== + +"@swc/core-win32-ia32-msvc@1.7.42": + version "1.7.42" + resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.7.42.tgz#9379dece3fd5089daf8d0defdbce3b981909a563" + integrity sha512-3j47seZ5pO62mbrqvPe1iwhe2BXnM5q7iB+n2xgA38PCGYt0mnaJafqmpCXm/uYZOCMqSNynaoOWCMMZm4sqtA== + +"@swc/core-win32-x64-msvc@1.7.42": + version "1.7.42" + resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.7.42.tgz#f779952389ad91145d71455da6165a779adaa9e0" + integrity sha512-FXl9MdeUogZLGDcLr6QIRdDVkpG0dkN4MLM4dwQ5kcAk+XfKPrQibX6M2kcfhsCx+jtBqtK7hRFReRXPWJZGbA== + +"@swc/core@^1.7.42": + version "1.7.42" + resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.7.42.tgz#8053e2ede08207d67b3ffef38c1c3d2befeb8848" + integrity sha512-iQrRk3SKndQZ4ptJv1rzeQSiCYQIhMjiO97QXOlCcCoaazOLKPnLnXzU4Kv0FuBFyYfG2FE94BoR0XI2BN02qw== + dependencies: + "@swc/counter" "^0.1.3" + "@swc/types" "^0.1.13" + optionalDependencies: + "@swc/core-darwin-arm64" "1.7.42" + "@swc/core-darwin-x64" "1.7.42" + "@swc/core-linux-arm-gnueabihf" "1.7.42" + "@swc/core-linux-arm64-gnu" "1.7.42" + "@swc/core-linux-arm64-musl" "1.7.42" + "@swc/core-linux-x64-gnu" "1.7.42" + "@swc/core-linux-x64-musl" "1.7.42" + "@swc/core-win32-arm64-msvc" "1.7.42" + "@swc/core-win32-ia32-msvc" "1.7.42" + "@swc/core-win32-x64-msvc" "1.7.42" + +"@swc/counter@^0.1.3": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@swc/counter/-/counter-0.1.3.tgz#cc7463bd02949611c6329596fccd2b0ec782b0e9" + integrity sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ== + +"@swc/jest@^0.2.37": + version "0.2.37" + resolved "https://registry.yarnpkg.com/@swc/jest/-/jest-0.2.37.tgz#9c2aaf22c87682aa968016e3e4843d1a25cae6bd" + integrity sha512-CR2BHhmXKGxTiFr21DYPRHQunLkX3mNIFGFkxBGji6r9uyIR5zftTOVYj1e0sFNMV2H7mf/+vpaglqaryBtqfQ== + dependencies: + "@jest/create-cache-key-function" "^29.7.0" + "@swc/counter" "^0.1.3" + jsonc-parser "^3.2.0" + +"@swc/types@^0.1.13": + version "0.1.13" + resolved "https://registry.yarnpkg.com/@swc/types/-/types-0.1.13.tgz#441734f8bfa6e9e738f1c68e98be6da282ecc7db" + integrity sha512-JL7eeCk6zWCbiYQg2xQSdLXQJl8Qoc9rXmG2cEKvHe3CKwMHwHGpfOb8frzNLmbycOo6I51qxnLnn9ESf4I20Q== + dependencies: + "@swc/counter" "^0.1.3" + +"@szmarczak/http-timer@^4.0.5": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807" + integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w== + dependencies: + defer-to-connect "^2.0.0" + +"@tokenizer/token@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@tokenizer/token/-/token-0.3.0.tgz#fe98a93fe789247e998c75e74e9c7c63217aa276" + integrity sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A== + "@ts-morph/common@~0.12.3": version "0.12.3" resolved "https://registry.npmjs.org/@ts-morph/common/-/common-0.12.3.tgz" @@ -3406,6 +3759,23 @@ resolved "https://registry.npmjs.org/@types/bytes/-/bytes-3.1.4.tgz" integrity sha512-A0uYgOj3zNc4hNjHc5lYUfJQ/HVyBXiUMKdXd7ysclaE6k9oJdavQzODHuwjpUu2/boCP8afjQYi8z/GtvNCWA== +"@types/cacheable-request@^6.0.1": + version "6.0.3" + resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz#a430b3260466ca7b5ca5bfd735693b36e7a9d183" + integrity sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw== + dependencies: + "@types/http-cache-semantics" "*" + "@types/keyv" "^3.1.4" + "@types/node" "*" + "@types/responselike" "^1.0.0" + +"@types/compression@^1.7.5": + version "1.7.5" + resolved "https://registry.yarnpkg.com/@types/compression/-/compression-1.7.5.tgz#0f80efef6eb031be57b12221c4ba6bc3577808f7" + integrity sha512-AAQvK5pxMpaT+nDvhHrsBhLSYG5yQdtkaJE1WYieSNY2mVFKAgmU4ks65rkZD5oqnGCFLyQpUr1CqI4DmUMyDg== + dependencies: + "@types/express" "*" + "@types/connect@*": version "3.4.35" resolved "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz" @@ -3444,14 +3814,6 @@ resolved "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.2.2.tgz" integrity sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ== -"@types/eslint-scope@^3.7.3": - version "3.7.4" - resolved "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz" - integrity sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA== - dependencies: - "@types/eslint" "*" - "@types/estree" "*" - "@types/eslint@*": version "8.4.5" resolved "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.5.tgz" @@ -3477,6 +3839,11 @@ resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz" integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== +"@types/estree@^1.0.6": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" + integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== + "@types/express-serve-static-core@^4.17.18": version "4.17.29" resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.29.tgz" @@ -3495,6 +3862,16 @@ "@types/qs" "*" "@types/range-parser" "*" +"@types/express-serve-static-core@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-5.0.0.tgz#91f06cda1049e8f17eeab364798ed79c97488a1c" + integrity sha512-AbXMTZGt40T+KON9/Fdxx0B2WK5hsgxcfXJLr5bFpZ7b4JCex2WyQPTEKdXqfHiY5nKKBScZ7yCoO6Pvgxfvnw== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/send" "*" + "@types/express@*": version "4.17.13" resolved "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz" @@ -3505,7 +3882,7 @@ "@types/qs" "*" "@types/serve-static" "*" -"@types/express@^4.17.17", "@types/express@^4.17.21": +"@types/express@^4.17.17": version "4.17.21" resolved "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz" integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ== @@ -3515,6 +3892,16 @@ "@types/qs" "*" "@types/serve-static" "*" +"@types/express@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@types/express/-/express-5.0.0.tgz#13a7d1f75295e90d19ed6e74cab3678488eaa96c" + integrity sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^5.0.0" + "@types/qs" "*" + "@types/serve-static" "*" + "@types/graceful-fs@^4.1.3": version "4.1.5" resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz" @@ -3522,6 +3909,11 @@ dependencies: "@types/node" "*" +"@types/http-cache-semantics@*": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4" + integrity sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA== + "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.4" resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz" @@ -3541,10 +3933,10 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@^29.5.12": - version "29.5.12" - resolved "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz" - integrity sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw== +"@types/jest@^29.5.14": + version "29.5.14" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.14.tgz#2b910912fa1d6856cadcd0c1f95af7df1d6049e5" + integrity sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ== dependencies: expect "^29.0.0" pretty-format "^29.0.0" @@ -3554,6 +3946,11 @@ resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz" integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== +"@types/json-schema@^7.0.15": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + "@types/jsonwebtoken@*": version "8.5.8" resolved "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.8.tgz" @@ -3575,10 +3972,22 @@ dependencies: "@types/node" "*" -"@types/lodash@^4.17.7": - version "4.17.7" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.7.tgz#2f776bcb53adc9e13b2c0dfd493dfcbd7de43612" - integrity sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA== +"@types/keyv@^3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6" + integrity sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg== + dependencies: + "@types/node" "*" + +"@types/lodash@^4.17.13": + version "4.17.13" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.13.tgz#786e2d67cfd95e32862143abe7463a7f90c300eb" + integrity sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg== + +"@types/luxon@^3.4.2": + version "3.4.2" + resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.4.2.tgz#e4fc7214a420173cea47739c33cdf10874694db7" + integrity sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA== "@types/methods@^1.1.4": version "1.1.4" @@ -3595,17 +4004,17 @@ resolved "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz" integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g== -"@types/multer@^1.4.11": - version "1.4.11" - resolved "https://registry.npmjs.org/@types/multer/-/multer-1.4.11.tgz" - integrity sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w== +"@types/multer@^1.4.12": + version "1.4.12" + resolved "https://registry.yarnpkg.com/@types/multer/-/multer-1.4.12.tgz#da67bd0c809f3a63fe097c458c0d4af1fea50ab7" + integrity sha512-pQ2hoqvXiJt2FP9WQVLPRO+AmiIm/ZYkavPlIQnx282u4ZrVdztx0pkh3jjpQt0Kz+YI0YhSG264y08UJKoUQg== dependencies: "@types/express" "*" -"@types/mysql@2.15.22": - version "2.15.22" - resolved "https://registry.yarnpkg.com/@types/mysql/-/mysql-2.15.22.tgz#8705edb9872bf4aa9dbc004cd494e00334e5cdb4" - integrity sha512-wK1pzsJVVAjYCSZWQoWHziQZbNggXFDUEIGf54g4ZM/ERuP86uGdWeKZWMYlqTPMZfHJJvLPyogXGvCOg87yLQ== +"@types/mysql@2.15.26": + version "2.15.26" + resolved "https://registry.yarnpkg.com/@types/mysql/-/mysql-2.15.26.tgz#f0de1484b9e2354d587e7d2bd17a873cc8300836" + integrity sha512-DSLCOXhkvfS5WNNPbfn2KdICAmk8lLc+/PNvnPnF7gOdMZCxopXduqv0OQ13y/yA/zXTSikZZqVgybUxOEg6YQ== dependencies: "@types/node" "*" @@ -3614,12 +4023,12 @@ resolved "https://registry.npmjs.org/@types/node/-/node-18.0.3.tgz" integrity sha512-HzNRZtp4eepNitP+BD6k2L6DROIDG4Q0fm4x+dwfsr6LGmROENnok75VGw40628xf+iR24WeMFcHuuBDUAzzsQ== -"@types/node@^22.2.0": - version "22.2.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.2.0.tgz#7cf046a99f0ba4d628ad3088cb21f790df9b0c5b" - integrity sha512-bm6EG6/pCpkxDf/0gDNDdtDILMOHgaQBVOJGdwsqClnxA3xL6jtMv76rLBc006RVMWbmaf0xbmom4Z/5o2nRkQ== +"@types/node@^22.8.7": + version "22.8.7" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.8.7.tgz#04ab7a073d95b4a6ee899f235d43f3c320a976f4" + integrity sha512-LidcG+2UeYIWcMuMUpBKOnryBWG/rnmOHQR5apjn8myTQcx3rinFRn7DcIFhMnS0PPFSC6OafdIKEad0lj6U0Q== dependencies: - undici-types "~6.13.0" + undici-types "~6.19.8" "@types/parse-json@^4.0.0": version "4.0.0" @@ -3649,10 +4058,10 @@ dependencies: "@types/express" "*" -"@types/pg-pool@2.0.4": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@types/pg-pool/-/pg-pool-2.0.4.tgz#b5c60f678094ff3acf3442628a7f708928fcf263" - integrity sha512-qZAvkv1K3QbmHHFYSNRYPkRjOWRLBYrL4B9c+wG0GSVGBw0NtJwPcgx/DSddeDJvRGMHCEQ4VMEVfuJ/0gZ3XQ== +"@types/pg-pool@2.0.6": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@types/pg-pool/-/pg-pool-2.0.6.tgz#1376d9dc5aec4bb2ec67ce28d7e9858227403c77" + integrity sha512-TaAUE5rq2VQYxab5Ts7WZhKNmuN78Q6PiFonTDdpbx8a1H0M1vhy3rhiMjl+e2iHmogyMw7jZF4FrE6eJUy5HQ== dependencies: "@types/pg" "*" @@ -3692,6 +4101,21 @@ "@types/express" "*" "@types/node" "*" +"@types/responselike@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.3.tgz#cc29706f0a397cfe6df89debfe4bf5cea159db50" + integrity sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw== + dependencies: + "@types/node" "*" + +"@types/send@*": + version "0.17.4" + resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a" + integrity sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + "@types/serve-static@*": version "1.13.10" resolved "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz" @@ -3705,6 +4129,11 @@ resolved "https://registry.yarnpkg.com/@types/shimmer/-/shimmer-1.0.5.tgz#491d8984d4510e550bfeb02d518791d7f59d2b88" integrity sha512-9Hp0ObzwwO57DpLFF0InUjUm/II8GmKAvzbefxQTihCb7KI6yc9yzf0nLc4mVdby5N4DRCgQM2wCup9KTieeww== +"@types/shimmer@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@types/shimmer/-/shimmer-1.2.0.tgz#9b706af96fa06416828842397a70dfbbf1c14ded" + integrity sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg== + "@types/stack-utils@^2.0.0": version "2.0.1" resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz" @@ -3761,85 +4190,85 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@8.1.0": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.1.0.tgz#3c020deeaaba82a6f741d00dacf172c53be4911f" - integrity sha512-LlNBaHFCEBPHyD4pZXb35mzjGkuGKXU5eeCA1SxvHfiRES0E82dOounfVpL4DCqYvJEKab0bZIA0gCRpdLKkCw== +"@typescript-eslint/eslint-plugin@8.12.2": + version "8.12.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.12.2.tgz#c2ef660bb83fd1432368319312a2581fc92ccac1" + integrity sha512-gQxbxM8mcxBwaEmWdtLCIGLfixBMHhQjBqR8sVWNTPpcj45WlYL2IObS/DNMLH1DBP0n8qz+aiiLTGfopPEebw== dependencies: "@eslint-community/regexpp" "^4.10.0" - "@typescript-eslint/scope-manager" "8.1.0" - "@typescript-eslint/type-utils" "8.1.0" - "@typescript-eslint/utils" "8.1.0" - "@typescript-eslint/visitor-keys" "8.1.0" + "@typescript-eslint/scope-manager" "8.12.2" + "@typescript-eslint/type-utils" "8.12.2" + "@typescript-eslint/utils" "8.12.2" + "@typescript-eslint/visitor-keys" "8.12.2" graphemer "^1.4.0" ignore "^5.3.1" natural-compare "^1.4.0" ts-api-utils "^1.3.0" -"@typescript-eslint/parser@8.1.0": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.1.0.tgz#b7e77f5fa212df59eba51ecd4986f194bccc2303" - integrity sha512-U7iTAtGgJk6DPX9wIWPPOlt1gO57097G06gIcl0N0EEnNw8RGD62c+2/DiP/zL7KrkqnnqF7gtFGR7YgzPllTA== +"@typescript-eslint/parser@8.12.2": + version "8.12.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.12.2.tgz#2e8173b34e1685e918b2d571c16c906d3747bad2" + integrity sha512-MrvlXNfGPLH3Z+r7Tk+Z5moZAc0dzdVjTgUgwsdGweH7lydysQsnSww3nAmsq8blFuRD5VRlAr9YdEFw3e6PBw== dependencies: - "@typescript-eslint/scope-manager" "8.1.0" - "@typescript-eslint/types" "8.1.0" - "@typescript-eslint/typescript-estree" "8.1.0" - "@typescript-eslint/visitor-keys" "8.1.0" + "@typescript-eslint/scope-manager" "8.12.2" + "@typescript-eslint/types" "8.12.2" + "@typescript-eslint/typescript-estree" "8.12.2" + "@typescript-eslint/visitor-keys" "8.12.2" debug "^4.3.4" -"@typescript-eslint/scope-manager@8.1.0": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.1.0.tgz#dd8987d2efebb71d230a1c71d82e84a7aead5c3d" - integrity sha512-DsuOZQji687sQUjm4N6c9xABJa7fjvfIdjqpSIIVOgaENf2jFXiM9hIBZOL3hb6DHK9Nvd2d7zZnoMLf9e0OtQ== +"@typescript-eslint/scope-manager@8.12.2": + version "8.12.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.12.2.tgz#6db0213745e6392c8e90fe9af5915e6da32eb94a" + integrity sha512-gPLpLtrj9aMHOvxJkSbDBmbRuYdtiEbnvO25bCMza3DhMjTQw0u7Y1M+YR5JPbMsXXnSPuCf5hfq0nEkQDL/JQ== dependencies: - "@typescript-eslint/types" "8.1.0" - "@typescript-eslint/visitor-keys" "8.1.0" + "@typescript-eslint/types" "8.12.2" + "@typescript-eslint/visitor-keys" "8.12.2" -"@typescript-eslint/type-utils@8.1.0": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.1.0.tgz#dbf5a4308166dfc37a36305390dea04a3a3b5048" - integrity sha512-oLYvTxljVvsMnldfl6jIKxTaU7ok7km0KDrwOt1RHYu6nxlhN3TIx8k5Q52L6wR33nOwDgM7VwW1fT1qMNfFIA== +"@typescript-eslint/type-utils@8.12.2": + version "8.12.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.12.2.tgz#132b0c52d45f6814e6f2e32416c7951ed480b016" + integrity sha512-bwuU4TAogPI+1q/IJSKuD4shBLc/d2vGcRT588q+jzayQyjVK2X6v/fbR4InY2U2sgf8MEvVCqEWUzYzgBNcGQ== dependencies: - "@typescript-eslint/typescript-estree" "8.1.0" - "@typescript-eslint/utils" "8.1.0" + "@typescript-eslint/typescript-estree" "8.12.2" + "@typescript-eslint/utils" "8.12.2" debug "^4.3.4" ts-api-utils "^1.3.0" -"@typescript-eslint/types@8.1.0": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.1.0.tgz#fbf1eaa668a7e444ac507732ca9d3c3468e5db9c" - integrity sha512-q2/Bxa0gMOu/2/AKALI0tCKbG2zppccnRIRCW6BaaTlRVaPKft4oVYPp7WOPpcnsgbr0qROAVCVKCvIQ0tbWog== +"@typescript-eslint/types@8.12.2": + version "8.12.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.12.2.tgz#8d70098c0e90442495b53d0296acdca6d0f3f73c" + integrity sha512-VwDwMF1SZ7wPBUZwmMdnDJ6sIFk4K4s+ALKLP6aIQsISkPv8jhiw65sAK6SuWODN/ix+m+HgbYDkH+zLjrzvOA== -"@typescript-eslint/typescript-estree@8.1.0": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.1.0.tgz#c44e5667683c0bb5caa43192e27de6a994f4e4c4" - integrity sha512-NTHhmufocEkMiAord/g++gWKb0Fr34e9AExBRdqgWdVBaKoei2dIyYKD9Q0jBnvfbEA5zaf8plUFMUH6kQ0vGg== +"@typescript-eslint/typescript-estree@8.12.2": + version "8.12.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.12.2.tgz#206df9b1cbff212aaa9401985ef99f04daa84da5" + integrity sha512-mME5MDwGe30Pq9zKPvyduyU86PH7aixwqYR2grTglAdB+AN8xXQ1vFGpYaUSJ5o5P/5znsSBeNcs5g5/2aQwow== dependencies: - "@typescript-eslint/types" "8.1.0" - "@typescript-eslint/visitor-keys" "8.1.0" + "@typescript-eslint/types" "8.12.2" + "@typescript-eslint/visitor-keys" "8.12.2" debug "^4.3.4" - globby "^11.1.0" + fast-glob "^3.3.2" is-glob "^4.0.3" minimatch "^9.0.4" semver "^7.6.0" ts-api-utils "^1.3.0" -"@typescript-eslint/utils@8.1.0": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.1.0.tgz#a922985a43d2560ce0d293be79148fa80c1325e0" - integrity sha512-ypRueFNKTIFwqPeJBfeIpxZ895PQhNyH4YID6js0UoBImWYoSjBsahUn9KMiJXh94uOjVBgHD9AmkyPsPnFwJA== +"@typescript-eslint/utils@8.12.2": + version "8.12.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.12.2.tgz#726cc9f49f5866605bd15bbc1768ffc15637930e" + integrity sha512-UTTuDIX3fkfAz6iSVa5rTuSfWIYZ6ATtEocQ/umkRSyC9O919lbZ8dcH7mysshrCdrAM03skJOEYaBugxN+M6A== dependencies: "@eslint-community/eslint-utils" "^4.4.0" - "@typescript-eslint/scope-manager" "8.1.0" - "@typescript-eslint/types" "8.1.0" - "@typescript-eslint/typescript-estree" "8.1.0" + "@typescript-eslint/scope-manager" "8.12.2" + "@typescript-eslint/types" "8.12.2" + "@typescript-eslint/typescript-estree" "8.12.2" -"@typescript-eslint/visitor-keys@8.1.0": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.1.0.tgz#ab2b3a9699a8ddebf0c205e133f114c1fed9daad" - integrity sha512-ba0lNI19awqZ5ZNKh6wCModMwoZs457StTebQ0q1NP58zSi2F6MOZRXwfKZy+jB78JNJ/WH8GSh2IQNzXX8Nag== +"@typescript-eslint/visitor-keys@8.12.2": + version "8.12.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.12.2.tgz#94d7410f78eb6d134b9fcabaf1eeedb910ba8c38" + integrity sha512-PChz8UaKQAVNHghsHcPyx1OMHoFRUEA7rJSK/mDhdq85bk+PLsUHUBqTQTFt18VJZbmxBovM65fezlheQRsSDA== dependencies: - "@typescript-eslint/types" "8.1.0" + "@typescript-eslint/types" "8.12.2" eslint-visitor-keys "^3.4.3" "@ucast/core@^1.0.0", "@ucast/core@^1.4.1", "@ucast/core@^1.6.1": @@ -4009,6 +4438,13 @@ JSONStream@^1.3.5: jsonparse "^1.2.0" through ">=2.2.7 <3" +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + accept-language-parser@^1.5.0: version "1.5.0" resolved "https://registry.npmjs.org/accept-language-parser/-/accept-language-parser-1.5.0.tgz" @@ -4022,11 +4458,6 @@ accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" -acorn-import-assertions@^1.9.0: - version "1.9.0" - resolved "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz" - integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== - acorn-import-attributes@^1.9.5: version "1.9.5" resolved "https://registry.yarnpkg.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz#7eb1557b1ba05ef18b5ed0ec67591bfab04688ef" @@ -4047,10 +4478,10 @@ acorn@^8.11.3, acorn@^8.8.2: resolved "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz" integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== -acorn@^8.12.0: - version "8.12.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.0.tgz#1627bfa2e058148036133b8d9b51a700663c294c" - integrity sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw== +acorn@^8.14.0: + version "8.14.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" + integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== acorn@^8.4.1: version "8.7.1" @@ -4189,6 +4620,11 @@ append-field@^1.0.0: resolved "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz" integrity sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw== +arch@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" + integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ== + arg@^4.1.0: version "4.1.3" resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz" @@ -4221,16 +4657,16 @@ array-timsort@^1.0.3: resolved "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz" integrity sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ== -array-union@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" - integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== - asap@^2.0.0: version "2.0.6" resolved "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz" integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== +async@3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.3.tgz#ac53dafd3f4720ee9e8a160628f18ea91df196c9" + integrity sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g== + async@^3.2.3: version "3.2.5" resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" @@ -4241,10 +4677,15 @@ asynckit@^0.4.0: resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== -axios@^1.7.3: - version "1.7.3" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.3.tgz#a1125f2faf702bc8e8f2104ec3a76fab40257d85" - integrity sha512-Ar7ND9pU99eJ9GpoGQKhKf58GpUOgnzuaB7ueNQ5BMi0p+LZ5oaEnfF999fAArcTIBwXTCHAmGcHOZJaWPq9Nw== +atomic-sleep@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" + integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== + +axios@^1.7.7: + version "1.7.7" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.7.tgz#2f554296f9892a72ac8d8e4c5b79c14a91d0a47f" + integrity sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q== dependencies: follow-redirects "^1.15.6" form-data "^4.0.0" @@ -4310,11 +4751,6 @@ babel-preset-jest@^29.6.3: babel-plugin-jest-hoist "^29.6.3" babel-preset-current-node-syntax "^1.0.0" -backo2@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz" - integrity sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA== - balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" @@ -4344,6 +4780,31 @@ bignumber.js@^9.0.0: resolved "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz" integrity sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug== +bin-check@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bin-check/-/bin-check-4.1.0.tgz#fc495970bdc88bb1d5a35fc17e65c4a149fc4a49" + integrity sha512-b6weQyEUKsDGFlACWSIOfveEnImkJyK/FGW6FAG42loyoquvjdtOIqO6yBFzHyqyVVhNgNkQxxx09SFLK28YnA== + dependencies: + execa "^0.7.0" + executable "^4.1.0" + +bin-version-check@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/bin-version-check/-/bin-version-check-5.1.0.tgz#788e80e036a87313f8be7908bc20e5abe43f0837" + integrity sha512-bYsvMqJ8yNGILLz1KP9zKLzQ6YpljV3ln1gqhuLkUtyfGi3qXKGuK2p+U4NAvjVFzDFiBBtOpCOSFNuYYEGZ5g== + dependencies: + bin-version "^6.0.0" + semver "^7.5.3" + semver-truncate "^3.0.0" + +bin-version@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/bin-version/-/bin-version-6.0.0.tgz#08ecbe5fc87898b441425e145f9e105064d00315" + integrity sha512-nk5wEsP4RiKjG+vF+uG8lFsEn4d7Y6FVDamzzftSunXOoOcOOkzcWdKVlGgFFwlUQCj63SgnUkLLGF8v7lufhw== + dependencies: + execa "^5.0.0" + find-versions "^5.0.0" + binary-extensions@^2.0.0: version "2.2.0" resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" @@ -4358,10 +4819,10 @@ bl@^4.1.0: inherits "^2.0.4" readable-stream "^3.4.0" -body-parser@1.20.2: - version "1.20.2" - resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz" - integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== +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.2" content-type "~1.0.5" @@ -4371,7 +4832,7 @@ body-parser@1.20.2: http-errors "2.0.0" iconv-lite "0.4.24" on-finished "2.4.1" - qs "6.11.0" + qs "6.13.0" raw-body "2.5.2" type-is "~1.6.18" unpipe "1.0.0" @@ -4444,9 +4905,9 @@ browserslist@^4.21.10: node-releases "^2.0.14" update-browserslist-db "^1.0.13" -bs-logger@0.x: +bs-logger@^0.2.6: version "0.2.6" - resolved "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz" + resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== dependencies: fast-json-stable-stringify "2.x" @@ -4481,10 +4942,18 @@ buffer@^5.5.0: base64-js "^1.3.1" ieee754 "^1.1.13" -bullmq@^5.12.5: - version "5.12.5" - resolved "https://registry.yarnpkg.com/bullmq/-/bullmq-5.12.5.tgz#09486440c50b75f6c9c5cbcbda0ec7540fd03436" - integrity sha512-lchCvFuPdaIbq01qnyS7MOt2piPeCDHzCqIxNAQEgDSzZ+Eb4RBboUUMgmW90UtMjV46mEqsWY9B1l/7/C13SA== +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + +bullmq@^5.23.0: + version "5.23.0" + resolved "https://registry.yarnpkg.com/bullmq/-/bullmq-5.23.0.tgz#05aa7c45f92207125bfb8339546056cfb49eb5e5" + integrity sha512-VILKTIOwo9AopMyVqvDhQ1qyLrOtBSfu+G2bntgauQfxYzT7ETj+h2HeUe7a9i9AU/+OXJGYYm49NHJedEz7VQ== dependencies: cron-parser "^4.6.0" ioredis "^5.4.1" @@ -4518,15 +4987,32 @@ cache-manager-redis-store@^3.0.1: dependencies: redis "^4.3.1" -cache-manager@^5.7.6: - version "5.7.6" - resolved "https://registry.yarnpkg.com/cache-manager/-/cache-manager-5.7.6.tgz#bdd8a154c73e5233824aa09ceb359ed225d37b7e" - integrity sha512-wBxnBHjDxF1RXpHCBD6HGvKER003Ts7IIm0CHpggliHzN1RZditb7rXoduE1rplc2DEFYKxhLKgFuchXMJje9w== +cache-manager@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/cache-manager/-/cache-manager-4.1.0.tgz#aa986421f1c975a862d6de88edb9ab1d30f4bd39" + integrity sha512-ZGM6dLxrP65bfOZmcviWMadUOCICqpLs92+P/S5tj8onz+k+tB7Gr+SAgOUHCQtfm2gYEQDHiKeul4+tYPOJ8A== dependencies: - eventemitter3 "^5.0.1" + async "3.2.3" lodash.clonedeep "^4.5.0" - lru-cache "^10.2.2" - promise-coalesce "^1.1.2" + lru-cache "^7.10.1" + +cacheable-lookup@^5.0.3: + version "5.0.4" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" + integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== + +cacheable-request@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.4.tgz#7a33ebf08613178b403635be7b899d3e69bbe817" + integrity sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^4.0.0" + lowercase-keys "^2.0.0" + normalize-url "^6.0.1" + responselike "^2.0.0" call-bind@^1.0.0: version "1.0.2" @@ -4634,10 +5120,10 @@ check-disk-space@3.4.0: resolved "https://registry.npmjs.org/check-disk-space/-/check-disk-space-3.4.0.tgz" integrity sha512-drVkSqfwA+TvuEhFipiR1OC9boEGZL5RrWvVsOthdcvQNXyCCuKkEiTOTXZ7qxSf/GLwq4GvzfrQD/Wz325hgw== -chokidar@3.5.3, chokidar@^3.5.3: - version "3.5.3" - resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz" - integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== +chokidar@3.6.0: + version "3.6.0" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== dependencies: anymatch "~3.1.2" braces "~3.0.2" @@ -4649,10 +5135,10 @@ chokidar@3.5.3, chokidar@^3.5.3: optionalDependencies: fsevents "~2.3.2" -chokidar@3.6.0: - version "3.6.0" - resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz" - integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== +chokidar@^3.5.3: + version "3.5.3" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== dependencies: anymatch "~3.1.2" braces "~3.0.2" @@ -4775,6 +5261,13 @@ cliui@^8.0.1: strip-ansi "^6.0.1" wrap-ansi "^7.0.0" +clone-response@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3" + integrity sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA== + dependencies: + mimic-response "^1.0.0" + clone@^1.0.2: version "1.0.4" resolved "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz" @@ -4829,7 +5322,7 @@ color-name@~1.1.4: resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -colorette@^2.0.20: +colorette@^2.0.20, colorette@^2.0.7: version "2.0.20" resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== @@ -4861,6 +5354,11 @@ commander@^6.2.1: resolved "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz" integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== +commander@^8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" + integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== + comment-json@4.2.3: version "4.2.3" resolved "https://registry.npmjs.org/comment-json/-/comment-json-4.2.3.tgz" @@ -4872,7 +5370,7 @@ comment-json@4.2.3: has-own-prop "^2.0.0" repeat-string "^1.6.1" -comment-json@^4.2.5: +comment-json@4.2.5, comment-json@^4.2.5: version "4.2.5" resolved "https://registry.yarnpkg.com/comment-json/-/comment-json-4.2.5.tgz#482e085f759c2704b60bc6f97f55b8c01bc41e70" integrity sha512-bKw/r35jR3HGt5PEPm1ljsQQGyCrR8sFGNiN5L+ykDHdpO8Smxkrkla9Yi6NkQyUrb8V54PGhfMs6NrIwtxtdw== @@ -4896,6 +5394,26 @@ component-emitter@^1.3.0: resolved "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz" integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== +compressible@~2.0.18: + version "2.0.18" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== + dependencies: + mime-db ">= 1.43.0 < 2" + +compression@^1.7.5: + version "1.7.5" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.5.tgz#fdd256c0a642e39e314c478f6c2cd654edd74c93" + integrity sha512-bQJ0YRck5ak3LgtnpKkiabX5pNF7tMUh1BSy2ZBOTh0Dim0BUu6aPPwByIns6/A5Prh8PufSPerMDUklpzes2Q== + dependencies: + bytes "3.1.2" + compressible "~2.0.18" + debug "2.6.9" + negotiator "~0.6.4" + on-headers "~1.0.2" + safe-buffer "5.2.1" + vary "~1.1.2" + concat-map@0.0.1: version "0.0.1" resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" @@ -4916,7 +5434,7 @@ consola@^2.15.0: resolved "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz" integrity sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw== -content-disposition@0.5.4: +content-disposition@0.5.4, content-disposition@^0.5.4: version "0.5.4" resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz" integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== @@ -4974,10 +5492,10 @@ cookie-signature@1.0.6: resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== -cookie@0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" - integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== +cookie@0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9" + integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w== cookie@^0.5.0: version "0.5.0" @@ -5070,12 +5588,14 @@ cron-parser@^4.6.0: dependencies: luxon "^3.2.1" -cross-inspect@1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/cross-inspect/-/cross-inspect-1.0.0.tgz" - integrity sha512-4PFfn4b5ZN6FMNGSZlyb7wUhuN8wvj8t/VQHZdM4JsDcruGJ8L2kf9zao98QIrBPFCpdk27qst/AGTl7pL3ypQ== +cross-spawn@^5.0.1: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + integrity sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A== dependencies: - tslib "^2.4.0" + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" cross-spawn@^6.0.0: version "6.0.5" @@ -5102,79 +5622,80 @@ crypto-js@^4.2.0: resolved "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz" integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q== -cspell-config-lib@8.13.3: - version "8.13.3" - resolved "https://registry.yarnpkg.com/cspell-config-lib/-/cspell-config-lib-8.13.3.tgz#06e574328d701dc34410f4b8fd14e8fd07b4d152" - integrity sha512-dzVdar8Kenwxho0PnUxOxwjUvyFYn6Q9mQAMHcQNXQrvo32bdpoF+oNtWC/5FfrQgUgyl19CVQ607bRigYWoOQ== +cspell-config-lib@8.15.6: + version "8.15.6" + resolved "https://registry.yarnpkg.com/cspell-config-lib/-/cspell-config-lib-8.15.6.tgz#ef361b647bfc18e992f7a6e825e7a6c70f4f298b" + integrity sha512-SfKx7iYzQcZmEF8b3od2dSGUmd+0y4Voq9vR9UItcc8gTeXdTRfEZc2uC6kiVrekFMTgXpLdnq71te03lDo06w== dependencies: - "@cspell/cspell-types" "8.13.3" + "@cspell/cspell-types" "8.15.6" comment-json "^4.2.5" - yaml "^2.5.0" + yaml "^2.6.0" -cspell-dictionary@8.13.3: - version "8.13.3" - resolved "https://registry.yarnpkg.com/cspell-dictionary/-/cspell-dictionary-8.13.3.tgz#622798ef6f8575d9ceb21dba20e8eace4177f74b" - integrity sha512-DQ3Tee7LIoy+9Mu52ht32O/MNBZ6i4iUeSTY2sMDDwogno3361BLRyfEjyiYNo3Fqf0Pcnt5MqY2DqIhrF/H/Q== +cspell-dictionary@8.15.6: + version "8.15.6" + resolved "https://registry.yarnpkg.com/cspell-dictionary/-/cspell-dictionary-8.15.6.tgz#98ab4f7642f195f86c00ed27dc10bbf4d923eede" + integrity sha512-P7uNxrp1EPUjDTXzk7/CmxVsJAEFWO4B3kVdKquDXWmX7a/8ZjWVIz4TBs6g83QK5I2HfXZCXu+fBEpZH4pm3w== dependencies: - "@cspell/cspell-pipe" "8.13.3" - "@cspell/cspell-types" "8.13.3" - cspell-trie-lib "8.13.3" + "@cspell/cspell-pipe" "8.15.6" + "@cspell/cspell-types" "8.15.6" + cspell-trie-lib "8.15.6" fast-equals "^5.0.1" -cspell-gitignore@8.13.3: - version "8.13.3" - resolved "https://registry.yarnpkg.com/cspell-gitignore/-/cspell-gitignore-8.13.3.tgz#d9bb175feb86899ed2e2af1bd714785f02b08aaa" - integrity sha512-0OZXuP33CXV4P95ySHGNqhq3VR5RaLwpyo0nGvLHOjPm3mCsQSjURLBKHvyQ3r2M7LWsGV1Xc81FfTx30FBZLg== +cspell-gitignore@8.15.6: + version "8.15.6" + resolved "https://registry.yarnpkg.com/cspell-gitignore/-/cspell-gitignore-8.15.6.tgz#74952dc47659a33ebca0d55ab30377054b23b804" + integrity sha512-HT9jn52fIlX4Mtj3JH5XkQa7A0jP7I8EXGhQMPB/brkKlSIoVm1Yr8/brEgUQCk/Vo6g7zXaEZzEkGxcsocW+A== dependencies: - "@cspell/url" "8.13.3" - cspell-glob "8.13.3" - cspell-io "8.13.3" + "@cspell/url" "8.15.6" + cspell-glob "8.15.6" + cspell-io "8.15.6" find-up-simple "^1.0.0" -cspell-glob@8.13.3: - version "8.13.3" - resolved "https://registry.yarnpkg.com/cspell-glob/-/cspell-glob-8.13.3.tgz#7533828ac52eb7ae87e8d58869ffc6fb9299a24e" - integrity sha512-+jGIMYyKDLmoOJIxNPXRdI7utcvw+9FMSmj1ApIdEff5dCkehi0gtzK4H7orXGYEvRdKQvfaXiyduVi79rXsZQ== - dependencies: - "@cspell/url" "8.13.3" - micromatch "^4.0.7" - -cspell-grammar@8.13.3: - version "8.13.3" - resolved "https://registry.yarnpkg.com/cspell-grammar/-/cspell-grammar-8.13.3.tgz#147e97a54ad2c8e4925bd84ae2afed7ff14960e2" - integrity sha512-xPSgKk9HY5EsI8lkMPC9hiZCeAUs+RY/IVliUBW1xEicAJhP4RZIGRdIwtDNNJGwKfNXazjqYhcS4LS0q7xPAQ== - dependencies: - "@cspell/cspell-pipe" "8.13.3" - "@cspell/cspell-types" "8.13.3" - -cspell-io@8.13.3: - version "8.13.3" - resolved "https://registry.yarnpkg.com/cspell-io/-/cspell-io-8.13.3.tgz#9f2323aa7ea6edce84c16964c95ee4c83b5b880e" - integrity sha512-AeMIkz7+4VuJaPKO/v1pUpyUSOOTyLOAfzeTRRAXEt+KRKOUe36MyUmBMza6gzNcX2yD04VgJukRL408TY9ntw== - dependencies: - "@cspell/cspell-service-bus" "8.13.3" - "@cspell/url" "8.13.3" - -cspell-lib@8.13.3: - version "8.13.3" - resolved "https://registry.yarnpkg.com/cspell-lib/-/cspell-lib-8.13.3.tgz#4d2516bbb09148079d634f73c65ffa05a62ae02c" - integrity sha512-aEqxIILeqDtNoCa47/oSl5c926b50ue3PobYs4usn0Ymf0434RopCP+DCGsF7BPtog4j4XWnEmvkcJs57DYWDg== - dependencies: - "@cspell/cspell-bundled-dicts" "8.13.3" - "@cspell/cspell-pipe" "8.13.3" - "@cspell/cspell-resolver" "8.13.3" - "@cspell/cspell-types" "8.13.3" - "@cspell/dynamic-import" "8.13.3" - "@cspell/strong-weak-map" "8.13.3" - "@cspell/url" "8.13.3" +cspell-glob@8.15.6: + version "8.15.6" + resolved "https://registry.yarnpkg.com/cspell-glob/-/cspell-glob-8.15.6.tgz#6821ed3eb047fd68562b13f5e04b9d579344dac9" + integrity sha512-X1mT4aP8eWO8s5KxQo4g3WXb0DDgIns9OnCzI4e/z2g3WoeQ479uTKAHgPJWY2JUhXeDNLutaoU4NgiDPdfC1Q== + dependencies: + "@cspell/url" "8.15.6" + micromatch "^4.0.8" + +cspell-grammar@8.15.6: + version "8.15.6" + resolved "https://registry.yarnpkg.com/cspell-grammar/-/cspell-grammar-8.15.6.tgz#358efbd890ef8d5ca910623addd8a8c5628fd641" + integrity sha512-1cd5o7kTTNO4nl2Em9RzXmmgfxBmE04o5X85AZfSCvbnwo7ntslLiSdXBSLJPHX8qqnAKAftsBYOUxkTqLuNAQ== + dependencies: + "@cspell/cspell-pipe" "8.15.6" + "@cspell/cspell-types" "8.15.6" + +cspell-io@8.15.6: + version "8.15.6" + resolved "https://registry.yarnpkg.com/cspell-io/-/cspell-io-8.15.6.tgz#a13f273969c6328d5c4f4c33b360ead33c95db86" + integrity sha512-jsrBGfikBFwLir28JyDinKw5z80APw6Agl2L6rWrhV9uDFYCyqHkrxzX8wmPY3beEXHwEU3e8B61JD/lbccyZQ== + dependencies: + "@cspell/cspell-service-bus" "8.15.6" + "@cspell/url" "8.15.6" + +cspell-lib@8.15.6: + version "8.15.6" + resolved "https://registry.yarnpkg.com/cspell-lib/-/cspell-lib-8.15.6.tgz#58d89c20cda17b1aef5b3b125a2e70737f482040" + integrity sha512-6GWA2yESLFVIfxa1M5LIsPgEbz2+OlkeBR/LshRBZJGpRbVxyxwYDUcwT4jyQq2edJnoCkFouLQHi+6s7fHgLQ== + dependencies: + "@cspell/cspell-bundled-dicts" "8.15.6" + "@cspell/cspell-pipe" "8.15.6" + "@cspell/cspell-resolver" "8.15.6" + "@cspell/cspell-types" "8.15.6" + "@cspell/dynamic-import" "8.15.6" + "@cspell/filetypes" "8.15.6" + "@cspell/strong-weak-map" "8.15.6" + "@cspell/url" "8.15.6" clear-module "^4.1.2" comment-json "^4.2.5" - cspell-config-lib "8.13.3" - cspell-dictionary "8.13.3" - cspell-glob "8.13.3" - cspell-grammar "8.13.3" - cspell-io "8.13.3" - cspell-trie-lib "8.13.3" + cspell-config-lib "8.15.6" + cspell-dictionary "8.15.6" + cspell-glob "8.15.6" + cspell-grammar "8.15.6" + cspell-io "8.15.6" + cspell-trie-lib "8.15.6" env-paths "^3.0.0" fast-equals "^5.0.1" gensequence "^7.0.0" @@ -5184,45 +5705,49 @@ cspell-lib@8.13.3: vscode-uri "^3.0.8" xdg-basedir "^5.1.0" -cspell-trie-lib@8.13.3: - version "8.13.3" - resolved "https://registry.yarnpkg.com/cspell-trie-lib/-/cspell-trie-lib-8.13.3.tgz#3fe376106faef310c3874685fc6e2ddaa2551066" - integrity sha512-Z0iLGi9HI+Vf+WhVVeru6dYgQdtaYCKWRlc1SayLfAZhw9BcjrXL8KTXDfAfv/lUgnRu6xwP1isLlDNZECsKVQ== +cspell-trie-lib@8.15.6: + version "8.15.6" + resolved "https://registry.yarnpkg.com/cspell-trie-lib/-/cspell-trie-lib-8.15.6.tgz#99290f1a4a6db2c321bf757ab48d2e286bfdde77" + integrity sha512-tD7F3ppSuD+ZYlql50eLVd/umTYrNOT8QGIu+RzWHhRxuMLU3/XkNHWsATQxGfE9Iki69g8Gjbe0AYH8pHaqbw== dependencies: - "@cspell/cspell-pipe" "8.13.3" - "@cspell/cspell-types" "8.13.3" + "@cspell/cspell-pipe" "8.15.6" + "@cspell/cspell-types" "8.15.6" gensequence "^7.0.0" -cspell@^8.13.3: - version "8.13.3" - resolved "https://registry.yarnpkg.com/cspell/-/cspell-8.13.3.tgz#cebe77faeed2d6d3e0a6dc244d5c1c3fad5b3ad5" - integrity sha512-2wv4Eby7g8wDB553fI8IoZjyitoKrD2kmtdeoYUN2EjVs3RMpIOver3fL+0VaFAaN0uLfAoeAAIB5xJEakvZYQ== +cspell@^8.15.6: + version "8.15.6" + resolved "https://registry.yarnpkg.com/cspell/-/cspell-8.15.6.tgz#0314bf470dbd4841521c6f8e4b628d164c18df0b" + integrity sha512-G5ab25AM4RZDTCZ3NgOwYVtWTCxj7Fv/v8Z2D0f6wGhcpjqZucd3CIVy10VcI6e9K7eLOnytECKpwMk2WO+pFA== dependencies: - "@cspell/cspell-json-reporter" "8.13.3" - "@cspell/cspell-pipe" "8.13.3" - "@cspell/cspell-types" "8.13.3" - "@cspell/dynamic-import" "8.13.3" - "@cspell/url" "8.13.3" + "@cspell/cspell-json-reporter" "8.15.6" + "@cspell/cspell-pipe" "8.15.6" + "@cspell/cspell-types" "8.15.6" + "@cspell/dynamic-import" "8.15.6" + "@cspell/url" "8.15.6" chalk "^5.3.0" chalk-template "^1.1.0" commander "^12.1.0" - cspell-dictionary "8.13.3" - cspell-gitignore "8.13.3" - cspell-glob "8.13.3" - cspell-io "8.13.3" - cspell-lib "8.13.3" - fast-glob "^3.3.2" + cspell-dictionary "8.15.6" + cspell-gitignore "8.15.6" + cspell-glob "8.15.6" + cspell-io "8.15.6" + cspell-lib "8.15.6" fast-json-stable-stringify "^2.1.0" - file-entry-cache "^9.0.0" + file-entry-cache "^9.1.0" get-stdin "^9.0.0" semver "^7.6.3" - strip-ansi "^7.1.0" + tinyglobby "^0.2.10" dargs@^8.0.0: version "8.1.0" resolved "https://registry.yarnpkg.com/dargs/-/dargs-8.1.0.tgz#a34859ea509cbce45485e5aa356fef70bfcc7272" integrity sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw== +dateformat@^4.6.3: + version "4.6.3" + resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-4.6.3.tgz#556fa6497e5217fedb78821424f8a1c22fa3f4b5" + integrity sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA== + debug@2.6.9: version "2.6.9" resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" @@ -5237,13 +5762,27 @@ debug@4, debug@4.x, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debu dependencies: ms "2.1.2" -debug@4.3.6, debug@~4.3.6: +debug@4.3.7: + version "4.3.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" + integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== + dependencies: + ms "^2.1.3" + +debug@~4.3.6: version "4.3.6" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b" integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg== dependencies: ms "2.1.2" +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + dedent@^1.0.0: version "1.5.1" resolved "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz" @@ -5266,6 +5805,11 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" +defer-to-connect@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" + integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== + 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" @@ -5285,22 +5829,17 @@ denque@^2.1.0: resolved "https://registry.yarnpkg.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1" integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw== -depd@2.0.0: +depd@2.0.0, depd@~2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== -depd@~1.1.0: - version "1.1.2" - resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" - integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== - destroy@1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== -detect-libc@^2.0.1: +detect-libc@^2.0.1, detect-libc@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700" integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw== @@ -5333,13 +5872,6 @@ diff@^4.0.1: resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== -dir-glob@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" - integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== - dependencies: - path-type "^4.0.0" - dot-prop@^5.1.0: version "5.3.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" @@ -5357,11 +5889,6 @@ dotenv@16.4.5: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== -dset@^3.1.2: - version "3.1.3" - resolved "https://registry.npmjs.org/dset/-/dset-3.1.3.tgz" - integrity sha512-20TuZZHCEZ2O71q9/+8BwKwZ0QtD9D8ObhrihJPr+vLLYlSuAU3/zL4cSlgbfeoGHTjCSJBa7NGcrF9/Bx/WJQ== - eastasianwidth@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz" @@ -5421,6 +5948,11 @@ encodeurl@~1.0.2: resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== +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: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" @@ -5436,10 +5968,10 @@ enhanced-resolve@^5.0.0, enhanced-resolve@^5.7.0: graceful-fs "^4.2.4" tapable "^2.2.0" -enhanced-resolve@^5.17.0: - version "5.17.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz#d037603789dd9555b89aaec7eb78845c49089bc5" - integrity sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA== +enhanced-resolve@^5.17.1: + version "5.17.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15" + integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -5508,6 +6040,11 @@ escape-string-regexp@^4.0.0: resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== +escape-string-regexp@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8" + integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw== + eslint-config-prettier@^9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz#31af3d94578645966c082fcb71a5846d3c94867f" @@ -5521,10 +6058,10 @@ eslint-scope@5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-scope@^8.0.2: - version "8.0.2" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.0.2.tgz#5cbb33d4384c9136083a71190d548158fe128f94" - integrity sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA== +eslint-scope@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.2.0.tgz#377aa6f1cb5dc7592cfd0b7f892fd0cf352ce442" + integrity sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" @@ -5544,27 +6081,36 @@ eslint-visitor-keys@^4.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz#e3adc021aa038a2a8e0b2f8b0ce8f66b9483b1fb" integrity sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw== -eslint@^9.9.0: - version "9.9.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.9.0.tgz#8d214e69ae4debeca7ae97daebbefe462072d975" - integrity sha512-JfiKJrbx0506OEerjK2Y1QlldtBxkAlLxT5OEcRF8uaQ86noDe2k31Vw9rnSWv+MXZHj7OOUV/dA0AhdLFcyvA== +eslint-visitor-keys@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45" + integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== + +eslint@^9.14.0: + version "9.14.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.14.0.tgz#534180a97c00af08bcf2b60b0ebf0c4d6c1b2c95" + integrity sha512-c2FHsVBr87lnUtjP4Yhvk4yEhKrQavGafRA/Se1ouse8PfbfC/Qh9Mxa00yWsZRlqeUB9raXip0aiiUZkgnr9g== dependencies: "@eslint-community/eslint-utils" "^4.2.0" - "@eslint-community/regexpp" "^4.11.0" - "@eslint/config-array" "^0.17.1" + "@eslint-community/regexpp" "^4.12.1" + "@eslint/config-array" "^0.18.0" + "@eslint/core" "^0.7.0" "@eslint/eslintrc" "^3.1.0" - "@eslint/js" "9.9.0" + "@eslint/js" "9.14.0" + "@eslint/plugin-kit" "^0.2.0" + "@humanfs/node" "^0.16.6" "@humanwhocodes/module-importer" "^1.0.1" - "@humanwhocodes/retry" "^0.3.0" - "@nodelib/fs.walk" "^1.2.8" + "@humanwhocodes/retry" "^0.4.0" + "@types/estree" "^1.0.6" + "@types/json-schema" "^7.0.15" ajv "^6.12.4" chalk "^4.0.0" cross-spawn "^7.0.2" debug "^4.3.2" escape-string-regexp "^4.0.0" - eslint-scope "^8.0.2" - eslint-visitor-keys "^4.0.0" - espree "^10.1.0" + eslint-scope "^8.2.0" + eslint-visitor-keys "^4.2.0" + espree "^10.3.0" esquery "^1.5.0" esutils "^2.0.2" fast-deep-equal "^3.1.3" @@ -5574,14 +6120,11 @@ eslint@^9.9.0: ignore "^5.2.0" imurmurhash "^0.1.4" is-glob "^4.0.0" - is-path-inside "^3.0.3" json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.4.1" lodash.merge "^4.6.2" minimatch "^3.1.2" natural-compare "^1.4.0" optionator "^0.9.3" - strip-ansi "^6.0.1" text-table "^0.2.0" espree@^10.0.1: @@ -5593,14 +6136,14 @@ espree@^10.0.1: acorn-jsx "^5.3.2" eslint-visitor-keys "^4.0.0" -espree@^10.1.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-10.1.0.tgz#8788dae611574c0f070691f522e4116c5a11fc56" - integrity sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA== +espree@^10.3.0: + version "10.3.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.3.0.tgz#29267cf5b0cb98735b65e64ba07e0ed49d1eed8a" + integrity sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg== dependencies: - acorn "^8.12.0" + acorn "^8.14.0" acorn-jsx "^5.3.2" - eslint-visitor-keys "^4.0.0" + eslint-visitor-keys "^4.2.0" esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" @@ -5641,17 +6184,17 @@ etag@~1.8.1: resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== -eventemitter3@^3.1.0: - version "3.1.2" - resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz" - integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== eventemitter3@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== -events@^3.2.0: +events@^3.2.0, events@^3.3.0: version "3.3.0" resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== @@ -5669,6 +6212,19 @@ execa@0.11.0: signal-exit "^3.0.0" strip-eof "^1.0.0" +execa@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" + integrity sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw== + dependencies: + cross-spawn "^5.0.1" + get-stream "^3.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@^5.0.0: version "5.1.1" resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz" @@ -5684,7 +6240,7 @@ execa@^5.0.0: signal-exit "^3.0.3" strip-final-newline "^2.0.0" -execa@^8.0.1, execa@~8.0.1: +execa@~8.0.1: version "8.0.1" resolved "https://registry.yarnpkg.com/execa/-/execa-8.0.1.tgz#51f6a5943b580f963c3ca9c6321796db8cc39b8c" integrity sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg== @@ -5699,6 +6255,13 @@ execa@^8.0.1, execa@~8.0.1: signal-exit "^4.1.0" strip-final-newline "^3.0.0" +executable@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" + integrity sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg== + dependencies: + pify "^2.2.0" + exit@^0.1.2: version "0.1.2" resolved "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" @@ -5726,43 +6289,58 @@ expect@^29.7.0: jest-message-util "^29.7.0" jest-util "^29.7.0" -express@4.19.2: - version "4.19.2" - resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465" - integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q== +express@4.21.1: + version "4.21.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.21.1.tgz#9dae5dda832f16b4eec941a4e44aa89ec481b281" + integrity sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ== dependencies: accepts "~1.3.8" array-flatten "1.1.1" - body-parser "1.20.2" + body-parser "1.20.3" content-disposition "0.5.4" content-type "~1.0.4" - cookie "0.6.0" + cookie "0.7.1" cookie-signature "1.0.6" debug "2.6.9" depd "2.0.0" - encodeurl "~1.0.2" + encodeurl "~2.0.0" escape-html "~1.0.3" etag "~1.8.1" - finalhandler "1.2.0" + finalhandler "1.3.1" fresh "0.5.2" http-errors "2.0.0" - merge-descriptors "1.0.1" + merge-descriptors "1.0.3" methods "~1.1.2" on-finished "2.4.1" parseurl "~1.3.3" - path-to-regexp "0.1.7" + path-to-regexp "0.1.10" proxy-addr "~2.0.7" - qs "6.11.0" + qs "6.13.0" range-parser "~1.2.1" safe-buffer "5.2.1" - send "0.18.0" - serve-static "1.15.0" + send "0.19.0" + serve-static "1.16.2" setprototypeof "1.2.0" statuses "2.0.1" type-is "~1.6.18" utils-merge "1.0.1" vary "~1.1.2" +ext-list@^2.0.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/ext-list/-/ext-list-2.2.2.tgz#0b98e64ed82f5acf0f2931babf69212ef52ddd37" + integrity sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA== + dependencies: + mime-db "^1.28.0" + +ext-name@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ext-name/-/ext-name-5.0.0.tgz#70781981d183ee15d13993c8822045c506c8f0a6" + integrity sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ== + dependencies: + ext-list "^2.0.0" + sort-keys-length "^1.0.0" + extend@^3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz" @@ -5777,6 +6355,11 @@ external-editor@^3.0.3, external-editor@^3.1.0: iconv-lite "^0.4.24" tmp "^0.0.33" +fast-copy@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/fast-copy/-/fast-copy-3.0.2.tgz#59c68f59ccbcac82050ba992e0d5c389097c9d35" + integrity sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ== + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" @@ -5792,7 +6375,7 @@ fast-equals@^5.0.1: resolved "https://registry.npmjs.org/fast-equals/-/fast-equals-5.0.1.tgz" integrity sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ== -fast-glob@3.3.2, fast-glob@^3.3.2: +fast-glob@^3.2.5, fast-glob@^3.3.2: version "3.3.2" resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz" integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== @@ -5803,7 +6386,7 @@ fast-glob@3.3.2, fast-glob@^3.3.2: merge2 "^1.3.0" micromatch "^4.0.4" -fast-glob@^3.2.7, fast-glob@^3.2.9: +fast-glob@^3.2.7: version "3.2.11" resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz" integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== @@ -5824,6 +6407,11 @@ fast-levenshtein@^2.0.6: resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== +fast-redact@^3.1.1: + version "3.5.0" + resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-3.5.0.tgz#e9ea02f7e57d0cd8438180083e93077e496285e4" + integrity sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A== + fast-safe-stringify@2.1.1, fast-safe-stringify@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz" @@ -5850,6 +6438,11 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" +fdir@^6.4.2: + version "6.4.2" + resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.4.2.tgz#ddaa7ce1831b161bc3657bb99cb36e1622702689" + integrity sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ== + figures@^3.0.0, figures@^3.2.0: version "3.2.0" resolved "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz" @@ -5864,13 +6457,22 @@ file-entry-cache@^8.0.0: dependencies: flat-cache "^4.0.0" -file-entry-cache@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-9.0.0.tgz#4478e7ceaa5191fa9676a2daa7030211c31b1e7e" - integrity sha512-6MgEugi8p2tiUhqO7GnPsmbCCzj0YRCwwaTbpGRyKZesjRSzkqkAE9fPp7V2yMs5hwfgbQLgdvSSkGNg1s5Uvw== +file-entry-cache@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-9.1.0.tgz#2e66ad98ce93f49aed1b178c57b0b5741591e075" + integrity sha512-/pqPFG+FdxWQj+/WSuzXSDaNzxgTLr/OrR1QuqfEZzDakpdYE70PwUxL7BPUa8hpjbvY1+qvCl8k+8Tq34xJgg== dependencies: flat-cache "^5.0.0" +file-type@^17.1.6: + version "17.1.6" + resolved "https://registry.yarnpkg.com/file-type/-/file-type-17.1.6.tgz#18669e0577a4849ef6e73a41f8bdf1ab5ae21023" + integrity sha512-hlDw5Ev+9e883s0pwUsuuYNu4tD7GgpUnOvykjv1Gya0ZIjuKumthDRua90VUn6/nlRKAjcxLUnHNTIUWwWIiw== + dependencies: + readable-web-to-node-stream "^3.0.2" + strtok3 "^7.0.0-alpha.9" + token-types "^5.0.0-alpha.2" + filelist@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5" @@ -5878,6 +6480,20 @@ filelist@^1.0.4: dependencies: minimatch "^5.0.1" +filename-reserved-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/filename-reserved-regex/-/filename-reserved-regex-3.0.0.tgz#3d5dd6d4e2d73a3fed2ebc4cd0b3448869a081f7" + integrity sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw== + +filenamify@^5.0.2: + version "5.1.1" + resolved "https://registry.yarnpkg.com/filenamify/-/filenamify-5.1.1.tgz#a1ccc5ae678a5e34f578afcb9b72898264d166d2" + integrity sha512-M45CbrJLGACfrPOkrTp3j2EcO9OBkKUYME0eiqOCa7i2poaklU0jhlIaMlr8ijLorT0uLAzrn3qXOp5684CkfA== + dependencies: + filename-reserved-regex "^3.0.0" + strip-outer "^2.0.0" + trim-repeated "^2.0.0" + fill-range@^7.0.1: version "7.0.1" resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" @@ -5892,13 +6508,13 @@ fill-range@^7.1.1: dependencies: to-regex-range "^5.0.1" -finalhandler@1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz" - integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== +finalhandler@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.1.tgz#0c575f1d1d324ddd1da35ad7ece3df7d19088019" + integrity sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ== dependencies: debug "2.6.9" - encodeurl "~1.0.2" + encodeurl "~2.0.0" escape-html "~1.0.3" on-finished "2.4.1" parseurl "~1.3.3" @@ -5935,6 +6551,13 @@ find-up@^7.0.0: path-exists "^5.0.0" unicorn-magic "^0.1.0" +find-versions@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/find-versions/-/find-versions-5.1.0.tgz#973f6739ce20f5e439a27eba8542a4b236c8e685" + integrity sha512-+iwzCJ7C5v5KgcBuueqVoNiHVoQpwiUK5XFLjf0affFTep+Wcw93tPvmb8tqujDNmzhBDPddnWV/qgWSXgq+Hg== + dependencies: + semver-regex "^4.0.5" + flat-cache@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.0.tgz" @@ -6142,6 +6765,11 @@ get-stdin@^9.0.0: resolved "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz" integrity sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA== +get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + integrity sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ== + get-stream@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -6149,6 +6777,13 @@ get-stream@^4.0.0: dependencies: pump "^3.0.0" +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + get-stream@^6.0.0: version "6.0.1" resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" @@ -6239,22 +6874,10 @@ globals@^14.0.0: resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== -globby@^11.1.0: - version "11.1.0" - resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz" - integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== - dependencies: - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.2.9" - ignore "^5.2.0" - merge2 "^1.4.1" - slash "^3.0.0" - -google-auth-library@^9.13.0: - version "9.13.0" - resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-9.13.0.tgz#0735ecdc33d350699dbf3ff48601a856911bbcff" - integrity sha512-p9Y03Uzp/Igcs36zAaB0XTSwZ8Y0/tpYiz5KIde5By+H9DCVUSYtDWZu6aFXsWTqENMb8BD/pDT3hR8NVrPkfA== +google-auth-library@^9.14.2: + version "9.14.2" + resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-9.14.2.tgz#92a53ba32b3a9ff9ced8ed34129edb5a7fa7fb52" + integrity sha512-R+FRIfk1GBo3RdlRYWPdwk8nmtVUOn6+BkDomAC46KoU8kzXzE1HLmOasSCbWUByMMAGkknVF0G5kQ69Vj7dlA== dependencies: base64-js "^1.3.0" ecdsa-sig-formatter "^1.0.11" @@ -6270,6 +6893,23 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" +got@^11.8.5: + version "11.8.6" + resolved "https://registry.yarnpkg.com/got/-/got-11.8.6.tgz#276e827ead8772eddbcfc97170590b841823233a" + integrity sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g== + dependencies: + "@sindresorhus/is" "^4.0.0" + "@szmarczak/http-timer" "^4.0.5" + "@types/cacheable-request" "^6.0.1" + "@types/responselike" "^1.0.0" + cacheable-lookup "^5.0.3" + cacheable-request "^7.0.2" + decompress-response "^6.0.0" + http2-wrapper "^1.0.0-beta.5.2" + lowercase-keys "^2.0.0" + p-cancelable "^2.0.0" + responselike "^2.0.0" + graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9: version "4.2.10" resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz" @@ -6285,18 +6925,6 @@ graphemer@^1.4.0: resolved "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz" integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== -graphql-tag@2.12.6: - version "2.12.6" - resolved "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz" - integrity sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg== - dependencies: - tslib "^2.1.0" - -graphql-ws@5.14.2: - version "5.14.2" - resolved "https://registry.npmjs.org/graphql-ws/-/graphql-ws-5.14.2.tgz" - integrity sha512-LycmCwhZ+Op2GlHz4BZDsUYHKRiiUz+3r9wbhBATMETNlORQJAaFlAgTFoeRh6xQoQegwYwIylVD1Qns9/DA3w== - gtoken@^7.0.0: version "7.0.1" resolved "https://registry.npmjs.org/gtoken/-/gtoken-7.0.1.tgz" @@ -6351,10 +6979,15 @@ hasown@^2.0.0: dependencies: function-bind "^1.1.2" -helmet@^7.1.0: - version "7.1.0" - resolved "https://registry.npmjs.org/helmet/-/helmet-7.1.0.tgz" - integrity sha512-g+HZqgfbpXdCkme/Cd/mZkV0aV3BZZZSugecH03kl38m/Kmdx8jKjBikpDj2cr+Iynv4KpYEviojNdTJActJAg== +helmet@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/helmet/-/helmet-8.0.0.tgz#05370fb1953aa7b81bd0ddfa459221247be6ea5c" + integrity sha512-VyusHLEIIO5mjQPUI1wpOAEu+wl6Q0998jzTxqUYGE45xCIcAxy3MsbEK/yyJUJ3ADeMoB6MornPH6GMWAf+Pw== + +help-me@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/help-me/-/help-me-5.0.0.tgz#b1ebe63b967b74060027c2ac61f9be12d354a6f6" + integrity sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg== hexoid@^1.0.0: version "1.0.0" @@ -6366,6 +6999,11 @@ html-escaper@^2.0.0: resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== +http-cache-semantics@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== + http-errors@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz" @@ -6377,6 +7015,14 @@ http-errors@2.0.0: statuses "2.0.1" toidentifier "1.0.1" +http2-wrapper@^1.0.0-beta.5.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" + integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.0.0" + https-proxy-agent@^7.0.1: version "7.0.2" resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz" @@ -6395,10 +7041,10 @@ human-signals@^5.0.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28" integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== -husky@^9.1.4: - version "9.1.4" - resolved "https://registry.yarnpkg.com/husky/-/husky-9.1.4.tgz#926fd19c18d345add5eab0a42b2b6d9a80259b34" - integrity sha512-bho94YyReb4JV7LYWRWxZ/xr6TtOTt8cMfmQ39MQYJ7f/YE268s3GdghGwi+y4zAeqewE5zYLvuhV0M0ijsDEA== +husky@^9.1.6: + version "9.1.6" + resolved "https://registry.yarnpkg.com/husky/-/husky-9.1.6.tgz#e23aa996b6203ab33534bdc82306b0cf2cb07d6c" + integrity sha512-sqbjZKK7kf44hfdE94EoX8MZNk0n7HeW37O4YrVGCF4wzgQjp+akPAkfUK5LZ6KuR/6sqeAVuXHji+RzQgOn5A== iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" @@ -6407,9 +7053,9 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -ieee754@^1.1.13: +ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== ignore@^5.2.0: @@ -6430,30 +7076,10 @@ import-fresh@^3.2.1, import-fresh@^3.3.0: parent-module "^1.0.0" resolve-from "^4.0.0" -import-in-the-middle@1.7.1: - version "1.7.1" - resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.7.1.tgz#3e111ff79c639d0bde459bd7ba29dd9fdf357364" - integrity sha512-1LrZPDtW+atAxH42S6288qyDFNQ2YCty+2mxEPRtfazH6Z5QwkaBSTS2ods7hnVJioF6rkRfNoA6A/MstpFXLg== - dependencies: - acorn "^8.8.2" - acorn-import-assertions "^1.9.0" - cjs-module-lexer "^1.2.2" - module-details-from-path "^1.0.3" - -import-in-the-middle@1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.8.0.tgz#c94d88d53701de9a248f9710b41f533e67f598a4" - integrity sha512-/xQjze8szLNnJ5rvHSzn+dcVXqCAU6Plbk4P24U/jwPmg1wy7IIp9OjKIO5tYue8GSPhDpPDiApQjvBUmWwhsQ== - dependencies: - acorn "^8.8.2" - acorn-import-attributes "^1.9.5" - cjs-module-lexer "^1.2.2" - module-details-from-path "^1.0.3" - -import-in-the-middle@^1.11.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.11.0.tgz#a94c4925b8da18256cde3b3b7b38253e6ca5e708" - integrity sha512-5DimNQGoe0pLUHbR9qK84iWaWjjbsxiqXnw6Qz64+azRgleqv9k2kTt5fw7QsOpmaGYtuxxursnPPsnTKEx10Q== +import-in-the-middle@^1.11.2: + version "1.11.2" + resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.11.2.tgz#dd848e72b63ca6cd7c34df8b8d97fc9baee6174f" + integrity sha512-gK6Rr6EykBcc6cVWRSBR5TWf8nn6hZMYSRYqCcHa0l0d1fPK7JSYo6+Mlmck76jIX9aL/IZ71c06U2VpFwl1zA== dependencies: acorn "^8.8.2" acorn-import-attributes "^1.9.5" @@ -6648,10 +7274,10 @@ is-obj@^2.0.0: resolved "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== -is-path-inside@^3.0.3: - version "3.0.3" - resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz" - integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== +is-plain-obj@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg== is-stream@^1.1.0: version "1.1.0" @@ -6743,11 +7369,6 @@ istanbul-reports@^3.1.3: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -iterall@^1.2.1: - version "1.3.0" - resolved "https://registry.npmjs.org/iterall/-/iterall-1.3.0.tgz" - integrity sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg== - iterare@1.2.1, iterare@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz" @@ -7222,6 +7843,11 @@ jose@^4.14.6: resolved "https://registry.yarnpkg.com/jose/-/jose-4.15.5.tgz#6475d0f467ecd3c630a1b5dadd2735a7288df706" integrity sha512-jc7BFxgKPKi94uOvEmzlSWFFe2+vASyXaKUpdQKatWAESU2MWjDfFf0fdfc83CDKcA5QecabZeNLyfhe3yKNkg== +joycon@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03" + integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw== + js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" @@ -7299,7 +7925,7 @@ jsonc-parser@3.2.1: resolved "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz" integrity sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA== -jsonc-parser@3.3.1: +jsonc-parser@3.3.1, jsonc-parser@^3.2.0: version "3.3.1" resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.3.1.tgz#f2a524b4f7fd11e3d791e559977ad60b98b798b4" integrity sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ== @@ -7395,7 +8021,7 @@ kareem@2.6.3: resolved "https://registry.yarnpkg.com/kareem/-/kareem-2.6.3.tgz#23168ec8ffb6c1abfd31b7169a6fb1dd285992ac" integrity sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q== -keyv@^4.5.4: +keyv@^4.0.0, keyv@^4.5.4: version "4.5.4" resolved "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz" integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== @@ -7440,10 +8066,10 @@ lines-and-columns@^1.1.6: resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== -lint-staged@^15.2.9: - version "15.2.9" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-15.2.9.tgz#bf70d40b6b192df6ad756fb89822211615e0f4da" - integrity sha512-BZAt8Lk3sEnxw7tfxM7jeZlPRuT4M68O0/CwZhhaw6eeWu0Lz5eERE3m386InivXB64fp/mDID452h48tvKlRQ== +lint-staged@^15.2.10: + version "15.2.10" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-15.2.10.tgz#92ac222f802ba911897dcf23671da5bb80643cd2" + integrity sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg== dependencies: chalk "~5.3.0" commander "~12.1.0" @@ -7451,7 +8077,7 @@ lint-staged@^15.2.9: execa "~8.0.1" lilconfig "~3.1.2" listr2 "~8.2.4" - micromatch "~4.0.7" + micromatch "~4.0.8" pidtree "~0.6.0" string-argv "~0.3.2" yaml "~2.5.0" @@ -7559,9 +8185,9 @@ lodash.kebabcase@^4.1.1: resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36" integrity sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g== -lodash.memoize@4.x: +lodash.memoize@^4.1.2: version "4.1.2" - resolved "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== lodash.merge@^4.6.2: @@ -7623,6 +8249,11 @@ log-update@^6.1.0: strip-ansi "^7.1.0" wrap-ansi "^9.0.0" +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + lru-cache@6.0.0, lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" @@ -7630,11 +8261,24 @@ lru-cache@6.0.0, lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -lru-cache@^10.2.0, lru-cache@^10.2.2: +lru-cache@^10.2.0: version "10.4.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== +lru-cache@^4.0.1: + version "4.1.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +lru-cache@^7.10.1: + version "7.18.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" + integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== + "lru-cache@^9.1.1 || ^10.0.0": version "10.0.1" resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz" @@ -7648,7 +8292,7 @@ lru-memoizer@^2.2.0: lodash.clonedeep "^4.5.0" lru-cache "6.0.0" -luxon@^3.2.1: +luxon@^3.2.1, luxon@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.5.0.tgz#6b6f65c5cd1d61d1fd19dbf07ee87a50bf4b8e20" integrity sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ== @@ -7674,9 +8318,9 @@ make-dir@^3.0.0: dependencies: semver "^6.0.0" -make-error@1.x, make-error@^1.1.1: +make-error@^1.1.1, make-error@^1.3.6: version "1.3.6" - resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== makeerror@1.0.12: @@ -7708,17 +8352,17 @@ meow@^12.0.1: resolved "https://registry.yarnpkg.com/meow/-/meow-12.1.1.tgz#e558dddbab12477b69b2e9a2728c327f191bace6" integrity sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw== -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz" - integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== +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" resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -merge2@^1.3.0, merge2@^1.4.1: +merge2@^1.3.0: version "1.4.1" resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== @@ -7736,10 +8380,10 @@ micromatch@^4.0.0, micromatch@^4.0.4: braces "^3.0.2" picomatch "^2.3.1" -micromatch@^4.0.7, micromatch@~4.0.7: - version "4.0.7" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.7.tgz#33e8190d9fe474a9895525f5618eee136d46c2e5" - integrity sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q== +micromatch@^4.0.8, micromatch@~4.0.8: + 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.3" picomatch "^2.3.1" @@ -7749,6 +8393,11 @@ mime-db@1.52.0: resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== +"mime-db@>= 1.43.0 < 2", mime-db@^1.28.0: + version "1.53.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.53.0.tgz#3cb63cd820fc29896d9d4e8c32ab4fcd74ccb447" + integrity sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg== + mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" @@ -7781,6 +8430,16 @@ mimic-function@^5.0.0: resolved "https://registry.yarnpkg.com/mimic-function/-/mimic-function-5.0.1.tgz#acbe2b3349f99b9deaca7fb70e48b83e94e67076" integrity sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA== +mimic-response@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" @@ -7802,6 +8461,13 @@ minimatch@^9.0.1: dependencies: brace-expansion "^2.0.1" +minimatch@^9.0.3: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + minimatch@^9.0.4: version "9.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51" @@ -7846,23 +8512,6 @@ module-details-from-path@^1.0.3: resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b" integrity sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A== -moment-timezone@^0.5.45: - version "0.5.45" - resolved "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.45.tgz" - integrity sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ== - dependencies: - moment "^2.29.4" - -moment@^2.29.4: - version "2.29.4" - resolved "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz" - integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== - -moment@^2.30.1: - version "2.30.1" - resolved "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz" - integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how== - mongodb-connection-string-url@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.0.tgz" @@ -7871,23 +8520,23 @@ mongodb-connection-string-url@^3.0.0: "@types/whatwg-url" "^11.0.2" whatwg-url "^13.0.0" -mongodb@6.7.0: - version "6.7.0" - resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-6.7.0.tgz#f86e51e6530e6a2ca4a99d7cfdf6f409223ac199" - integrity sha512-TMKyHdtMcO0fYBNORiYdmM25ijsHs+Njs963r4Tro4OQZzqYigAzYQouwWRg4OIaiLRUEGUh/1UAcH5lxdSLIA== +mongodb@~6.10.0: + version "6.10.0" + resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-6.10.0.tgz#20a9f1cf3c6829e75fc39e6d8c1c19f164209c2e" + integrity sha512-gP9vduuYWb9ZkDM546M+MP2qKVk5ZG2wPF63OvSRuUbqCR+11ZCAE1mOfllhlAG0wcoJY5yDL/rV3OmYEwXIzg== dependencies: "@mongodb-js/saslprep" "^1.1.5" bson "^6.7.0" mongodb-connection-string-url "^3.0.0" -mongoose@^8.5.2: - version "8.5.2" - resolved "https://registry.yarnpkg.com/mongoose/-/mongoose-8.5.2.tgz#73b40ce778f3fc66407aba3c3157795cdd278543" - integrity sha512-GZB4rHMdYfGatV+23IpCrqFbyCOjCNOHXgWbirr92KRwTEncBrtW3kgU9vmpKjsGf7nMmnAy06SwWUv1vhDkSg== +mongoose@^8.8.0: + version "8.8.0" + resolved "https://registry.yarnpkg.com/mongoose/-/mongoose-8.8.0.tgz#4475fdac6ce525ef4bdce406abd91c914e666f29" + integrity sha512-KluvgwnQB1GPOYZZXUHJRjS1TW6xxwTlf/YgjWExuuNanIe3W7VcR7dDXQVCIRk8L7NYge8EnoTcu2grWtN+XQ== dependencies: bson "^6.7.0" kareem "2.6.3" - mongodb "6.7.0" + mongodb "~6.10.0" mpath "0.9.0" mquery "5.0.0" ms "2.1.3" @@ -7915,7 +8564,7 @@ ms@2.1.2: resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@2.1.3, ms@^2.1.1: +ms@2.1.3, ms@^2.1.1, ms@^2.1.3: version "2.1.3" resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -7974,6 +8623,11 @@ negotiator@0.6.3: resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== +negotiator@~0.6.4: + version "0.6.4" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.4.tgz#777948e2452651c570b712dd01c23e262713fff7" + integrity sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w== + neo-async@^2.6.2: version "2.6.2" resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" @@ -7987,10 +8641,10 @@ nestjs-command@^3.1.4: lodash.compact "^3.0.1" lodash.flattendeep "^4.4.0" -nestjs-i18n@^10.4.5: - version "10.4.5" - resolved "https://registry.npmjs.org/nestjs-i18n/-/nestjs-i18n-10.4.5.tgz" - integrity sha512-OamX+Bf7yGtbKnfMLQNaVZL89YmFfcYCf0Y+R282D3rExpXDvHNNVrSbJoOXPK2fBq7/lnhlVYwEHiniqOiqXw== +nestjs-i18n@^10.4.9: + version "10.4.9" + resolved "https://registry.yarnpkg.com/nestjs-i18n/-/nestjs-i18n-10.4.9.tgz#10888e806402c1f82f5d1b55f799e570f9a10b8a" + integrity sha512-LYH4bcXZdS2heCQDzvASQS1fzFCozFUannsZzJeUV7FIQxW0GafTopZ79IcViMljVLLYRmddmvKqD1XzZu54fQ== dependencies: accept-language-parser "^1.5.0" chokidar "^3.5.3" @@ -7999,11 +8653,23 @@ nestjs-i18n@^10.4.5: js-yaml "^4.1.0" string-format "^2.0.0" +nestjs-pino@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/nestjs-pino/-/nestjs-pino-4.1.0.tgz#dc671b8cad5fa3862b49464d0a553b23215ac16a" + integrity sha512-I6zcddauD2TNMRbsraEIxNUvHcz0El5QRUYH5eY1+pBzj7R17U+Yoyypoc+akVdSLWJ1r0kDYAZPy2mlhXv6vw== + 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== +node-abi@^3.61.0: + version "3.67.0" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.67.0.tgz#1d159907f18d18e18809dbbb5df47ed2426a08df" + integrity sha512-bLn/fU/ALVBE9wj+p4Y21ZJWYFjUXLXPi/IewyLZkx3ApxKDNBWCKdReeKOtD8dWpOdDCeMyLh6ZewzcLsG2Nw== + dependencies: + semver "^7.3.5" + node-abort-controller@^3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.0.1.tgz" @@ -8057,11 +8723,16 @@ node-releases@^2.0.5: resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.5.tgz" integrity sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q== -normalize-path@3.0.0, normalize-path@^3.0.0, normalize-path@~3.0.0: +normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== +normalize-url@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" + integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== + 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" @@ -8088,6 +8759,11 @@ object-assign@^4, object-assign@^4.1.1: resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== +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.9.0: version "1.12.2" resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz" @@ -8098,6 +8774,11 @@ obuf@~1.1.2: resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== +on-exit-leak-free@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz#fed195c9ebddb7d9e4c3842f93f281ac8dadd3b8" + integrity sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA== + on-finished@2.4.1: version "2.4.1" resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz" @@ -8105,7 +8786,7 @@ on-finished@2.4.1: dependencies: ee-first "1.1.1" -on-headers@~1.0.1: +on-headers@~1.0.1, on-headers@~1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz" integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== @@ -8138,14 +8819,6 @@ onetime@^7.0.0: dependencies: mimic-function "^5.0.0" -opentelemetry-instrumentation-fetch-node@1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/opentelemetry-instrumentation-fetch-node/-/opentelemetry-instrumentation-fetch-node-1.2.3.tgz#beb24048bdccb1943ba2a5bbadca68020e448ea7" - integrity sha512-Qb11T7KvoCevMaSeuamcLsAD+pZnavkhDnlVL0kRozfhl42dKG5Q3anUklAFKJZjY3twLR+BnRa6DlwwkIE/+A== - dependencies: - "@opentelemetry/instrumentation" "^0.46.0" - "@opentelemetry/semantic-conventions" "^1.17.0" - optionator@^0.9.3: version "0.9.3" resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz" @@ -8173,11 +8846,23 @@ ora@5.4.1, ora@^5.4.1: strip-ansi "^6.0.0" wcwidth "^1.0.1" +os-filter-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/os-filter-obj/-/os-filter-obj-2.0.0.tgz#1c0b62d5f3a2442749a2d139e6dddee6e81d8d16" + integrity sha512-uksVLsqG3pVdzzPvmAHpBK0wKxYItuzZr7SziusRPoz67tGV8rL1szZ6IdeUrbqLjGDwApBtN29eEE3IqGHOjg== + dependencies: + arch "^2.1.0" + os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== +p-cancelable@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" + integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== + p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" @@ -8350,15 +9035,15 @@ path-scurry@^1.11.1: lru-cache "^10.2.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz" - integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== +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-to-regexp@3.2.0: - version "3.2.0" - resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.2.0.tgz" - integrity sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA== +path-to-regexp@3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-3.3.0.tgz#f7f31d32e8518c2660862b644414b6d5c63a611b" + integrity sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw== path-type@^4.0.0: version "4.0.0" @@ -8370,6 +9055,11 @@ pause@0.0.1: resolved "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz" integrity sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg== +peek-readable@^5.1.3: + version "5.3.1" + resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-5.3.1.tgz#9cc2c275cceda9f3d07a988f4f664c2080387dff" + integrity sha512-GVlENSDW6KHaXcd9zkZltB7tCLosKB/4Hg0fqBJkAoBgYG2Tn1xtMgXtSUuMU9AK/gCm/tTdT8mgAeF4YNeeqw== + pg-int8@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c" @@ -8424,16 +9114,92 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +picomatch@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.2.tgz#77c742931e8f3b8820946c76cd0c1f13730d1dab" + integrity sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg== + pidtree@~0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.6.0.tgz#90ad7b6d42d5841e69e0a2419ef38f8883aa057c" integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g== +pify@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== + +pino-abstract-transport@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz#de241578406ac7b8a33ce0d77ae6e8a0b3b68a60" + integrity sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw== + dependencies: + split2 "^4.0.0" + +pino-http@^10.3.0: + version "10.3.0" + resolved "https://registry.yarnpkg.com/pino-http/-/pino-http-10.3.0.tgz#c1999d9a5243748cdb1f66833a1f566c0cc40ef2" + integrity sha512-kaHQqt1i5S9LXWmyuw6aPPqYW/TjoDPizPs4PnDW4hSpajz2Uo/oisNliLf7We1xzpiLacdntmw8yaZiEkppQQ== + dependencies: + get-caller-file "^2.0.5" + pino "^9.0.0" + pino-std-serializers "^7.0.0" + process-warning "^4.0.0" + +pino-pretty@^11.3.0: + version "11.3.0" + resolved "https://registry.yarnpkg.com/pino-pretty/-/pino-pretty-11.3.0.tgz#390b3be044cf3d2e9192c7d19d44f6b690468f2e" + integrity sha512-oXwn7ICywaZPHmu3epHGU2oJX4nPmKvHvB/bwrJHlGcbEWaVcotkpyVHMKLKmiVryWYByNp0jpgAcXpFJDXJzA== + dependencies: + colorette "^2.0.7" + dateformat "^4.6.3" + fast-copy "^3.0.2" + fast-safe-stringify "^2.1.1" + help-me "^5.0.0" + joycon "^3.1.1" + minimist "^1.2.6" + on-exit-leak-free "^2.1.0" + pino-abstract-transport "^2.0.0" + pump "^3.0.0" + readable-stream "^4.0.0" + secure-json-parse "^2.4.0" + sonic-boom "^4.0.1" + strip-json-comments "^3.1.1" + +pino-std-serializers@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz#7c625038b13718dbbd84ab446bd673dc52259e3b" + integrity sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA== + +pino@^9.0.0: + version "9.5.0" + resolved "https://registry.yarnpkg.com/pino/-/pino-9.5.0.tgz#a7ef0fea868d22d52d8a4ce46e6e03c5dc46fdd6" + integrity sha512-xSEmD4pLnV54t0NOUN16yCl7RIB1c5UUOse5HSyEXtBp+FgFQyPeDutc+Q2ZO7/22vImV7VfEjH/1zV2QuqvYw== + dependencies: + atomic-sleep "^1.0.0" + fast-redact "^3.1.1" + on-exit-leak-free "^2.1.0" + pino-abstract-transport "^2.0.0" + pino-std-serializers "^7.0.0" + process-warning "^4.0.0" + quick-format-unescaped "^4.0.3" + real-require "^0.2.0" + safe-stable-stringify "^2.3.1" + sonic-boom "^4.0.1" + thread-stream "^3.0.0" + pirates@^4.0.4: version "4.0.5" resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz" integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== +piscina@^4.3.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/piscina/-/piscina-4.7.0.tgz#68936fc77128db00541366531330138e366dc851" + integrity sha512-b8hvkpp9zS0zsfa939b/jXbe64Z2gZv0Ha7FYPNUiDIB1y2AtxcOZdfP8xN8HFjUaqQiT9gRlfjAsoL8vdJ1Iw== + optionalDependencies: + "@napi-rs/nice" "^1.0.1" + pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz" @@ -8528,10 +9294,15 @@ process-nextick-args@~2.0.0: resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== -promise-coalesce@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/promise-coalesce/-/promise-coalesce-1.1.2.tgz#5d3bc4d0b2cf2e41e9df7cbeb6519b2a09459e3d" - integrity sha512-zLaJ9b8hnC564fnJH6NFSOGZYYdzrAJn2JUUIwzoQb32fG2QAakpDNM+CZo1km6keXkRXRM+hml1BFAPVnPkxg== +process-warning@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-4.0.0.tgz#581e3a7a1fb456c5f4fd239f76bce75897682d5a" + integrity sha512-/MyYDxttz7DfGMMHiysAsFE4qF+pQYAA8ziO/3NcRVrQ5fSk+Mns4QZA/oRPFzvcqNoVJXQNWNAsdwBXLUkQKw== + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== prompts@^2.0.1, prompts@~2.4.2: version "2.4.2" @@ -8554,6 +9325,11 @@ proxy-from-env@^1.1.0: resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== + pump@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" @@ -8562,14 +9338,9 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -punycode@^2.1.0: - version "2.1.1" - resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== - -punycode@^2.3.0: +punycode@^2.1.0, punycode@^2.3.0, punycode@^2.3.1: version "2.3.1" - resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== pure-rand@^6.0.0: @@ -8577,7 +9348,14 @@ pure-rand@^6.0.0: resolved "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.1.tgz" integrity sha512-t+x1zEHDjBwkDGY5v5ApnZ/utcd4XYDiJsaQQoptTXgUXX95sDg1elCdJghzicm7n2mbCBJ3uYWr6M22SO19rg== -qs@6.11.0, qs@^6.11.0: +qs@6.13.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" + +qs@^6.11.0: version "6.11.0" resolved "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz" integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== @@ -8589,6 +9367,16 @@ queue-microtask@^1.2.2: resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +quick-format-unescaped@^4.0.3: + version "4.0.4" + resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz#93ef6dd8d3453cbc7970dd614fad4c5954d6b5a7" + integrity sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg== + +quick-lru@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== + randombytes@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" @@ -8638,6 +9426,33 @@ readable-stream@^3.4.0: string_decoder "^1.1.1" util-deprecate "^1.0.1" +readable-stream@^3.6.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-stream@^4.0.0: + version "4.5.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.5.2.tgz#9e7fc4c45099baeed934bff6eb97ba6cf2729e09" + integrity sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g== + dependencies: + abort-controller "^3.0.0" + buffer "^6.0.3" + events "^3.3.0" + process "^0.11.10" + string_decoder "^1.3.0" + +readable-web-to-node-stream@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz#5d52bb5df7b54861fd48d015e93a2cb87b3ee0bb" + integrity sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw== + dependencies: + readable-stream "^3.6.0" + readdirp@~3.6.0: version "3.6.0" resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" @@ -8645,6 +9460,11 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" +real-require@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/real-require/-/real-require-0.2.0.tgz#209632dea1810be2ae063a6ac084fee7e33fba78" + integrity sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg== + redis-errors@^1.0.0, redis-errors@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/redis-errors/-/redis-errors-1.2.0.tgz#eb62d2adb15e4eaf4610c04afe1529384250abad" @@ -8698,6 +9518,11 @@ require-in-the-middle@^7.1.1: module-details-from-path "^1.0.3" resolve "^1.22.1" +resolve-alpn@^1.0.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" + integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== + resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz" @@ -8738,14 +9563,21 @@ resolve@^1.22.1: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -response-time@^2.3.2: - version "2.3.2" - resolved "https://registry.npmjs.org/response-time/-/response-time-2.3.2.tgz" - integrity sha512-MUIDaDQf+CVqflfTdQ5yam+aYCkXj1PY8fjlPDQ6ppxJlmgZb864pHtA750mayywNg8tx4rS7qH9JXd/OF+3gw== +response-time@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/response-time/-/response-time-2.3.3.tgz#cfec433ea1b286943a2b48b01c67a051a536b130" + integrity sha512-SsjjOPHl/FfrTQNgmc5oen8Hr1Jxpn6LlHNXxCIFdYMHuK1kMeYMobb9XN3mvxaGQm3dbegqYFMX4+GDORfbWg== dependencies: - depd "~1.1.0" + depd "~2.0.0" on-headers "~1.0.1" +responselike@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.1.tgz#9a0bc8fdc252f3fb1cca68b016591059ba1422bc" + integrity sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw== + dependencies: + lowercase-keys "^2.0.0" + restore-cursor@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz" @@ -8841,6 +9673,11 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +safe-stable-stringify@^2.3.1: + version "2.5.0" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz#4ca2f8e385f2831c432a719b108a3bf7af42a1dd" + integrity sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA== + "safer-buffer@>= 2.1.2 < 3": version "2.1.2" resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" @@ -8864,6 +9701,23 @@ schema-utils@^3.2.0: ajv "^6.12.5" ajv-keywords "^3.5.2" +secure-json-parse@^2.4.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/secure-json-parse/-/secure-json-parse-2.7.0.tgz#5a5f9cd6ae47df23dba3151edd06855d47e09862" + integrity sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw== + +semver-regex@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-4.0.5.tgz#fbfa36c7ba70461311f5debcb3928821eb4f9180" + integrity sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw== + +semver-truncate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/semver-truncate/-/semver-truncate-3.0.0.tgz#0e3b4825d4a4225d8ae6e7c72231182b42edba40" + integrity sha512-LJWA9kSvMolR51oDE6PN3kALBNaUdkxzAGcexw8gjMA8xr5zUqK0JiR3CgARSqanYF3Z1YHvsErb1KDgh+v7Rg== + dependencies: + semver "^7.3.5" + semver@^5.5.0: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" @@ -8912,10 +9766,10 @@ semver@^7.6.3: resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== -send@0.18.0: - version "0.18.0" - resolved "https://registry.npmjs.org/send/-/send-0.18.0.tgz" - integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== +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" @@ -8938,15 +9792,15 @@ serialize-javascript@^6.0.1: dependencies: randombytes "^2.1.0" -serve-static@1.15.0: - version "1.15.0" - resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz" - integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== +serve-static@1.16.2: + version "1.16.2" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.2.tgz#b6a5343da47f6bdd2673848bf45754941e803296" + integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw== dependencies: - encodeurl "~1.0.2" + encodeurl "~2.0.0" escape-html "~1.0.3" parseurl "~1.3.3" - send "0.18.0" + send "0.19.0" set-function-length@^1.2.1: version "1.2.2" @@ -9003,6 +9857,16 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" +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" @@ -9028,7 +9892,7 @@ sisteransi@^1.0.5: resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz" integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== -slash@^3.0.0: +slash@3.0.0, slash@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== @@ -9049,6 +9913,27 @@ slice-ansi@^7.1.0: ansi-styles "^6.2.1" is-fullwidth-code-point "^5.0.0" +sonic-boom@^4.0.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-4.2.0.tgz#e59a525f831210fa4ef1896428338641ac1c124d" + integrity sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww== + dependencies: + atomic-sleep "^1.0.0" + +sort-keys-length@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sort-keys-length/-/sort-keys-length-1.0.1.tgz#9cb6f4f4e9e48155a6aa0671edd336ff1479a188" + integrity sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw== + dependencies: + sort-keys "^1.0.0" + +sort-keys@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" + integrity sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg== + dependencies: + is-plain-obj "^1.0.0" + source-map-support@0.5.13: version "0.5.13" resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz" @@ -9065,7 +9950,7 @@ source-map-support@~0.5.20: buffer-from "^1.0.0" source-map "^0.6.0" -source-map@0.7.4, source-map@^0.7.4: +source-map@0.7.4, source-map@^0.7.3, source-map@^0.7.4: version "0.7.4" resolved "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz" integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== @@ -9116,12 +10001,12 @@ statuses@2.0.1: resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== -stop-only@^3.3.3: - version "3.3.3" - resolved "https://registry.yarnpkg.com/stop-only/-/stop-only-3.3.3.tgz#c259b8746f2058ce7bce9282ec47e49ba4a9afe8" - integrity sha512-DVvoYzfqucHGkJ8pEK8p7RRD41gJDPBkPIsiIrS8Ex7ncXhfwO3ddWfmUhbEG4CYE766tZKPvV5d5AkTB6DHEQ== +stop-only@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/stop-only/-/stop-only-3.4.1.tgz#c4c407a82270f0d540dd57b551c7bd16ce81a457" + integrity sha512-zMxczK6GJG5h1UZgk3VPJjOQJ0w7C9MclgqgcQPct8KgOWUItrUz7sK7MjoRQWvFJQJaDUpXrcV/OHyhqB9pZQ== dependencies: - debug "4.3.6" + debug "4.3.7" execa "0.11.0" minimist "1.2.8" @@ -9148,7 +10033,16 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + 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-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -9175,7 +10069,7 @@ string-width@^7.0.0: get-east-asian-width "^1.0.0" strip-ansi "^7.1.0" -string_decoder@^1.1.1: +string_decoder@^1.1.1, string_decoder@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== @@ -9189,7 +10083,14 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -9233,21 +10134,23 @@ strip-json-comments@^3.1.1: resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +strip-outer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-outer/-/strip-outer-2.0.0.tgz#c45c724ed9b1ff6be5f660503791404f4714084b" + integrity sha512-A21Xsm1XzUkK0qK1ZrytDUvqsQWict2Cykhvi0fBQntGG5JSprESasEyV1EZ/4CiR5WB5KjzLTrP/bO37B0wPg== + strnum@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz" integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA== -subscriptions-transport-ws@0.11.0: - version "0.11.0" - resolved "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.11.0.tgz" - integrity sha512-8D4C6DIH5tGiAIpp5I0wD/xRlNiZAPGHygzCe7VzyzUoxHtawzjNAY9SUTXU05/EY2NMY9/9GF0ycizkXr1CWQ== +strtok3@^7.0.0-alpha.9: + version "7.1.1" + resolved "https://registry.yarnpkg.com/strtok3/-/strtok3-7.1.1.tgz#f548fd9dc59d0a76d5567ff8c16be31221f29dfc" + integrity sha512-mKX8HA/cdBqMKUr0MMZAFssCkIGoZeSCMXgnt79yKxNFguMLVFgRe6wB+fsL0NmoHDbeyZXczy7vEPSoo3rkzg== dependencies: - backo2 "^1.0.2" - eventemitter3 "^3.1.0" - iterall "^1.2.1" - symbol-observable "^1.0.4" - ws "^5.2.0 || ^6.0.0 || ^7.0.0" + "@tokenizer/token" "^0.3.0" + peek-readable "^5.1.3" superagent@^9.0.1: version "9.0.2" @@ -9308,11 +10211,6 @@ symbol-observable@4.0.0: resolved "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz" integrity sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ== -symbol-observable@^1.0.4: - version "1.2.0" - resolved "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz" - integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== - tapable@^2.1.1, tapable@^2.2.0, tapable@^2.2.1: version "2.2.1" resolved "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz" @@ -9358,11 +10256,31 @@ text-table@^0.2.0: resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== +thread-stream@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/thread-stream/-/thread-stream-3.1.0.tgz#4b2ef252a7c215064507d4ef70c05a5e2d34c4f1" + integrity sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A== + dependencies: + real-require "^0.2.0" + "through@>=2.2.7 <3", through@^2.3.6: version "2.3.8" resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== +tinyexec@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.0.tgz#ed60cfce19c17799d4a241e06b31b0ec2bee69e6" + integrity sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg== + +tinyglobby@^0.2.10: + version "0.2.10" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.10.tgz#e712cf2dc9b95a1f5c5bbd159720e15833977a0f" + integrity sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew== + dependencies: + fdir "^6.4.2" + picomatch "^4.0.2" + tmp@^0.0.33: version "0.0.33" resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz" @@ -9392,6 +10310,14 @@ toidentifier@1.0.1: resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== +token-types@^5.0.0-alpha.2: + version "5.0.1" + resolved "https://registry.yarnpkg.com/token-types/-/token-types-5.0.1.tgz#aa9d9e6b23c420a675e55413b180635b86a093b4" + integrity sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg== + dependencies: + "@tokenizer/token" "^0.3.0" + ieee754 "^1.2.1" + tr46@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz" @@ -9409,6 +10335,13 @@ tree-kill@1.2.2: resolved "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz" integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== +trim-repeated@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/trim-repeated/-/trim-repeated-2.0.0.tgz#5d60556d6d40d9461b7c7e06c3ac20b6b1d50090" + integrity sha512-QUHBFTJGdOwmp0tbOG505xAgOp/YliZP/6UgafFXYZ26WT1bvQmSMJUvkeVSASuJJHbqsFbynTvkd5W8RBTipg== + dependencies: + escape-string-regexp "^5.0.0" + "true-myth@^4.1.0": version "4.1.1" resolved "https://registry.npmjs.org/true-myth/-/true-myth-4.1.1.tgz" @@ -9419,20 +10352,20 @@ ts-api-utils@^1.3.0: resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ== -ts-jest@^29.2.4: - version "29.2.4" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.2.4.tgz#38ccf487407d7a63054a72689f6f99b075e296e5" - integrity sha512-3d6tgDyhCI29HlpwIq87sNuI+3Q6GLTTCeYRHCs7vDz+/3GCMwEtV9jezLyl4ZtnBgx00I7hm8PCP8cTksMGrw== +ts-jest@^29.2.5: + version "29.2.5" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.2.5.tgz#591a3c108e1f5ebd013d3152142cb5472b399d63" + integrity sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA== dependencies: - bs-logger "0.x" + bs-logger "^0.2.6" ejs "^3.1.10" - fast-json-stable-stringify "2.x" + fast-json-stable-stringify "^2.1.0" jest-util "^29.0.0" json5 "^2.2.3" - lodash.memoize "4.x" - make-error "1.x" - semver "^7.5.3" - yargs-parser "^21.0.1" + lodash.memoize "^4.1.2" + make-error "^1.3.6" + semver "^7.6.3" + yargs-parser "^21.1.1" ts-loader@^9.5.1: version "9.5.1" @@ -9511,12 +10444,17 @@ tsconfig-paths@^4.1.2: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@2.6.2, tslib@^2.4.0, tslib@^2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" - integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== +tslib@2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01" + integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA== + +tslib@2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.0.tgz#d124c86c3c05a40a91e6fdea4021bd31d377971b" + integrity sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA== -tslib@2.6.3, tslib@^2.0.0: +tslib@^2.0.0: version "2.6.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== @@ -9526,6 +10464,11 @@ tslib@^2.1.0, tslib@^2.3.1: resolved "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz" integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== +tslib@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" @@ -9561,24 +10504,24 @@ typedarray@^0.0.6: resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== -typescript-eslint@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.1.0.tgz#c43a3543ab34c37b7f88deb4ff18b9764aed0b60" - integrity sha512-prB2U3jXPJLpo1iVLN338Lvolh6OrcCZO+9Yv6AR+tvegPPptYCDBIHiEEUdqRi8gAv2bXNKfMUrgAd2ejn/ow== +typescript-eslint@^8.12.2: + version "8.12.2" + resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.12.2.tgz#e273d69af30b478b1c410f4159d675ce7925f9a7" + integrity sha512-UbuVUWSrHVR03q9CWx+JDHeO6B/Hr9p4U5lRH++5tq/EbFq1faYZe50ZSBePptgfIKLEti0aPQ3hFgnPVcd8ZQ== dependencies: - "@typescript-eslint/eslint-plugin" "8.1.0" - "@typescript-eslint/parser" "8.1.0" - "@typescript-eslint/utils" "8.1.0" + "@typescript-eslint/eslint-plugin" "8.12.2" + "@typescript-eslint/parser" "8.12.2" + "@typescript-eslint/utils" "8.12.2" typescript@5.3.3: version "5.3.3" resolved "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz" integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw== -typescript@^5.5.4: - version "5.5.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.4.tgz#d9852d6c82bad2d2eda4fd74a5762a8f5909e9ba" - integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q== +typescript@^5.6.3: + version "5.6.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.3.tgz#5f3449e31c9d94febb17de03cc081dd56d81db5b" + integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw== uid@2.0.2: version "2.0.2" @@ -9587,10 +10530,10 @@ uid@2.0.2: dependencies: "@lukeed/csprng" "^1.0.0" -undici-types@~6.13.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.13.0.tgz#e3e79220ab8c81ed1496b5812471afd7cf075ea5" - integrity sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg== +undici-types@~6.19.8: + version "6.19.8" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" + integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== unicorn-magic@^0.1.0: version "0.1.0" @@ -9640,7 +10583,7 @@ utils-merge@1.0.1, utils-merge@^1.0.1: resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== -uuid@9.0.1, uuid@^9.0.0, uuid@^9.0.1: +uuid@^9.0.0, uuid@^9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== @@ -9664,11 +10607,6 @@ validator@^13.9.0: resolved "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz" integrity sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ== -value-or-promise@^1.0.12: - version "1.0.12" - resolved "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.12.tgz" - integrity sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q== - vary@^1, vary@~1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" @@ -9734,12 +10672,11 @@ webpack-sources@^3.2.3: resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== -webpack@5.93.0: - version "5.93.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.93.0.tgz#2e89ec7035579bdfba9760d26c63ac5c3462a5e5" - integrity sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA== +webpack@5.94.0: + version "5.94.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.94.0.tgz#77a6089c716e7ab90c1c67574a28da518a20970f" + integrity sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg== dependencies: - "@types/eslint-scope" "^3.7.3" "@types/estree" "^1.0.5" "@webassemblyjs/ast" "^1.12.1" "@webassemblyjs/wasm-edit" "^1.12.1" @@ -9748,7 +10685,7 @@ webpack@5.93.0: acorn-import-attributes "^1.9.5" browserslist "^4.21.10" chrome-trace-event "^1.0.2" - enhanced-resolve "^5.17.0" + enhanced-resolve "^5.17.1" es-module-lexer "^1.2.1" eslint-scope "5.1.1" events "^3.2.0" @@ -9811,7 +10748,7 @@ word@~0.3.0: resolved "https://registry.npmjs.org/word/-/word-0.3.0.tgz" integrity sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -9829,6 +10766,15 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz" @@ -9860,16 +10806,6 @@ write-file-atomic@^4.0.2: imurmurhash "^0.1.4" signal-exit "^3.0.7" -ws@8.14.2: - version "8.14.2" - resolved "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz" - integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g== - -"ws@^5.2.0 || ^6.0.0 || ^7.0.0": - version "7.5.9" - resolved "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz" - integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== - xdg-basedir@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-5.1.0.tgz#1efba19425e73be1bc6f2a6ceb52a3d2c884c0c9" @@ -9903,12 +10839,22 @@ yallist@4.0.0, yallist@^4.0.0: resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + integrity sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A== + yaml@^1.10.0: version "1.10.2" resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -yaml@^2.5.0, yaml@~2.5.0: +yaml@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.6.0.tgz#14059ad9d0b1680d0f04d3a60fe00f3a857303c3" + integrity sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ== + +yaml@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.5.0.tgz#c6165a721cf8000e91c36490a41d7be25176cf5d" integrity sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw== @@ -9918,7 +10864,7 @@ yargs-parser@21.1.1, yargs-parser@^21.1.1: resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== -yargs-parser@^21.0.0, yargs-parser@^21.0.1: +yargs-parser@^21.0.0: version "21.0.1" resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz" integrity sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==