diff --git a/.dockerignore b/.dockerignore index ec9673fe..a255a93f 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,3 +2,4 @@ *Dockerfile* node_modules dist +volumes diff --git a/.github/workflows/cd-dev.yml b/.github/workflows/cd-dev.yml new file mode 100644 index 00000000..cf0325a9 --- /dev/null +++ b/.github/workflows/cd-dev.yml @@ -0,0 +1,27 @@ +name: dev branch auto ci process script + +on: # 아래 job을 실행시킬 상황 + push: + branches: [dev] + +jobs: + deploy: + name: deploy + runs-on: ubuntu-latest # 실행될 인스턴스 OS와 버전 + + steps: + - name: excuting remote ssh commands + uses: appleboy/ssh-action@v0.1.6 # ssh 접속하는 오픈소스 + with: + host: ${{ secrets.REMOTE_IP_DEV }} # 인스턴스 IP + username: ${{ secrets.REMOTE_USER_DEV }} # 우분투 아이디 + key: ${{ secrets.REMOTE_PRIVATE_KEY_DEV }} # ec2 instance pem key + port: ${{ secrets.REMOTE_SSH_PORT_DEV }} # 접속포트 + passphrase: ${{ secrets.REMOTE_PASSPHRASE_DEV }} + script: | # 실행할 스크립트 + cd otlplus-nest-server + git pull origin dev + sudo docker stop otlplus-server-nest-dev + sudo docker rm otlplus-server-nest-dev + sudo docker rmi otlplus-server-nest-dev + sudo ./deploy.sh -e dev diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 64f47e99..fdedc280 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,9 +2,9 @@ name: CI on: push: - branches: [main] + branches: [main, dev, release] pull_request: - branches: [main] + branches: [main, dev, release] env: NODE_VERSION: 18 @@ -23,7 +23,7 @@ jobs: node-version: ${{ env.NODE_VERSION }} cache: 'npm' - name: Install dependencies - run: npm ci + run: npm ci --force - name: Check format run: npm run format:check @@ -40,7 +40,7 @@ jobs: node-version: ${{ env.NODE_VERSION }} cache: 'npm' - name: Install dependencies - run: npm ci + run: npm ci --force - name: Check lint run: npm run lint:check @@ -57,8 +57,10 @@ jobs: node-version: ${{ env.NODE_VERSION }} cache: 'npm' - name: Install dependencies - run: npm ci + run: npm ci --force - name: Copy env file run: cp ./env/.env.example ./env/.env.local + - name: Generate PrismaClient + run: npm run client:generate - name: Build run: npm run build diff --git a/.github/workflows/code-review.yml b/.github/workflows/code-review.yml new file mode 100644 index 00000000..048cdcf5 --- /dev/null +++ b/.github/workflows/code-review.yml @@ -0,0 +1,28 @@ +name: Code Review + +permissions: + contents: read + pull-requests: write + +on: + pull_request: + types: [opened, reopened, synchronize] + +jobs: + gpt-review: + if: ${{ contains(github.event.*.labels.*.name, 'gpt review') }} # Optional; to run only when a label is attached + runs-on: ubuntu-latest + steps: + - uses: anc95/ChatGPT-CodeReview@main + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + # Optional + LANGUAGE: Korean + OPENAI_API_ENDPOINT: https://api.openai.com/v1 + MODEL: gpt-4o-mini # https://platform.openai.com/docs/models + PROMPT: # example: Please check if there are any confusions or irregularities in the following code diff: + top_p: 1 # https://platform.openai.com/docs/api-reference/chat/create#chat/create-top_p + temperature: 1 # https://platform.openai.com/docs/api-reference/chat/create#chat/create-temperature + max_tokens: 10000 + MAX_PATCH_LENGTH: 10000 # if the patch/diff length is large than MAX_PATCH_LENGTH, will be ignored and won't review. By default, with no MAX_PATCH_LENGTH set, there is also no limit for the patch/diff length. diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 00000000..7950a445 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +v18.17.0 diff --git a/.prettierrc b/.prettierrc index dcb72794..a20502b7 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,4 +1,4 @@ { "singleQuote": true, "trailingComma": "all" -} \ No newline at end of file +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..0600eab3 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "editor.codeActionsOnSave": { + "source.organizeImports": "explicit" + } +} diff --git a/README.md b/README.md index a72f59f8..2d128761 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # OTL Plus Server -[![CI](https://github.com/sparcs-kaist/otlplus-server/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/sparcs-kaist/otlplus-server/actions/workflows/ci.yml) +[![CI](https://github.com/sparcs-kaist/otlplus-server/actions/workflows/ci.yml/badge.svg?branch=dev)](https://github.com/sparcs-kaist/otlplus-server/actions/workflows/ci.yml) ## How to run @@ -26,6 +26,30 @@ sudo docker compose up 또는 로컬에서 MySQL 5.7을 설치하여 연결할 수 있습니다. +### Node.js 설치 및 버전 관리 + +Node.js v18 을 설치합니다. + +버전 체크 + +```bash +node -v # v18.17.0 +npm -v # v9.x.x +``` + +(Tip) [nvm](https://github.com/nvm-sh/nvm) 설치 후, nvm을 이용하여 로컬 개발 환경의 node.js 버전을 설정하는 것을 권장합니다! + +```bash +nvm install 18 +nvm use 18 +``` + +아래 명령어를 사용하면 `.nvmrc` 설정 파일에 따라 자동으로 버전이 변경됩니다. + +```bash +nvm use # uses v18 +``` + ### NestJS 서버 실행 ```sh diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 00000000..8a47ce82 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +usage() { + echo "Usage: $0 -e value" + echo " -e: 현재 배포 환경을 입력하세요. (prod, dev, local)" +} + +NODE_ENV="" + +while getopts "e:" opt; do + case $opt in + e) + NODE_ENV=$OPTARG + echo "NODE_ENV: $NODE_ENV" + if [[ "$NODE_ENV" = "prod" ]]; then + COMPOSE_FILE="docker/docker-compose.prod.yml" + elif [[ "$NODE_ENV" = "dev" ]]; then + COMPOSE_FILE="docker/docker-compose.dev.yml" + elif [[ "$NODE_ENV" = "local" ]]; then + COMPOSE_FILE="docker/docker-compose.local.yml" + else + echo "Invalid environment: $NODE_ENV" 1>&2 + usage + exit 1 + fi + + if [[ ! -f "$COMPOSE_FILE" ]]; then + echo "Error: Compose file does not exist: $COMPOSE_FILE" 1>&2 + exit 1 + fi + + docker-compose -f "$COMPOSE_FILE" up -d + ;; + \?) + echo "Invalid option: -$OPTARG" 1>&2 + usage + exit 1 + ;; + esac +done + +if [ -z "$NODE_ENV" ]; then + echo "Error: Environment option is required." + usage + exit 1 +fi \ No newline at end of file diff --git a/docker/Dockerfile.server b/docker/Dockerfile.server index a07762ac..5454137b 100644 --- a/docker/Dockerfile.server +++ b/docker/Dockerfile.server @@ -1,6 +1,4 @@ -FROM node:18 - -ENV NODE_ENV=dev +FROM node:18 AS build RUN mkdir -p /var/www/otlplus-server WORKDIR /var/www/otlplus-server @@ -9,8 +7,16 @@ COPY package*.json ./ RUN npm install COPY . . -RUN npm run prisma-generate +RUN npm run client:generate RUN npm run build -EXPOSE 3000 -CMD [ "npm", "run", "start:dev" ] +FROM node:18-slim AS server +RUN apt-get update -y && apt-get install -y openssl + +COPY --from=build /var/www/otlplus-server/ /var/www/otlplus-server/ + +WORKDIR /var/www/otlplus-server + +RUN npm prune --production + +EXPOSE 8000 \ No newline at end of file diff --git a/docker-compose.yml b/docker/docker-compose-db-local.yml similarity index 90% rename from docker-compose.yml rename to docker/docker-compose-db-local.yml index 9053db72..3265dfa1 100644 --- a/docker-compose.yml +++ b/docker/docker-compose-db-local.yml @@ -3,12 +3,12 @@ version: '3.4' services: db: container_name: otlplus-server-db - image: mysql:5.7 + image: mysql:8.0.36 restart: on-failure ports: - '${OTLPLUS_DB_PORT:-43306}:3306' environment: - - MYSQL_ROOT_HOSTS=% + - MYSQL_ROOT_HOSTS=admin - MYSQL_ROOT_PASSWORD=${OTLPLUS_DB_PASSWORD:-password} - MYSQL_DATABASE=otlplus - TZ=Asia/Seoul diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index 9dc07ed5..e4f0efdf 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -1,36 +1,23 @@ version: '3.4' services: - db: - container_name: otlplus-server-db - platform: linux/amd64 - image: mysql:5.7 - restart: on-failure - ports: - - "43306:3306" - environment: - - MYSQL_ROOT_HOSTS=% - - MYSQL_ROOT_PASSWORD=${OTLPLUS_DB_PASSWORD:-password} - - MYSQL_DATABASE=otlplus - - TZ=Asia/Seoul - volumes: - - ../volumes/db:/var/lib/mysql - - ../volumes/dump:/dump - command: | - --sql_mode=NO_ENGINE_SUBSTITUTION --default_storage_engine=InnoDB - --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci back: - depends_on: - - db - container_name: otlplus-server + container_name: otlplus-server-nest-dev platform: linux/amd64 + env_file: + - ../env/.env.dev + environment: + - NODE_ENV=dev + - DOCKER_DEPLOY=true build: context: .. dockerfile: ./docker/Dockerfile.server + image: otlplus-server-nest-dev restart: always tty: true ports: - - "43000:3000" + - '8080:8000' volumes: - - "/etc/timezone:/etc/timezone:ro" + - '/etc/timezone:/etc/timezone:ro' working_dir: /var/www/otlplus-server + command: node dist/src/bootstrap/bootstrap.js diff --git a/docker/docker-compose.local.yml b/docker/docker-compose.local.yml new file mode 100644 index 00000000..07570442 --- /dev/null +++ b/docker/docker-compose.local.yml @@ -0,0 +1,26 @@ +version: '3.4' + +services: + back: + container_name: otlplus-server-nest-local + platform: linux/amd64 + env_file: + - ../env/.env.local + environment: + - NODE_ENV=local + - DOCKER_DEPLOY=true + build: + context: .. + dockerfile: ./docker/Dockerfile.server + image: otlplus-server-nest-local + restart: always + tty: true + ports: + - '58000:8000' + expose: + - '8000' + volumes: + - '/etc/timezone:/etc/timezone:ro' + network_mode: 'host' + working_dir: /var/www/otlplus-server + command: node dist/src/bootstrap/bootstrap.js diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml new file mode 100644 index 00000000..01ad13f8 --- /dev/null +++ b/docker/docker-compose.prod.yml @@ -0,0 +1,23 @@ +version: '3.4' + +services: + back: + container_name: otlplus-server-nest-prod + platform: linux/amd64 + env_file: + - ../env/.env.prod + environment: + - NODE_ENV=prod + - DOCKER_DEPLOY=true + build: + context: .. + dockerfile: ./docker/Dockerfile.server + image: otlplus-server-nest-prod + restart: always + tty: true + ports: + - '58000:8000' + volumes: + - '/etc/timezone:/etc/timezone:ro' + working_dir: /var/www/otlplus-server + command: node dist/src/bootstrap/bootstrap.js diff --git a/local-SSO-swap.ts b/local-SSO-swap.ts new file mode 100644 index 00000000..bec6f8d5 --- /dev/null +++ b/local-SSO-swap.ts @@ -0,0 +1,167 @@ +import settings from './src/settings'; +import { PrismaClient, session_userprofile } from '@prisma/client'; +import * as readline from 'readline'; +import { prompt } from 'enquirer'; + +// readline.Interface 인스턴스 생성 +class PrismaClientMock extends PrismaClient { + constructor() { + const ormOption = settings().ormconfig(); + super(ormOption); + } +} + +const prisma = new PrismaClientMock(); + +const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, +}); + +function question(query: string): Promise { + return new Promise((resolve) => { + rl.question(query, (input) => resolve(input)); + }); +} + +function objectToStringWithTabs(obj: object): string { + // Object.entries를 사용하여 객체의 키-값 쌍을 배열로 변환하고, + // map 함수를 사용하여 각 키-값 쌍을 문자열로 변환합니다. + // 각 필드는 "\t"로 구분됩니다. + const keyValuePairs = Object.entries(obj).map(([key, value]) => { + // 여기서 value가 객체인 경우, 재귀적으로 처리하거나 JSON.stringify 등을 사용할 수 있습니다. + return `${value}`; + }); + + // Array.join 메서드를 사용하여 모든 필드를 "\t"로 연결합니다. + return keyValuePairs.join('\t'); +} + +function keysToStringWithTabs(obj: object): string { + // Object.keys 메서드로 객체의 모든 키를 배열로 가져옵니다. + const keys = Object.keys(obj); + + // Array.join 메서드로 모든 키를 "\t"로 구분하여 하나의 문자열로 합칩니다. + return keys.join('\t'); +} + +async function chooseUser(users: session_userprofile[]) { + console.log(keysToStringWithTabs(users[0])); + + const choices = users.map((user) => ({ + name: objectToStringWithTabs(user), + value: user.id, + })); + + const response: { userId: string } = await prompt({ + type: 'select', + name: 'userId', + message: 'Choose a user:', + choices, + }); + + const userId = Number(response.userId.split('\t')[0]); + + console.log(`Selected User ID: ${userId}`); + return userId; +} + +async function main() { + // email1과 email2를 순차적으로 입력받기 + const email1 = await question('Enter email1: '); + const email2 = await question('Enter email2: '); + // main 함수 호출 + + await update_sid(email1, email2); + + // readline 인터페이스 종료 + rl.close(); + return true; +} + +async function update_sid(email1: string, email2: string) { + try { + await prisma.$connect(); + console.log('db connect'); + + const users1 = await prisma.session_userprofile.findMany({ + where: { + email: email1, + }, + orderBy: { + date_joined: 'desc', + }, + }); + if (users1 == null || users1.length == 0) { + return; + } + const user1_id = await chooseUser(users1); + + const users2 = await prisma.session_userprofile.findMany({ + where: { + email: email2, + }, + orderBy: { + date_joined: 'desc', + }, + }); + + if (users2 == null || users2.length == 0) { + return; + } + const user2_id = await chooseUser(users2); + + const user1 = await prisma.session_userprofile.findUnique({ + where: { + id: user1_id, + }, + }); + + const user2 = await prisma.session_userprofile.findUnique({ + where: { + id: user2_id, + }, + }); + + if (user1 == null || user2 == null) { + console.log('no users with given id'); + } + await prisma.$transaction(async (tx) => { + await tx.session_userprofile.update({ + where: { + id: user1?.id, + }, + data: { + sid: user2?.sid, + }, + }); + + await tx.session_userprofile.update({ + where: { + id: user2?.id, + }, + data: { + sid: user1?.sid, + }, + }); + + console.log('Complete Swap Sid'); + return true; + }); + } catch (e) { + console.error(e); + } finally { + await prisma.$disconnect(); + } +} + +main() + .then(() => { + console.log('done'); + }) + .catch((e) => { + console.error(e); + }) + .finally(() => { + process.exit(0); + }); diff --git a/package-lock.json b/package-lock.json index 92cc4d3f..fde61e90 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,37 +1,47 @@ { "name": "otl-nest", - "version": "0.0.1", - "lockfileVersion": 2, + "version": "3.3.1", + "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "otl-nest", - "version": "0.0.1", + "version": "3.3.1", "license": "UNLICENSED", "dependencies": { + "@nestjs-cls/transactional": "^2.4.1", + "@nestjs-cls/transactional-adapter-prisma": "^1.2.3", "@nestjs/common": "^9.0.0", "@nestjs/core": "^9.0.0", "@nestjs/jwt": "^10.0.3", "@nestjs/passport": "^9.0.3", "@nestjs/platform-express": "^9.0.0", - "@prisma/client": "4.16.0", + "@nestjs/swagger": "^7.1.1", + "@prisma/client": "^5.18.0", "@types/cookie-parser": "^1.4.3", "@types/express-session": "^1.17.7", "@types/morgan": "^1.9.4", "@types/passport-jwt": "^3.0.6", "@types/passport-local": "^1.0.34", + "@types/sharp": "^0.32.0", "axios": "^1.4.0", "bcrypt": "^5.1.0", + "canvas": "^2.11.2", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "cookie-parser": "^1.4.6", - "dotenv": "^16.0.3", + "csurf": "^1.10.0", + "date-fns": "^3.6.0", + "dotenv": "^16.4.5", "dotenv-cli": "^7.2.1", + "enquirer": "^2.4.1", "express-session": "^1.17.3", + "ical-generator": "^7.1.0", + "moment-timezone": "^0.5.45", "morgan": "^1.10.0", + "nestjs-cls": "^4.4.0", "passport": "^0.6.0", "passport-jwt": "^4.0.1", - "prisma": "4.16.0", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", "rxjs": "^7.2.0" @@ -41,54 +51,62 @@ "@nestjs/schematics": "^9.0.0", "@nestjs/testing": "^9.0.0", "@types/bcrypt": "^5.0.0", + "@types/csurf": "^1.11.5", "@types/express": "^4.17.13", + "@types/inquirer": "^9.0.7", "@types/jest": "28.1.4", "@types/node": "^16.18.23", - "@types/supertest": "^2.0.11", + "@types/supertest": "^6.0.2", "@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/parser": "^5.0.0", + "cross-env": "^7.0.3", "eslint": "^8.0.1", "eslint-config-prettier": "^8.3.0", "husky": "^8.0.0", "jest": "28.1.2", "lint-staged": "^13.2.3", "prettier": "^2.3.2", + "prisma": "^5.18.0", "source-map-support": "^0.5.20", - "supertest": "^6.1.3", - "ts-jest": "28.0.5", + "supertest": "^7.0.0", + "ts-jest": "^28.0.5", "ts-loader": "^9.2.3", "ts-node": "^10.9.1", "tsconfig-paths": "4.0.0", - "typescript": "^4.3.5" + "typescript": "^5.0.0" + }, + "engines": { + "node": ">=18.20.4", + "npm": ">=10.7.0" } }, "node_modules/@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dev": true, "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@angular-devkit/core": { - "version": "15.2.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-15.2.4.tgz", - "integrity": "sha512-yl+0j1bMwJLKShsyCXw77tbJG8Sd21+itisPLL2MgEpLNAO252kr9zG4TLlFRJyKVftm2l1h78KjqvM5nbOXNg==", + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-16.0.1.tgz", + "integrity": "sha512-2uz98IqkKJlgnHbWQ7VeL4pb+snGAZXIama2KXi+k9GsRntdcw+udX8rL3G9SdUGUF+m6+147Y1oRBMHsO/v4w==", "dev": true, "dependencies": { "ajv": "8.12.0", "ajv-formats": "2.1.1", "jsonc-parser": "3.2.0", - "rxjs": "6.6.7", + "rxjs": "7.8.1", "source-map": "0.7.4" }, "engines": { - "node": "^14.20.0 || ^16.13.0 || >=18.10.0", + "node": "^16.14.0 || >=18.10.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, @@ -101,50 +119,32 @@ } } }, - "node_modules/@angular-devkit/core/node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/@angular-devkit/core/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, "node_modules/@angular-devkit/schematics": { - "version": "15.2.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-15.2.4.tgz", - "integrity": "sha512-/W7/vvn59PAVLzhcvD4/N/E8RDhub8ny1A7I96LTRjC5o+yvVV16YJ4YJzolrRrIEN01KmLVQJ9A58VCaweMgw==", + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-16.0.1.tgz", + "integrity": "sha512-A9D0LTYmiqiBa90GKcSuWb7hUouGIbm/AHbJbjL85WLLRbQA2PwKl7P5Mpd6nS/ZC0kfG4VQY3VOaDvb3qpI9g==", "dev": true, "dependencies": { - "@angular-devkit/core": "15.2.4", + "@angular-devkit/core": "16.0.1", "jsonc-parser": "3.2.0", - "magic-string": "0.29.0", + "magic-string": "0.30.0", "ora": "5.4.1", - "rxjs": "6.6.7" + "rxjs": "7.8.1" }, "engines": { - "node": "^14.20.0 || ^16.13.0 || >=18.10.0", + "node": "^16.14.0 || >=18.10.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } }, "node_modules/@angular-devkit/schematics-cli": { - "version": "15.2.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-15.2.4.tgz", - "integrity": "sha512-QTTKEH5HOkxvQtCxb2Lna2wubehkaIzA6DKUBISijPQliLomw74tzc7lXCywmMqRTbQPVRLG3kBK97hR4x67nA==", + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-16.0.1.tgz", + "integrity": "sha512-6KLA125dpgd6oJGtiO2JpZAb92uOG3njQGIt7NFcuQGW/5GO7J41vMXH9cBAfdtbV8SIggSmR/cIEE9ijfj6YQ==", "dev": true, "dependencies": { - "@angular-devkit/core": "15.2.4", - "@angular-devkit/schematics": "15.2.4", + "@angular-devkit/core": "16.0.1", + "@angular-devkit/schematics": "16.0.1", "ansi-colors": "4.1.3", "inquirer": "8.2.4", "symbol-observable": "4.0.0", @@ -154,7 +154,7 @@ "schematics": "bin/schematics.js" }, "engines": { - "node": "^14.20.0 || ^16.13.0 || >=18.10.0", + "node": "^16.14.0 || >=18.10.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } @@ -185,66 +185,49 @@ "node": ">=12.0.0" } }, - "node_modules/@angular-devkit/schematics/node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/@angular-devkit/schematics/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, "node_modules/@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", + "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", "dev": true, "dependencies": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.24.7", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz", - "integrity": "sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.2.tgz", + "integrity": "sha512-bYcppcpKBvX4znYaPEeFau03bp89ShqNMLs+rmdptMw+heSZh9+z84d2YG+K7cYLbWwzdjtDoW/uqZmPjulClQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.3.tgz", - "integrity": "sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", + "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.3", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.21.2", - "@babel/helpers": "^7.21.0", - "@babel/parser": "^7.21.3", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.3", - "@babel/types": "^7.21.3", - "convert-source-map": "^1.7.0", + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-module-transforms": "^7.25.2", + "@babel/helpers": "^7.25.0", + "@babel/parser": "^7.25.0", + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.2", + "@babel/types": "^7.25.2", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -254,6 +237,12 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, "node_modules/@babel/core/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -264,51 +253,34 @@ } }, "node_modules/@babel/generator": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.3.tgz", - "integrity": "sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", + "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", "dev": true, "dependencies": { - "@babel/types": "^7.21.3", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", + "@babel/types": "^7.25.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", - "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", + "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.21.3", + "@babel/compat-data": "^7.25.2", + "@babel/helper-validator-option": "^7.24.8", + "browserslist": "^4.23.1", "lru-cache": "^5.1.1", - "semver": "^6.3.0" + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { @@ -320,154 +292,109 @@ "semver": "bin/semver.js" } }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", - "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", - "dev": true, - "dependencies": { - "@babel/template": "^7.20.7", - "@babel/types": "^7.21.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", - "dev": true, - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", + "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", - "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", + "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.2", - "@babel/types": "^7.21.2" + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-simple-access": "^7.24.7", + "@babel/helper-validator-identifier": "^7.24.7", + "@babel/traverse": "^7.25.2" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", - "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", + "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", - "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", + "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", + "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", + "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", - "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", + "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", - "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz", + "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==", "dev": true, "dependencies": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0" + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", + "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "@babel/helper-validator-identifier": "^7.24.7", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" @@ -545,10 +472,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.3.tgz", - "integrity": "sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.3.tgz", + "integrity": "sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==", "dev": true, + "dependencies": { + "@babel/types": "^7.25.2" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -592,6 +522,36 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.7.tgz", + "integrity": "sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-import-meta": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", @@ -688,6 +648,21 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-top-level-await": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", @@ -704,12 +679,12 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", - "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.7.tgz", + "integrity": "sha512-c/+fVeJBB0FeKsFvwytYiUD+LBvhHjGSI0g446PRGdSVGZLRNArBUno2PETbAly3tpiNAQR5XaZ+JslxkotsbA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.19.0" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -719,34 +694,31 @@ } }, "node_modules/@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", + "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/code-frame": "^7.24.7", + "@babel/parser": "^7.25.0", + "@babel/types": "^7.25.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.3.tgz", - "integrity": "sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.3", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.3", - "@babel/types": "^7.21.3", - "debug": "^4.1.0", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.3.tgz", + "integrity": "sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/parser": "^7.25.3", + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.2", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { @@ -763,13 +735,13 @@ } }, "node_modules/@babel/types": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.3.tgz", - "integrity": "sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.2.tgz", + "integrity": "sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", + "@babel/helper-string-parser": "^7.24.8", + "@babel/helper-validator-identifier": "^7.24.7", "to-fast-properties": "^2.0.0" }, "engines": { @@ -830,23 +802,23 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.0.tgz", - "integrity": "sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ==", + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", + "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.2.tgz", - "integrity": "sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.5.1", + "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -884,22 +856,23 @@ "dev": true }, "node_modules/@eslint/js": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.37.0.tgz", - "integrity": "sha512-x5vzdtOOGgFVDCUs81QRB2+liax8rFg3+7hqM+QhBG0/G3F1ZsoYl97UrqgHgQ9KKT7G6c4V+aTUCgu/n22v1A==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "deprecated": "Use @eslint/config-array instead", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", "minimatch": "^3.0.5" }, "engines": { @@ -920,11 +893,48 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", "dev": true }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", + "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", + "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -1313,88 +1323,75 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@lukeed/csprng": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.0.1.tgz", - "integrity": "sha512-uSvJdwQU5nK+Vdf6zxcWAY2A8r7uqe+gePwLWzJ+fsQehq18pc0I2hJKwypZ2aLM90+Er9u1xn4iLJPZ+xlL4g==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", + "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==", "engines": { "node": ">=8" } }, "node_modules/@mapbox/node-pre-gyp": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz", - "integrity": "sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", "dependencies": { "detect-libc": "^2.0.0", "https-proxy-agent": "^5.0.0", @@ -1410,15 +1407,49 @@ "node-pre-gyp": "bin/node-pre-gyp" } }, + "node_modules/@microsoft/tsdoc": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.0.tgz", + "integrity": "sha512-HZpPoABogPvjeJOdzCOSJsXeL/SMCBgBZMVC3X3d7YYp2gf31MfxhUoYUNwf1ERPJOnQc0wkFn9trqI6ZEdZuA==" + }, + "node_modules/@nestjs-cls/transactional": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@nestjs-cls/transactional/-/transactional-2.4.2.tgz", + "integrity": "sha512-bQZ4Xo5BOPnmKcBk/Qsh/VX8kHr+fKTfJ6Fcxu/RGmxzSwjRVgShNu0E57V8CZkZJ6YuIKJoDQHAoIbeRIffbQ==", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@nestjs/common": "> 7.0.0 < 11", + "@nestjs/core": "> 7.0.0 < 11", + "nestjs-cls": "^4.4.1", + "reflect-metadata": "*", + "rxjs": ">= 7" + } + }, + "node_modules/@nestjs-cls/transactional-adapter-prisma": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@nestjs-cls/transactional-adapter-prisma/-/transactional-adapter-prisma-1.2.4.tgz", + "integrity": "sha512-W/Ej+T3QG81//d4YuXsuHnV9WFvWTQBQk1BSiowyq7d812lyr3zk8ZMwmAhB7JAY80cJMeBY6i3TBlP0QVIczQ==", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@nestjs-cls/transactional": "^2.4.2", + "@prisma/client": "> 4 < 6", + "nestjs-cls": "^4.4.1", + "prisma": "> 4 < 6" + } + }, "node_modules/@nestjs/cli": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-9.3.0.tgz", - "integrity": "sha512-v/E8Y3zFk30+FljETvPgpoGIUiOfWuOe6WUFw3ExGfDeWrF/A8ceupDHPWNknBAqvNtz2kVrWu5mwsZUEKGIgg==", + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-9.5.0.tgz", + "integrity": "sha512-Z7q+3vNsQSG2d2r2Hl/OOj5EpfjVx3OfnJ9+KuAsOdw1sKLm7+Zc6KbhMFTd/eIvfx82ww3Nk72xdmfPYCulWA==", "dev": true, "dependencies": { - "@angular-devkit/core": "15.2.4", - "@angular-devkit/schematics": "15.2.4", - "@angular-devkit/schematics-cli": "15.2.4", + "@angular-devkit/core": "16.0.1", + "@angular-devkit/schematics": "16.0.1", + "@angular-devkit/schematics-cli": "16.0.1", "@nestjs/schematics": "^9.0.4", "chalk": "4.1.2", "chokidar": "3.5.3", @@ -1429,14 +1460,14 @@ "node-emoji": "1.11.0", "ora": "5.4.1", "os-name": "4.0.1", - "rimraf": "4.4.0", + "rimraf": "4.4.1", "shelljs": "0.8.5", "source-map-support": "0.5.21", "tree-kill": "1.2.2", - "tsconfig-paths": "4.1.2", + "tsconfig-paths": "4.2.0", "tsconfig-paths-webpack-plugin": "4.0.1", "typescript": "4.9.5", - "webpack": "5.76.2", + "webpack": "5.82.1", "webpack-node-externals": "3.0.0" }, "bin": { @@ -1456,13 +1487,13 @@ } }, "node_modules/@nestjs/cli/node_modules/glob": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.2.tgz", - "integrity": "sha512-BTv/JhKXFEHsErMte/AnfiSv8yYOLLiyH2lTg8vn02O21zWFgHPTfxtgn1QRe7NRgggUhC8hacR2Re94svHqeA==", + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", + "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", - "minimatch": "^7.4.1", + "minimatch": "^8.0.2", "minipass": "^4.2.4", "path-scurry": "^1.6.1" }, @@ -1474,24 +1505,33 @@ } }, "node_modules/@nestjs/cli/node_modules/minimatch": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.3.tgz", - "integrity": "sha512-5UB4yYusDtkRPbRiy1cqZ1IpGNcJCGlEMG17RKzPddpyiPKoCdwohbED8g4QXT0ewCt8LTkQXuljsUfQ3FKM4A==", + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.4.tgz", + "integrity": "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@nestjs/cli/node_modules/minipass": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", + "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/@nestjs/cli/node_modules/rimraf": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.4.0.tgz", - "integrity": "sha512-X36S+qpCUR0HjXlkDe4NAOhS//aHH0Z+h8Ckf2auGJk3PTnx5rLmrHkwNdbVQuCSUhOyFrlRvFEllZOYE+yZGQ==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.4.1.tgz", + "integrity": "sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==", "dev": true, "dependencies": { "glob": "^9.2.0" @@ -1516,9 +1556,9 @@ } }, "node_modules/@nestjs/cli/node_modules/tsconfig-paths": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.1.2.tgz", - "integrity": "sha512-uhxiMgnXQp1IR622dUXI+9Ehnws7i/y6xvpZB9IbUVOPy0muvdvgXeZOn88UcGPiT98Vp3rJPTa8bFoalZ3Qhw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", "dev": true, "dependencies": { "json5": "^2.2.2", @@ -1529,23 +1569,36 @@ "node": ">=6" } }, + "node_modules/@nestjs/cli/node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, "node_modules/@nestjs/cli/node_modules/webpack": { - "version": "5.76.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.2.tgz", - "integrity": "sha512-Th05ggRm23rVzEOlX8y67NkYCHa9nTNcwHPBhdg+lKG+mtiW7XgggjAeeLnADAe7mLjJ6LUNfgHAuRRh+Z6J7w==", + "version": "5.82.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.82.1.tgz", + "integrity": "sha512-C6uiGQJ+Gt4RyHXXYt+v9f+SN1v83x68URwgxNQ98cvH8kxiuywWGP4XeNZ1paOzZ63aY3cTciCEQJNFUljlLw==", "dev": true, "dependencies": { "@types/eslint-scope": "^3.7.3", - "@types/estree": "^0.0.51", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", + "@types/estree": "^1.0.0", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", "acorn": "^8.7.1", "acorn-import-assertions": "^1.7.6", "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.10.0", - "es-module-lexer": "^0.9.0", + "enhanced-resolve": "^5.14.0", + "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", @@ -1554,9 +1607,9 @@ "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", + "schema-utils": "^3.1.2", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", + "terser-webpack-plugin": "^5.3.7", "watchpack": "^2.4.0", "webpack-sources": "^3.2.3" }, @@ -1577,13 +1630,13 @@ } }, "node_modules/@nestjs/common": { - "version": "9.3.12", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-9.3.12.tgz", - "integrity": "sha512-NtrUG2VgCbhmZEO1yRt/Utq16uFRV+xeHAOtdYIsfHGG0ssAV2lVLlvFFAQYh0SQ+KuYY1Gsxd3GK2JFoJCNqQ==", + "version": "9.4.3", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-9.4.3.tgz", + "integrity": "sha512-Gd6D4IaYj01o14Bwv81ukidn4w3bPHCblMUq+SmUmWLyosK+XQmInCS09SbDDZyL8jy86PngtBLTdhJ2bXSUig==", "dependencies": { "iterare": "1.2.1", - "tslib": "2.5.0", - "uid": "2.0.1" + "tslib": "2.5.3", + "uid": "2.0.2" }, "funding": { "type": "opencollective", @@ -1609,17 +1662,17 @@ } }, "node_modules/@nestjs/core": { - "version": "9.3.12", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-9.3.12.tgz", - "integrity": "sha512-Qe0ZjJo7bOlfudn7KHLppYrt5i4k1nR1+9d5ppYat2bb5knCIT4kIqblj666n+22/2zvsHRiTo015cLyLKsLRQ==", + "version": "9.4.3", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-9.4.3.tgz", + "integrity": "sha512-Qi63+wi55Jh4sDyaj5Hhx2jOpKqT386aeo+VOKsxnd+Ql9VvkO/FjmuwBGUyzkJt29ENYc+P0Sx/k5LtstNpPQ==", "hasInstallScript": true, "dependencies": { "@nuxtjs/opencollective": "0.3.2", "fast-safe-stringify": "2.1.1", "iterare": "1.2.1", "path-to-regexp": "3.2.0", - "tslib": "2.5.0", - "uid": "2.0.1" + "tslib": "2.5.3", + "uid": "2.0.2" }, "funding": { "type": "opencollective", @@ -1646,23 +1699,34 @@ } }, "node_modules/@nestjs/jwt": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-10.0.3.tgz", - "integrity": "sha512-WO8MI3uEMOFKpbO+SAg6l4aRCr+9KvaL+raFMZaXuEUDphXek6pqdox+4tex9242pNSJUA0trfAMaiy/yVrXQg==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-10.2.0.tgz", + "integrity": "sha512-x8cG90SURkEiLOehNaN2aRlotxT0KZESUliOPKKnjWiyJOcWurkF3w345WOX0P4MgFzUjGoZ1Sy0aZnxeihT0g==", "dependencies": { - "@types/jsonwebtoken": "9.0.1", - "jsonwebtoken": "9.0.0" + "@types/jsonwebtoken": "9.0.5", + "jsonwebtoken": "9.0.2" }, "peerDependencies": { - "@nestjs/common": "^8.0.0 || ^9.0.0" + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0" } }, - "node_modules/@nestjs/jwt/node_modules/@types/jsonwebtoken": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.1.tgz", - "integrity": "sha512-c5ltxazpWabia/4UzhIoaDcIza4KViOQhdbjRlfcIGVnsE3c3brkz9Z+F/EeJIECOQP7W7US2hNE930cWWkPiw==", - "dependencies": { - "@types/node": "*" + "node_modules/@nestjs/mapped-types": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.5.tgz", + "integrity": "sha512-bSJv4pd6EY99NX9CjBIyn4TVDoSit82DUZlL4I3bqNfy5Gt+gXTa86i3I/i0iIV9P4hntcGM5GyO+FhZAhxtyg==", + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "class-transformer": "^0.4.0 || ^0.5.0", + "class-validator": "^0.13.0 || ^0.14.0", + "reflect-metadata": "^0.1.12 || ^0.2.0" + }, + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } } }, "node_modules/@nestjs/passport": { @@ -1675,15 +1739,15 @@ } }, "node_modules/@nestjs/platform-express": { - "version": "9.3.12", - "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-9.3.12.tgz", - "integrity": "sha512-iQToH9rnZHmm3a2YDKLEN7weU2qC/EVOBnuwTf1lIkqB48yLxlykSJu3KmgtlUUNDt2/HY527QIo+GZSZfCLyg==", + "version": "9.4.3", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-9.4.3.tgz", + "integrity": "sha512-FpdczWoRSC0zz2dNL9u2AQLXKXRVtq4HgHklAhbL59X0uy+mcxhlSThG7DHzDMkoSnuuHY8ojDVf7mDxk+GtCw==", "dependencies": { "body-parser": "1.20.2", "cors": "2.8.5", "express": "4.18.2", "multer": "1.4.4-lts.1", - "tslib": "2.5.0" + "tslib": "2.5.3" }, "funding": { "type": "opencollective", @@ -1695,118 +1759,59 @@ } }, "node_modules/@nestjs/schematics": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-9.0.4.tgz", - "integrity": "sha512-egurCfAc4e5i1r2TmeAF0UrOKejFmT5oTdv4b7HcOVPupc3QGU7CbEfGleL3mkM5AjrixTQeMxU9bJ00ttAbGg==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-9.2.0.tgz", + "integrity": "sha512-wHpNJDPzM6XtZUOB3gW0J6mkFCSJilzCM3XrHI1o0C8vZmFE1snbmkIXNyoi1eV0Nxh1BMymcgz5vIMJgQtTqw==", "dev": true, "dependencies": { - "@angular-devkit/core": "15.0.4", - "@angular-devkit/schematics": "15.0.4", - "fs-extra": "11.1.0", + "@angular-devkit/core": "16.0.1", + "@angular-devkit/schematics": "16.0.1", "jsonc-parser": "3.2.0", "pluralize": "8.0.0" }, "peerDependencies": { - "typescript": "^4.3.5" + "typescript": ">=4.3.5" } }, - "node_modules/@nestjs/schematics/node_modules/@angular-devkit/core": { - "version": "15.0.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-15.0.4.tgz", - "integrity": "sha512-4ITpRAevd652SxB+qNesIQ9qfbm7wT5UBU5kJOPPwGL77I21g8CQpkmV1n5VSacPvC9Zbz90feOWexf7w7JzcA==", - "dev": true, + "node_modules/@nestjs/swagger": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-7.4.0.tgz", + "integrity": "sha512-dCiwKkRxcR7dZs5jtrGspBAe/nqJd1AYzOBTzw9iCdbq3BGrLpwokelk6lFZPe4twpTsPQqzNKBwKzVbI6AR/g==", "dependencies": { - "ajv": "8.11.0", - "ajv-formats": "2.1.1", - "jsonc-parser": "3.2.0", - "rxjs": "6.6.7", - "source-map": "0.7.4" - }, - "engines": { - "node": "^14.20.0 || ^16.13.0 || >=18.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" + "@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", + "swagger-ui-dist": "5.17.14" }, "peerDependencies": { - "chokidar": "^3.5.2" + "@fastify/static": "^6.0.0 || ^7.0.0", + "@nestjs/common": "^9.0.0 || ^10.0.0", + "@nestjs/core": "^9.0.0 || ^10.0.0", + "class-transformer": "*", + "class-validator": "*", + "reflect-metadata": "^0.1.12 || ^0.2.0" }, "peerDependenciesMeta": { - "chokidar": { + "@fastify/static": { + "optional": true + }, + "class-transformer": { + "optional": true + }, + "class-validator": { "optional": true } } }, - "node_modules/@nestjs/schematics/node_modules/@angular-devkit/schematics": { - "version": "15.0.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-15.0.4.tgz", - "integrity": "sha512-/gXiLFS0+xFdx6wPoBpe/c6/K9I5edMpaASqPf4XheKtrsSvL+qTlIi3nsbfItzOiDXbaBmlbxGfkMHz/yg0Ig==", - "dev": true, - "dependencies": { - "@angular-devkit/core": "15.0.4", - "jsonc-parser": "3.2.0", - "magic-string": "0.26.7", - "ora": "5.4.1", - "rxjs": "6.6.7" - }, - "engines": { - "node": "^14.20.0 || ^16.13.0 || >=18.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@nestjs/schematics/node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "node_modules/@nestjs/testing": { + "version": "9.4.3", + "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-9.4.3.tgz", + "integrity": "sha512-LDT8Ai2eKnTzvnPaJwWOK03qTaFap5uHHsJCv6dL0uKWk6hyF9jms8DjyVaGsaujCaXDG8izl1mDEER0OmxaZA==", "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@nestjs/schematics/node_modules/magic-string": { - "version": "0.26.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.7.tgz", - "integrity": "sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==", - "dev": true, - "dependencies": { - "sourcemap-codec": "^1.4.8" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@nestjs/schematics/node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/@nestjs/schematics/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/@nestjs/testing": { - "version": "9.3.12", - "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-9.3.12.tgz", - "integrity": "sha512-nH274IXEqU4hr4bcb71POe58hYLONt9RcfKKM5ZvOS7wYMnybMpKKR8DkC1WcfE1P2k2GQmQoHeSH5emPtYrBA==", - "dev": true, - "dependencies": { - "tslib": "2.5.0" + "tslib": "2.5.3" }, "funding": { "type": "opencollective", @@ -1880,15 +1885,12 @@ } }, "node_modules/@prisma/client": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.16.0.tgz", - "integrity": "sha512-CBD+5IdZPiavhLkQokvsz1uz4r9ppixaqY/ajybWs4WXNnsDVMBKEqN3BiPzpSo79jiy22VKj/67pqt4VwIg9w==", + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.18.0.tgz", + "integrity": "sha512-BWivkLh+af1kqC89zCJYkHsRcyWsM8/JHpsDMM76DjP3ZdEquJhXa4IeX+HkWPnwJ5FanxEJFZZDTWiDs/Kvyw==", "hasInstallScript": true, - "dependencies": { - "@prisma/engines-version": "4.16.0-66.b20ead4d3ab9e78ac112966e242ded703f4a052c" - }, "engines": { - "node": ">=14.17" + "node": ">=16.13" }, "peerDependencies": { "prisma": "*" @@ -1899,16 +1901,45 @@ } } }, + "node_modules/@prisma/debug": { + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.18.0.tgz", + "integrity": "sha512-f+ZvpTLidSo3LMJxQPVgAxdAjzv5OpzAo/eF8qZqbwvgi2F5cTOI9XCpdRzJYA0iGfajjwjOKKrVq64vkxEfUw==" + }, "node_modules/@prisma/engines": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.16.0.tgz", - "integrity": "sha512-M6XoMRXnqL0rqZGQS8ZpNiHYG4G1fKBdoqW/oBtHnr1in5UYgerZqal3CXchmd6OBD/770PE9dtjQuqcilZJUA==", - "hasInstallScript": true + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.18.0.tgz", + "integrity": "sha512-ofmpGLeJ2q2P0wa/XaEgTnX/IsLnvSp/gZts0zjgLNdBhfuj2lowOOPmDcfKljLQUXMvAek3lw5T01kHmCG8rg==", + "hasInstallScript": true, + "dependencies": { + "@prisma/debug": "5.18.0", + "@prisma/engines-version": "5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169", + "@prisma/fetch-engine": "5.18.0", + "@prisma/get-platform": "5.18.0" + } }, "node_modules/@prisma/engines-version": { - "version": "4.16.0-66.b20ead4d3ab9e78ac112966e242ded703f4a052c", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.16.0-66.b20ead4d3ab9e78ac112966e242ded703f4a052c.tgz", - "integrity": "sha512-tMWAF/qF00fbUH1HB4Yjmz6bjh7fzkb7Y3NRoUfMlHu6V+O45MGvqwYxqwBjn1BIUXkl3r04W351D4qdJjrgvA==" + "version": "5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169.tgz", + "integrity": "sha512-a/+LpJj8vYU3nmtkg+N3X51ddbt35yYrRe8wqHTJtYQt7l1f8kjIBcCs6sHJvodW/EK5XGvboOiwm47fmNrbgg==" + }, + "node_modules/@prisma/fetch-engine": { + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.18.0.tgz", + "integrity": "sha512-I/3u0x2n31rGaAuBRx2YK4eB7R/1zCuayo2DGwSpGyrJWsZesrV7QVw7ND0/Suxeo/vLkJ5OwuBqHoCxvTHpOg==", + "dependencies": { + "@prisma/debug": "5.18.0", + "@prisma/engines-version": "5.18.0-25.4c784e32044a8a016d99474bd02a3b6123742169", + "@prisma/get-platform": "5.18.0" + } + }, + "node_modules/@prisma/get-platform": { + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.18.0.tgz", + "integrity": "sha512-Tk+m7+uhqcKDgnMnFN0lRiH7Ewea0OEsZZs9pqXa7i3+7svS3FSCqDBCaM9x5fmhhkufiG0BtunJVDka+46DlA==", + "dependencies": { + "@prisma/debug": "5.18.0" + } }, "node_modules/@sinclair/typebox": { "version": "0.24.51", @@ -1935,9 +1966,9 @@ } }, "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", "dev": true }, "node_modules/@tsconfig/node12": { @@ -1953,15 +1984,15 @@ "dev": true }, "node_modules/@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true }, "node_modules/@types/babel__core": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", - "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, "dependencies": { "@babel/parser": "^7.20.7", @@ -1972,18 +2003,18 @@ } }, "node_modules/@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", "dev": true, "dependencies": { "@babel/types": "^7.0.0" } }, "node_modules/@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, "dependencies": { "@babel/parser": "^7.1.0", @@ -1991,58 +2022,68 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", - "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", "dev": true, "dependencies": { - "@babel/types": "^7.3.0" + "@babel/types": "^7.20.7" } }, "node_modules/@types/bcrypt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.0.tgz", - "integrity": "sha512-agtcFKaruL8TmcvqbndlqHPSJgsolhf/qPWchFlgnW1gECTN/nKbFcoFnvKAQRFfKbh+BO6A3SWdJu9t+xF3Lw==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.2.tgz", + "integrity": "sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", "dependencies": { "@types/connect": "*", "@types/node": "*" } }, "node_modules/@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "dependencies": { "@types/node": "*" } }, "node_modules/@types/cookie-parser": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.3.tgz", - "integrity": "sha512-CqSKwFwefj4PzZ5n/iwad/bow2hTCh0FlNAeWLtQM3JA/NX/iYagIpWG2cf1bQKQ2c9gU2log5VUCrn7LDOs0w==", + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-Fvuyi354Z+uayxzIGCwYTayFKocfV7TuDYZClCdIP9ckhvAu/ixDtCB6qx2TT0FKjPLf1f3P/J1rgf6lPs64mw==", "dependencies": { "@types/express": "*" } }, "node_modules/@types/cookiejar": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz", - "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", + "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==", "dev": true }, + "node_modules/@types/csurf": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/@types/csurf/-/csurf-1.11.5.tgz", + "integrity": "sha512-5rw87+5YGixyL2W8wblSUl5DSZi5YOlXE6Awwn2ofLvqKr/1LruKffrQipeJKUX44VaxKj8m5es3vfhltJTOoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express-serve-static-core": "*" + } + }, "node_modules/@types/eslint": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.37.0.tgz", - "integrity": "sha512-Piet7dG2JBuDIfohBngQ3rCt7MgO9xCO4xIMKxBThCq5PNRB91IjlJ10eJVwfoNtvTErmxLzwBZ7rHZtbOMmFQ==", + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.0.tgz", + "integrity": "sha512-gi6WQJ7cHRgZxtkQEoyHMppPjq9Kxo5Tjn2prSKDSmZrCz8TZ3jSRCeTJm+WoM+oB0WG37bRqLzaaU3q7JypGg==", "dev": true, "dependencies": { "@types/estree": "*", @@ -2050,9 +2091,9 @@ } }, "node_modules/@types/eslint-scope": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", - "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", "dev": true, "dependencies": { "@types/eslint": "*", @@ -2060,15 +2101,15 @@ } }, "node_modules/@types/estree": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, "node_modules/@types/express": { - "version": "4.17.17", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", - "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", @@ -2077,51 +2118,67 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "4.17.33", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz", - "integrity": "sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==", + "version": "4.19.5", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.5.tgz", + "integrity": "sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==", "dependencies": { "@types/node": "*", "@types/qs": "*", - "@types/range-parser": "*" + "@types/range-parser": "*", + "@types/send": "*" } }, "node_modules/@types/express-session": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.7.tgz", - "integrity": "sha512-L25080PBYoRLu472HY/HNCxaXY8AaGgqGC8/p/8+BYMhG0RDOLQ1wpXOpAzr4Gi5TGozTKyJv5BVODM5UNyVMw==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.18.0.tgz", + "integrity": "sha512-27JdDRgor6PoYlURY+Y5kCakqp5ulC0kmf7y+QwaY+hv9jEFuQOThgkjyA53RP3jmKuBsH5GR6qEfFmvb8mwOA==", "dependencies": { "@types/express": "*" } }, "node_modules/@types/graceful-fs": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", - "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", "dev": true, "dependencies": { "@types/node": "*" } }, - "node_modules/@types/istanbul-lib-coverage": { + "node_modules/@types/http-errors": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" + }, + "node_modules/@types/inquirer": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-9.0.7.tgz", + "integrity": "sha512-Q0zyBupO6NxGRZut/JdmqYKOnN95Eg5V8Csg3PGKkP+FnvsUZx1jAyK7fztIszxxMuoBA6E3KXWvdZVXIpx60g==", + "dev": true, + "dependencies": { + "@types/through": "*", + "rxjs": "^7.2.0" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", "dev": true }, "node_modules/@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", "dev": true, "dependencies": { "@types/istanbul-lib-coverage": "*" } }, "node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", "dev": true, "dependencies": { "@types/istanbul-lib-report": "*" @@ -2138,55 +2195,61 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, "node_modules/@types/jsonwebtoken": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-drE6uz7QBKq1fYqqoFKTDRdFCPHd5TCub75BM+D+cMx7NU9hUz7SESLfC2fSCXVFMO5Yj8sOWHuGqPgjc+fz0Q==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz", + "integrity": "sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==", "dependencies": { "@types/node": "*" } }, + "node_modules/@types/methods": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", + "integrity": "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==", + "dev": true + }, "node_modules/@types/mime": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", - "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" }, "node_modules/@types/morgan": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.4.tgz", - "integrity": "sha512-cXoc4k+6+YAllH3ZHmx4hf7La1dzUk6keTR4bF4b4Sc0mZxU/zK4wO7l+ZzezXm/jkYj/qC+uYGZrarZdIVvyQ==", + "version": "1.9.9", + "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.9.tgz", + "integrity": "sha512-iRYSDKVaC6FkGSpEVVIvrRGw0DfJMiQzIn3qr2G5B3C//AWkulhXgaBd7tS9/J79GWSYMTHGs7PfI5b3Y8m+RQ==", "dependencies": { "@types/node": "*" } }, "node_modules/@types/node": { - "version": "16.18.23", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.23.tgz", - "integrity": "sha512-XAMpaw1s1+6zM+jn2tmw8MyaRDIJfXxqmIQIS0HfoGYPuf7dUWeiUKopwq13KFX9lEp1+THGtlaaYx39Nxr58g==" + "version": "16.18.105", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.105.tgz", + "integrity": "sha512-w2d0Z9yMk07uH3+Cx0N8lqFyi3yjXZxlbYappPj+AsOlT02OyxyiuNoNHdGt6EuiSm8Wtgp2YV7vWg+GMFrvFA==" }, "node_modules/@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", "dev": true }, "node_modules/@types/passport": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.12.tgz", - "integrity": "sha512-QFdJ2TiAEoXfEQSNDISJR1Tm51I78CymqcBa8imbjo6dNNu+l2huDxxbDEIoFIwOSKMkOfHEikyDuZ38WwWsmw==", + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.16.tgz", + "integrity": "sha512-FD0qD5hbPWQzaM0wHUnJ/T0BBCJBxCeemtnCwc/ThhTg3x9jfrAcRUmj5Dopza+MfFS9acTe3wk7rcVnRIp/0A==", "dependencies": { "@types/express": "*" } }, "node_modules/@types/passport-jwt": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-3.0.8.tgz", - "integrity": "sha512-VKJZDJUAHFhPHHYvxdqFcc5vlDht8Q2pL1/ePvKAgqRThDaCc84lSYOTQmnx3+JIkDlN+2KfhFhXIzlcVT+Pcw==", + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-3.0.13.tgz", + "integrity": "sha512-fjHaC6Bv8EpMMqzTnHP32SXlZGaNfBPC/Po5dmRGYi2Ky7ljXPbGnOy+SxZqa6iZvFgVhoJ1915Re3m93zmcfA==", "dependencies": { "@types/express": "*", "@types/jsonwebtoken": "*", @@ -2194,9 +2257,9 @@ } }, "node_modules/@types/passport-local": { - "version": "1.0.35", - "resolved": "https://registry.npmjs.org/@types/passport-local/-/passport-local-1.0.35.tgz", - "integrity": "sha512-K4eLTJ8R0yYW8TvCqkjB0pTKoqfUSdl5PfZdidTjV2ETV3604fQxtY6BHKjQWAx50WUS0lqzBvKv3LoI1ZBPeA==", + "version": "1.0.38", + "resolved": "https://registry.npmjs.org/@types/passport-local/-/passport-local-1.0.38.tgz", + "integrity": "sha512-nsrW4A963lYE7lNTv9cr5WmiUD1ibYJvWrpE13oxApFsRt77b0RdtZvKbCdNIY4v/QZ6TRQWaDDEwV1kCTmcXg==", "dependencies": { "@types/express": "*", "@types/passport": "*", @@ -2204,102 +2267,133 @@ } }, "node_modules/@types/passport-strategy": { - "version": "0.2.35", - "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.35.tgz", - "integrity": "sha512-o5D19Jy2XPFoX2rKApykY15et3Apgax00RRLf0RUotPDUsYrQa7x4howLYr9El2mlUApHmCMv5CZ1IXqKFQ2+g==", + "version": "0.2.38", + "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.38.tgz", + "integrity": "sha512-GC6eMqqojOooq993Tmnmp7AUTbbQSgilyvpCYQjT+H6JfG/g6RGc7nXEniZlp0zyKJ0WUdOiZWLBZft9Yug1uA==", "dependencies": { "@types/express": "*", "@types/passport": "*" } }, "node_modules/@types/prettier": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", - "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==", + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", "dev": true }, "node_modules/@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + "version": "6.9.15", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", + "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==" }, "node_modules/@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" }, "node_modules/@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, - "node_modules/@types/serve-static": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.1.tgz", - "integrity": "sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==", + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", "dependencies": { - "@types/mime": "*", + "@types/mime": "^1", "@types/node": "*" } }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", + "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/sharp": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/@types/sharp/-/sharp-0.32.0.tgz", + "integrity": "sha512-OOi3kL+FZDnPhVzsfD37J88FNeZh6gQsGcLc95NbeURRGvmSjeXiDcyWzF2o3yh/gQAUn2uhh/e+CPCa5nwAxw==", + "deprecated": "This is a stub types definition. sharp provides its own type definitions, so you do not need this installed.", + "dependencies": { + "sharp": "*" + } + }, "node_modules/@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "dev": true }, "node_modules/@types/superagent": { - "version": "4.1.16", - "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.16.tgz", - "integrity": "sha512-tLfnlJf6A5mB6ddqF159GqcDizfzbMUB1/DeT59/wBNqzRTNNKsaw79A/1TZ84X+f/EwWH8FeuSkjlCLyqS/zQ==", + "version": "8.1.8", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.8.tgz", + "integrity": "sha512-nTqHJ2OTa7PFEpLahzSEEeFeqbMpmcN7OeayiOc7v+xk+/vyTKljRe+o4MPqSnPeRCMvtxuLG+5QqluUVQJOnA==", "dev": true, "dependencies": { - "@types/cookiejar": "*", - "@types/node": "*" + "@types/cookiejar": "^2.1.5", + "@types/methods": "^1.1.4", + "@types/node": "*", + "form-data": "^4.0.0" } }, "node_modules/@types/supertest": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.12.tgz", - "integrity": "sha512-X3HPWTwXRerBZS7Mo1k6vMVR1Z6zmJcDVn5O/31whe0tnjE4te6ZJSJGq1RiqHPjzPdMTfjCFogDJmwng9xHaQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-6.0.2.tgz", + "integrity": "sha512-137ypx2lk/wTQbW6An6safu9hXmajAifU/s7szAHLN/FeIm5w7yR0Wkl9fdJMRSHwOn4HLAI0DaB2TOORuhPDg==", + "dev": true, + "dependencies": { + "@types/methods": "^1.1.4", + "@types/superagent": "^8.1.0" + } + }, + "node_modules/@types/through": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.33.tgz", + "integrity": "sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==", "dev": true, "dependencies": { - "@types/superagent": "*" + "@types/node": "*" } }, "node_modules/@types/validator": { - "version": "13.7.15", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.15.tgz", - "integrity": "sha512-yeinDVQunb03AEP8luErFcyf/7Lf7AzKCD0NXfgVoGCCQDNpZET8Jgq74oBgqKld3hafLbfzt/3inUdQvaFeXQ==" + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.12.0.tgz", + "integrity": "sha512-nH45Lk7oPIJ1RVOF6JgFI6Dy0QpHEzq4QecZhvguxYPDwT8c93prCMqAtiIttm39voZ+DDR+qkNnMpJmMBRqag==" }, "node_modules/@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", "dev": true, "dependencies": { "@types/yargs-parser": "*" } }, "node_modules/@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.57.0.tgz", - "integrity": "sha512-itag0qpN6q2UMM6Xgk6xoHa0D0/P+M17THnr4SVgqn9Rgam5k/He33MA7/D7QoJcdMxHFyX7U9imaBonAX/6qA==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.57.0", - "@typescript-eslint/type-utils": "5.57.0", - "@typescript-eslint/utils": "5.57.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", + "graphemer": "^1.4.0", "ignore": "^5.2.0", "natural-compare-lite": "^1.4.0", "semver": "^7.3.7", @@ -2323,14 +2417,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.57.0.tgz", - "integrity": "sha512-orrduvpWYkgLCyAdNtR1QIWovcNZlEm6yL8nwH/eTxWLd8gsP+25pdLHYzL2QdkqrieaDwLpytHqycncv0woUQ==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.57.0", - "@typescript-eslint/types": "5.57.0", - "@typescript-eslint/typescript-estree": "5.57.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", "debug": "^4.3.4" }, "engines": { @@ -2350,13 +2444,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.57.0.tgz", - "integrity": "sha512-NANBNOQvllPlizl9LatX8+MHi7bx7WGIWYjPHDmQe5Si/0YEYfxSljJpoTyTWFTgRy3X8gLYSE4xQ2U+aCozSw==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.57.0", - "@typescript-eslint/visitor-keys": "5.57.0" + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -2367,13 +2461,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.57.0.tgz", - "integrity": "sha512-kxXoq9zOTbvqzLbdNKy1yFrxLC6GDJFE2Yuo3KqSwTmDOFjUGeWSakgoXT864WcK5/NAJkkONCiKb1ddsqhLXQ==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.57.0", - "@typescript-eslint/utils": "5.57.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -2394,9 +2488,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.57.0.tgz", - "integrity": "sha512-mxsod+aZRSyLT+jiqHw1KK6xrANm19/+VFALVFP5qa/aiJnlP38qpyaTd0fEKhWvQk6YeNZ5LGwI1pDpBRBhtQ==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -2407,13 +2501,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.57.0.tgz", - "integrity": "sha512-LTzQ23TV82KpO8HPnWuxM2V7ieXW8O142I7hQTxWIHDcCEIjtkat6H96PFkYBQqGFLW/G/eVVOB9Z8rcvdY/Vw==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.57.0", - "@typescript-eslint/visitor-keys": "5.57.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -2434,17 +2528,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.57.0.tgz", - "integrity": "sha512-ps/4WohXV7C+LTSgAL5CApxvxbMkl9B9AUZRtnEFonpIxZDIT7wC1xfvuJONMidrkB9scs4zhtRyIwHh4+18kw==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.57.0", - "@typescript-eslint/types": "5.57.0", - "@typescript-eslint/typescript-estree": "5.57.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", "eslint-scope": "^5.1.1", "semver": "^7.3.7" }, @@ -2460,12 +2554,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.57.0.tgz", - "integrity": "sha512-ery2g3k0hv5BLiKpPuwYt9KBkAp2ugT6VvyShXdLOkax895EC55sP0Tx5L0fZaQueiK3fBLvHVvEl3jFS5ia+g==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.57.0", + "@typescript-eslint/types": "5.62.0", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -2476,149 +2570,155 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, "node_modules/@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", + "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", "dev": true, "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" } }, "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", "dev": true }, "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", "dev": true }, "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", + "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", "dev": true }, "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", "dev": true, "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", "dev": true }, "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", + "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.12.1" } }, "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", "dev": true, "dependencies": { "@xtuc/ieee754": "^1.2.0" } }, "node_modules/@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", "dev": true, "dependencies": { "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", "dev": true }, "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", + "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-opt": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1", + "@webassemblyjs/wast-printer": "1.12.1" } }, "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", + "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", + "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1" } }, "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", + "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", + "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/ast": "1.12.1", "@xtuc/long": "4.2.2" } }, @@ -2652,9 +2752,9 @@ } }, "node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -2664,10 +2764,20 @@ } }, "node_modules/acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "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==", + "dev": true, + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", "dev": true, + "peer": true, "peerDependencies": { "acorn": "^8" } @@ -2682,10 +2792,13 @@ } }, "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", + "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", "dev": true, + "dependencies": { + "acorn": "^8.11.0" + }, "engines": { "node": ">=0.4.0" } @@ -2701,19 +2814,6 @@ "node": ">= 6.0.0" } }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/ajv": { "version": "8.12.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", @@ -2751,7 +2851,6 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, "engines": { "node": ">=6" } @@ -2832,6 +2931,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "deprecated": "This package is no longer supported.", "dependencies": { "delegates": "^1.0.0", "readable-stream": "^3.6.0" @@ -2862,8 +2962,7 @@ "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/array-flatten": { "version": "1.1.1", @@ -2885,26 +2984,17 @@ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "dev": true }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", - "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", + "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==", "dependencies": { - "follow-redirects": "^1.15.0", + "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } @@ -2962,23 +3052,26 @@ } }, "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", + "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", "dev": true, "dependencies": { "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" }, "peerDependencies": { "@babel/core": "^7.0.0" @@ -3042,12 +3135,12 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "node_modules/bcrypt": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.0.tgz", - "integrity": "sha512-RHBS7HI5N5tEnGTmtR/pppX0mmDSBpQ4aCBsj7CEQfYXDcO74A8sIBYcJMuCsis2E81zDxeENYhv66oZwLiA+Q==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", + "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", "hasInstallScript": true, "dependencies": { - "@mapbox/node-pre-gyp": "^1.0.10", + "@mapbox/node-pre-gyp": "^1.0.11", "node-addon-api": "^5.0.0" }, "engines": { @@ -3055,12 +3148,15 @@ } }, "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/bl": { @@ -3134,21 +3230,21 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" } }, "node_modules/browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", + "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", "dev": true, "funding": [ { @@ -3158,13 +3254,17 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" + "caniuse-lite": "^1.0.30001646", + "electron-to-chromium": "^1.5.4", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" }, "bin": { "browserslist": "cli.js" @@ -3248,12 +3348,18 @@ } }, "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3278,9 +3384,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001473", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001473.tgz", - "integrity": "sha512-ewDad7+D2vlyy+E4UJuVfiBsU69IL+8oVmTuZnH5Q6CIUbxNfI50uVpRHbUPDD6SUaN2o0Lh4DhTrvLG/Tn1yg==", + "version": "1.0.30001651", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz", + "integrity": "sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==", "dev": true, "funding": [ { @@ -3297,6 +3403,20 @@ } ] }, + "node_modules/canvas": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz", + "integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==", + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.0", + "nan": "^2.17.0", + "simple-get": "^3.0.3" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -3363,18 +3483,18 @@ } }, "node_modules/chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", "dev": true, "engines": { "node": ">=6.0" } }, "node_modules/ci-info": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", - "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "dev": true, "funding": [ { @@ -3387,9 +3507,9 @@ } }, "node_modules/cjs-module-lexer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", - "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz", + "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==", "dev": true }, "node_modules/class-transformer": { @@ -3398,22 +3518,13 @@ "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==" }, "node_modules/class-validator": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.0.tgz", - "integrity": "sha512-ct3ltplN8I9fOwUd8GrP8UQixwff129BkEtuWDKL5W45cQuLd19xqmTLu5ge78YDm/fdje6FMt0hGOhl0lii3A==", + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.1.tgz", + "integrity": "sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ==", "dependencies": { - "@types/validator": "^13.7.10", - "libphonenumber-js": "^1.10.14", - "validator": "^13.7.0" - } - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, - "engines": { - "node": ">=6" + "@types/validator": "^13.11.8", + "libphonenumber-js": "^1.10.53", + "validator": "^13.9.0" } }, "node_modules/cli-cursor": { @@ -3429,9 +3540,9 @@ } }, "node_modules/cli-spinners": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", - "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", "dev": true, "engines": { "node": ">=6" @@ -3564,11 +3675,23 @@ } }, "node_modules/collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", "dev": true }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -3585,6 +3708,15 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, "node_modules/color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", @@ -3620,10 +3752,13 @@ } }, "node_modules/component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", + "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/concat-map": { "version": "0.0.1", @@ -3680,9 +3815,9 @@ "dev": true }, "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", "engines": { "node": ">= 0.6" } @@ -3699,14 +3834,6 @@ "node": ">= 0.8.0" } }, - "node_modules/cookie-parser/node_modules/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", @@ -3757,6 +3884,24 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -3770,55 +3915,178 @@ "node": ">= 8" } }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/csrf": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/csrf/-/csrf-3.1.0.tgz", + "integrity": "sha512-uTqEnCvWRk042asU6JtapDTcJeeailFy4ydOQS28bj1hcLnYRiqi8SsD2jS412AY1I/4qdOwWZun774iqywf9w==", + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "rndm": "1.2.0", + "tsscmp": "1.0.6", + "uid-safe": "2.1.5" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">= 0.8" } }, - "node_modules/dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", - "dev": true + "node_modules/csurf": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/csurf/-/csurf-1.10.0.tgz", + "integrity": "sha512-fh725p0R83wA5JukCik5hdEko/LizW/Vl7pkKDa1WJUVCosg141mqaAWCScB+nkEaRMFMGbutHMOr6oBNc/j9A==", + "license": "MIT", + "dependencies": { + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "csrf": "3.1.0", + "http-errors": "~1.7.2" + }, + "engines": { + "node": ">= 0.8.0" + } }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true + "node_modules/csurf/node_modules/cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha512-+IJOX0OqlHCszo2mBUq+SrEbCj6w7Kpffqx60zYbPTFaO4+yYgRjHwcZNpWvaTylDHaV7PPmBHzSecZiMhtPgw==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, + "node_modules/csurf/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 0.6" } }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dev": true, + "node_modules/csurf/node_modules/http-errors": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", + "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", + "license": "MIT", "dependencies": { - "clone": "^1.0.2" + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/csurf/node_modules/setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", + "license": "ISC" + }, + "node_modules/csurf/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/csurf/node_modules/toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/date-fns": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/debug": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", + "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "dependencies": { + "mimic-response": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "dev": true + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "dependencies": { + "clone": "^1.0.2" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -3850,9 +4118,9 @@ } }, "node_modules/detect-libc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", - "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", "engines": { "node": ">=8" } @@ -3919,20 +4187,24 @@ } }, "node_modules/dotenv": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", - "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "license": "BSD-2-Clause", "engines": { "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" } }, "node_modules/dotenv-cli": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/dotenv-cli/-/dotenv-cli-7.2.1.tgz", - "integrity": "sha512-ODHbGTskqRtXAzZapDPvgNuDVQApu4oKX8lZW7Y0+9hKA6le1ZJlyRS687oU9FXjOVEDU/VFV6zI125HzhM1UQ==", + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/dotenv-cli/-/dotenv-cli-7.4.2.tgz", + "integrity": "sha512-SbUj8l61zIbzyhIbg0FwPJq6+wjbzdn9oEtozQpZ6kW2ihCcapKVZj49oCT3oPM+mgQm+itgvUQcG5szxVrZTA==", "dependencies": { "cross-spawn": "^7.0.3", - "dotenv": "^16.0.0", + "dotenv": "^16.3.0", "dotenv-expand": "^10.0.0", "minimist": "^1.2.6" }, @@ -3968,9 +4240,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.345", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.345.tgz", - "integrity": "sha512-znGhOQK2TUYLICgS25uaM0a7pHy66rSxbre7l762vg9AUoCcJK+Bu+HCPWpjL/U/kK8/Hf+6E0szAUJSyVYb3Q==", + "version": "1.5.12", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.12.tgz", + "integrity": "sha512-tIhPkdlEoCL1Y+PToq3zRNehUaKp3wBX/sr7aclAWdIWjvqAe/Im/H0SiCM4c1Q8BLPHCdoJTol+ZblflydehA==", "dev": true }, "node_modules/emittery": { @@ -4008,9 +4280,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", - "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==", + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", "dev": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -4020,6 +4292,18 @@ "node": ">=10.13.0" } }, + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "dependencies": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -4029,16 +4313,35 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", + "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", "dev": true }, "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", "dev": true, "engines": { "node": ">=6" @@ -4062,27 +4365,28 @@ } }, "node_modules/eslint": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.37.0.tgz", - "integrity": "sha512-NU3Ps9nI05GUoVMxcZx1J8CNR6xOvUT4jAUMH5+z8lpp3aEdPVCImKw6PWG4PY+Vfkpr+jvMpxs/qoE7wq0sPw==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.2", - "@eslint/js": "8.37.0", - "@humanwhocodes/config-array": "^0.11.8", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-visitor-keys": "^3.4.0", - "espree": "^9.5.1", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -4090,22 +4394,19 @@ "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", + "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "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.1", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "bin": { @@ -4119,9 +4420,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", - "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz", + "integrity": "sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==", "dev": true, "bin": { "eslint-config-prettier": "bin/cli.js" @@ -4144,9 +4445,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz", - "integrity": "sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -4172,9 +4473,9 @@ } }, "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -4182,6 +4483,9 @@ }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/estraverse": { @@ -4212,14 +4516,14 @@ "dev": true }, "node_modules/espree": { - "version": "9.5.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.1.tgz", - "integrity": "sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "dependencies": { - "acorn": "^8.8.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.0" + "eslint-visitor-keys": "^3.4.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -4242,9 +4546,9 @@ } }, "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -4309,6 +4613,12 @@ "node": ">= 0.6" } }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -4408,12 +4718,12 @@ } }, "node_modules/express-session": { - "version": "1.17.3", - "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz", - "integrity": "sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.0.tgz", + "integrity": "sha512-m93QLWr0ju+rOwApSsyso838LQwgfs44QtOP/WBiwtAgPIo/SAh1a5c6nn2BR6mFNZehTpqKDESzP+fRHVbxwQ==", "dependencies": { - "cookie": "0.4.2", - "cookie-signature": "1.0.6", + "cookie": "0.6.0", + "cookie-signature": "1.0.7", "debug": "2.6.9", "depd": "~2.0.0", "on-headers": "~1.0.2", @@ -4426,13 +4736,18 @@ } }, "node_modules/express-session/node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "engines": { "node": ">= 0.6" } }, + "node_modules/express-session/node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==" + }, "node_modules/express-session/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -4469,6 +4784,14 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/express/node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -4522,9 +4845,9 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -4555,9 +4878,9 @@ "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" }, "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dev": true, "dependencies": { "reusify": "^1.0.4" @@ -4609,9 +4932,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -4667,12 +4990,13 @@ } }, "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, "dependencies": { - "flatted": "^3.1.0", + "flatted": "^3.2.9", + "keyv": "^4.5.3", "rimraf": "^3.0.2" }, "engines": { @@ -4680,15 +5004,15 @@ } }, "node_modules/flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "funding": [ { "type": "individual", @@ -4732,20 +5056,6 @@ "webpack": "^5.11.0" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", @@ -4760,15 +5070,14 @@ } }, "node_modules/formidable": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", - "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.1.tgz", + "integrity": "sha512-WJWKelbRHN41m5dumb0/k8TeAx7Id/y3a+Z7QfhxP/htI9Js5zYaEDtG8uMgG0vM0lOlqnmjE99/kfpOYi/0Og==", "dev": true, "dependencies": { "dezalgo": "^1.0.4", "hexoid": "^1.0.0", - "once": "^1.4.0", - "qs": "^6.11.0" + "once": "^1.4.0" }, "funding": { "url": "https://ko-fi.com/tunnckoCore/commissions" @@ -4791,9 +5100,9 @@ } }, "node_modules/fs-extra": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz", - "integrity": "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, "dependencies": { "graceful-fs": "^4.2.0", @@ -4801,7 +5110,7 @@ "universalify": "^2.0.0" }, "engines": { - "node": ">=14.14" + "node": ">=12" } }, "node_modules/fs-minipass": { @@ -4832,9 +5141,9 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/fs-monkey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", - "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz", + "integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==", "dev": true }, "node_modules/fs.realpath": { @@ -4843,9 +5152,9 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, "optional": true, @@ -4857,14 +5166,18 @@ } }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/gauge": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "deprecated": "This package is no longer supported.", "dependencies": { "aproba": "^1.0.3 || ^2.0.0", "color-support": "^1.1.2", @@ -4899,13 +5212,18 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4936,6 +5254,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -4970,9 +5289,9 @@ "dev": true }, "node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -5004,29 +5323,29 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -5035,6 +5354,28 @@ "node": ">=8" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", @@ -5051,6 +5392,17 @@ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/hexoid": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", @@ -5117,6 +5469,56 @@ "url": "https://github.com/sponsors/typicode" } }, + "node_modules/ical-generator": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/ical-generator/-/ical-generator-7.2.0.tgz", + "integrity": "sha512-7I34QvxWqIRthaao81lmapa0OjftfDaSBZmADjV0IqxVMUWT5ywlATRsv/hZN9Rgf2VgRsnMY+xUUaA4ZvAJLA==", + "dependencies": { + "uuid-random": "^1.3.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@touch4it/ical-timezones": ">=1.6.0", + "@types/luxon": ">= 1.26.0", + "@types/mocha": ">= 8.2.1", + "dayjs": ">= 1.10.0", + "luxon": ">= 1.26.0", + "moment": ">= 2.29.0", + "moment-timezone": ">= 0.5.33", + "rrule": ">= 2.6.8" + }, + "peerDependenciesMeta": { + "@touch4it/ical-timezones": { + "optional": true + }, + "@types/luxon": { + "optional": true + }, + "@types/mocha": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "dayjs": { + "optional": true + }, + "luxon": { + "optional": true + }, + "moment": { + "optional": true + }, + "moment-timezone": { + "optional": true + }, + "rrule": { + "optional": true + } + } + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -5149,9 +5551,9 @@ ] }, "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "engines": { "node": ">= 4" @@ -5174,9 +5576,9 @@ } }, "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", "dev": true, "dependencies": { "pkg-dir": "^4.2.0", @@ -5201,19 +5603,11 @@ "node": ">=0.8.19" } }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -5286,12 +5680,15 @@ } }, "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.0.tgz", + "integrity": "sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==", "dev": true, "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5307,14 +5704,18 @@ } }, "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-generator-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", @@ -5397,9 +5798,9 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, "engines": { "node": ">=8" @@ -5431,17 +5832,32 @@ } }, "node_modules/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "dependencies": { "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", + "make-dir": "^4.0.0", "supports-color": "^7.1.0" }, "engines": { - "node": ">=8" + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/istanbul-lib-source-maps": { @@ -5468,9 +5884,9 @@ } }, "node_modules/istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", "dev": true, "dependencies": { "html-escaper": "^2.0.0", @@ -6062,16 +6478,6 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/js-sdsl": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz", - "integrity": "sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -6082,7 +6488,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, "dependencies": { "argparse": "^2.0.1" }, @@ -6102,6 +6507,12 @@ "node": ">=4" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -6151,14 +6562,20 @@ } }, "node_modules/jsonwebtoken": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", - "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", "dependencies": { "jws": "^3.2.2", - "lodash": "^4.17.21", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", "ms": "^2.1.1", - "semver": "^7.3.8" + "semver": "^7.5.4" }, "engines": { "node": ">=12", @@ -6184,6 +6601,15 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -6216,9 +6642,9 @@ } }, "node_modules/libphonenumber-js": { - "version": "1.10.28", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.28.tgz", - "integrity": "sha512-1eAgjLrZA0+2Wgw4hs+4Q/kEBycxQo8ZLYnmOvZ3AlM8ImAVAJgDPlZtISLEzD1vunc2q8s2Pn7XwB7I8U3Kzw==" + "version": "1.11.7", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.11.7.tgz", + "integrity": "sha512-x2xON4/Qg2bRIS11KIN9yCNYUjhtiEjNyptjX0mX+pyKHecxuJVLIpfX1lq9ZD6CrC/rB+y4GBi18c6CEcUR+A==" }, "node_modules/lilconfig": { "version": "2.1.0", @@ -6236,39 +6662,36 @@ "dev": true }, "node_modules/lint-staged": { - "version": "13.2.3", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.2.3.tgz", - "integrity": "sha512-zVVEXLuQIhr1Y7R7YAWx4TZLdvuzk7DnmrsTNL0fax6Z3jrpFcas+vKbzxhhvp6TA55m1SQuWkpzI1qbfDZbAg==", + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.3.0.tgz", + "integrity": "sha512-mPRtrYnipYYv1FEE134ufbWpeggNTo+O/UPzngoaKzbzHAthvR55am+8GfHTnqNRQVRRrYQLGW9ZyUoD7DsBHQ==", "dev": true, "dependencies": { - "chalk": "5.2.0", - "cli-truncate": "^3.1.0", - "commander": "^10.0.0", - "debug": "^4.3.4", - "execa": "^7.0.0", + "chalk": "5.3.0", + "commander": "11.0.0", + "debug": "4.3.4", + "execa": "7.2.0", "lilconfig": "2.1.0", - "listr2": "^5.0.7", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-inspect": "^1.12.3", - "pidtree": "^0.6.0", - "string-argv": "^0.3.1", - "yaml": "^2.2.2" + "listr2": "6.6.1", + "micromatch": "4.0.5", + "pidtree": "0.6.0", + "string-argv": "0.3.2", + "yaml": "2.3.1" }, "bin": { "lint-staged": "bin/lint-staged.js" }, "engines": { - "node": "^14.13.1 || >=16.0.0" + "node": "^16.14.0 || >=18.0.0" }, "funding": { "url": "https://opencollective.com/lint-staged" } }, "node_modules/lint-staged/node_modules/chalk": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", - "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" @@ -6278,18 +6701,35 @@ } }, "node_modules/lint-staged/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", + "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", "dev": true, "engines": { - "node": ">=14" + "node": ">=16" + } + }, + "node_modules/lint-staged/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/lint-staged/node_modules/execa": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.1.1.tgz", - "integrity": "sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", + "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", "dev": true, "dependencies": { "cross-spawn": "^7.0.3", @@ -6330,6 +6770,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lint-staged/node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/lint-staged/node_modules/mimic-fn": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", @@ -6343,9 +6796,9 @@ } }, "node_modules/lint-staged/node_modules/npm-run-path": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", - "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", "dev": true, "dependencies": { "path-key": "^4.0.0" @@ -6406,22 +6859,20 @@ } }, "node_modules/listr2": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-5.0.8.tgz", - "integrity": "sha512-mC73LitKHj9w6v30nLNGPetZIlfpUniNSsxxrbaPcWOjDb92SHPzJPi/t+v1YC/lxKz/AJ9egOjww0qUuFxBpA==", + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-6.6.1.tgz", + "integrity": "sha512-+rAXGHh0fkEWdXBmX+L6mmfmXmXvDGEKzkjxO+8mP3+nI/r/CWznVBvsibXdxda9Zz0OW2e2ikphN3OwCT/jSg==", "dev": true, "dependencies": { - "cli-truncate": "^2.1.0", - "colorette": "^2.0.19", - "log-update": "^4.0.0", - "p-map": "^4.0.0", + "cli-truncate": "^3.1.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^5.0.1", "rfdc": "^1.3.0", - "rxjs": "^7.8.0", - "through": "^2.3.8", - "wrap-ansi": "^7.0.0" + "wrap-ansi": "^8.1.0" }, "engines": { - "node": "^14.13.1 || >=16.0.0" + "node": ">=16.0.0" }, "peerDependencies": { "enquirer": ">= 2.3.0 < 3" @@ -6432,34 +6883,83 @@ } } }, - "node_modules/listr2/node_modules/cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "node_modules/listr2/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/listr2/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/listr2/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/listr2/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, "dependencies": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/listr2/node_modules/slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "node_modules/listr2/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/loader-runner": { @@ -6491,6 +6991,36 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -6503,6 +7033,11 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -6520,111 +7055,218 @@ } }, "node_modules/log-update": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", - "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-5.0.1.tgz", + "integrity": "sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw==", "dev": true, "dependencies": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" + "ansi-escapes": "^5.0.0", + "cli-cursor": "^4.0.0", + "slice-ansi": "^5.0.0", + "strip-ansi": "^7.0.1", + "wrap-ansi": "^8.0.1" }, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-update/node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "node_modules/log-update/node_modules/ansi-escapes": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz", + "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==", "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" + "type-fest": "^1.0.2" }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-update/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "node_modules/log-update/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "engines": { + "node": ">=12" }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/log-update/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "node_modules/log-update/node_modules/cli-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", "dev": true, "dependencies": { - "yallist": "^3.0.2" + "restore-cursor": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/macos-release": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.1.tgz", - "integrity": "sha512-DXqXhEM7gW59OjZO8NIjBCz9AQ1BEMrfiOAl4AYByHCtVHRF4KoGNO8mqQeM8lRCtQe/UnJ4imO/d2HdkKsd+A==", + "node_modules/log-update/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/log-update/node_modules/restore-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, "engines": { - "node": ">=6" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/magic-string": { - "version": "0.29.0", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.29.0.tgz", - "integrity": "sha512-WcfidHrDjMY+eLjlU+8OvwREqHwpgCeKVBUpQ3OhYYuvfaYCUgcbuBzappNzZvg/v8onU3oQj+BYpkOJe9Iw4Q==", + "node_modules/log-update/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.13" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "node_modules/log-update/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, "dependencies": { - "semver": "^6.0.0" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" + "node_modules/log-update/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-error": { - "version": "1.3.6", + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/macos-release": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.1.tgz", + "integrity": "sha512-DXqXhEM7gW59OjZO8NIjBCz9AQ1BEMrfiOAl4AYByHCtVHRF4KoGNO8mqQeM8lRCtQe/UnJ4imO/d2HdkKsd+A==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/magic-string": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz", + "integrity": "sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.13" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/make-error": { + "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true @@ -6647,12 +7289,12 @@ } }, "node_modules/memfs": { - "version": "3.4.13", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.13.tgz", - "integrity": "sha512-omTM41g3Skpvx5dSYeZIbXKcXoAVc/AoMNwn9TKx++L/gaen/+4TTttmu8ZSch5vfVJ8uJvGbroTsIlslRg6lg==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", "dev": true, "dependencies": { - "fs-monkey": "^1.0.3" + "fs-monkey": "^1.0.4" }, "engines": { "node": ">= 4.0.0" @@ -6687,12 +7329,12 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", "dev": true, "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -6738,6 +7380,17 @@ "node": ">=6" } }, + "node_modules/mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -6758,10 +7411,9 @@ } }, "node_modules/minipass": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.5.tgz", - "integrity": "sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==", - "dev": true, + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", "engines": { "node": ">=8" } @@ -6805,6 +7457,25 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "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" + }, + "engines": { + "node": "*" + } + }, "node_modules/morgan": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", @@ -6872,6 +7543,11 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, + "node_modules/nan": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.20.0.tgz", + "integrity": "sha512-bk3gXBZDGILuuo/6sKtr0DQmSThYHLtNCdSdXk9YkxD/jK6X2vmCyyXBBxyqZ4XcnzTyYEAThfX3DCEnLf6igw==" + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -6898,6 +7574,20 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, + "node_modules/nestjs-cls": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/nestjs-cls/-/nestjs-cls-4.4.1.tgz", + "integrity": "sha512-4yhldwm/cJ02lQ8ZAdM8KQ7gMfjAc1z3fo5QAQgXNyN4N6X5So9BCwv+BTLRugDCkELUo3qtzQHnKhGYL/ftPg==", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@nestjs/common": "> 7.0.0 < 11", + "@nestjs/core": "> 7.0.0 < 11", + "reflect-metadata": "*", + "rxjs": ">= 7" + } + }, "node_modules/node-abort-controller": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", @@ -6919,9 +7609,9 @@ } }, "node_modules/node-fetch": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", - "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "dependencies": { "whatwg-url": "^5.0.0" }, @@ -6944,9 +7634,9 @@ "dev": true }, "node_modules/node-releases": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", - "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", "dev": true }, "node_modules/nopt": { @@ -6988,6 +7678,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "deprecated": "This package is no longer supported.", "dependencies": { "are-we-there-yet": "^2.0.0", "console-control-strings": "^1.1.0", @@ -7004,9 +7695,12 @@ } }, "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -7054,9 +7748,9 @@ } }, "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "dependencies": { "deep-is": "^0.1.3", @@ -7064,7 +7758,7 @@ "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "word-wrap": "^1.2.5" }, "engines": { "node": ">= 0.8.0" @@ -7148,21 +7842,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -7276,29 +7955,26 @@ "dev": true }, "node_modules/path-scurry": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.6.3.tgz", - "integrity": "sha512-RAmB+n30SlN+HnNx6EbcpoDy9nwdpcGPnEKrJnu6GZoDWBdIjo1UQMVtW2ybtC7LC2oKLcMq8y5g8WnKLiod9g==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, "dependencies": { - "lru-cache": "^7.14.1", - "minipass": "^4.0.2" + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true, - "engines": { - "node": ">=12" - } + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true }, "node_modules/path-to-regexp": { "version": "3.2.0", @@ -7320,9 +7996,9 @@ "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", "dev": true }, "node_modules/picomatch": { @@ -7350,9 +8026,9 @@ } }, "node_modules/pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", "dev": true, "engines": { "node": ">= 6" @@ -7441,9 +8117,9 @@ } }, "node_modules/prettier": { - "version": "2.8.7", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz", - "integrity": "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==", + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true, "bin": { "prettier": "bin-prettier.js" @@ -7483,19 +8159,18 @@ } }, "node_modules/prisma": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-4.16.0.tgz", - "integrity": "sha512-kSCwbTm3LCephyGfZMJYqBXpPJXdJStg5xwfzeFmR5C05zfkOURK9pQpJF6uUQvFWm3lI9ZMSNkObmFkAPnB+g==", + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.18.0.tgz", + "integrity": "sha512-+TrSIxZsh64OPOmaSgVPH7ALL9dfU0jceYaMJXsNrTkFHO7/3RANi5K2ZiPB1De9+KDxCWn7jvRq8y8pvk+o9g==", "hasInstallScript": true, "dependencies": { - "@prisma/engines": "4.16.0" + "@prisma/engines": "5.18.0" }, "bin": { - "prisma": "build/index.js", - "prisma2": "build/index.js" + "prisma": "build/index.js" }, "engines": { - "node": ">=14.17" + "node": ">=16.13" } }, "node_modules/process-nextick-args": { @@ -7544,9 +8219,9 @@ } }, "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "engines": { "node": ">=6" @@ -7626,9 +8301,9 @@ } }, "node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true }, "node_modules/readable-stream": { @@ -7675,9 +8350,9 @@ } }, "node_modules/reflect-metadata": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" + "version": "0.1.14", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", + "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==" }, "node_modules/require-directory": { "version": "2.1.1", @@ -7698,12 +8373,12 @@ } }, "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, "dependencies": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -7777,15 +8452,16 @@ } }, "node_modules/rfdc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "dev": true }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dependencies": { "glob": "^7.1.3" }, @@ -7796,6 +8472,12 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rndm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/rndm/-/rndm-1.2.0.tgz", + "integrity": "sha512-fJhQQI5tLrQvYIYFpOnFinzv9dwmR7hRnUz1XqP3OJ1jIweTNOd6aTO4jwQSgcBSFUB+/KHJxuGneime+FdzOw==", + "license": "MIT" + }, "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -7829,9 +8511,9 @@ } }, "node_modules/rxjs": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", - "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", "dependencies": { "tslib": "^2.1.0" } @@ -7861,9 +8543,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.8", @@ -7910,12 +8592,9 @@ "dev": true }, "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "bin": { "semver": "bin/semver.js" }, @@ -7923,22 +8602,6 @@ "node": ">=10" } }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, "node_modules/send": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", @@ -7981,9 +8644,9 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/serialize-javascript": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", - "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, "dependencies": { "randombytes": "^2.1.0" @@ -8008,11 +8671,65 @@ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/sharp": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", + "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", + "hasInstallScript": true, + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.3", + "semver": "^7.6.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.33.5", + "@img/sharp-darwin-x64": "0.33.5", + "@img/sharp-libvips-darwin-arm64": "1.0.4", + "@img/sharp-libvips-darwin-x64": "1.0.4", + "@img/sharp-libvips-linux-arm": "1.0.5", + "@img/sharp-libvips-linux-arm64": "1.0.4", + "@img/sharp-libvips-linux-s390x": "1.0.4", + "@img/sharp-libvips-linux-x64": "1.0.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", + "@img/sharp-libvips-linuxmusl-x64": "1.0.4", + "@img/sharp-linux-arm": "0.33.5", + "@img/sharp-linux-arm64": "0.33.5", + "@img/sharp-linux-s390x": "0.33.5", + "@img/sharp-linux-x64": "0.33.5", + "@img/sharp-linuxmusl-arm64": "0.33.5", + "@img/sharp-linuxmusl-x64": "0.33.5", + "@img/sharp-wasm32": "0.33.5", + "@img/sharp-win32-ia32": "0.33.5", + "@img/sharp-win32-x64": "0.33.5" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -8050,13 +8767,17 @@ } }, "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -8067,6 +8788,48 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/simple-get": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", + "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", + "dependencies": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -8110,18 +8873,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/source-map": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", @@ -8150,13 +8901,6 @@ "node": ">=0.10.0" } }, - "node_modules/sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "deprecated": "Please use @jridgewell/sourcemap-codec instead", - "dev": true - }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -8248,6 +8992,14 @@ "node": ">=8" } }, + "node_modules/string-width/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -8290,9 +9042,9 @@ } }, "node_modules/superagent": { - "version": "8.0.9", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.0.9.tgz", - "integrity": "sha512-4C7Bh5pyHTvU33KpZgwrNKh/VQnvgtCSqPRfJAUdmrtSYePVzVg4E4OzsrbkhJj9O7SO6Bnv75K/F8XVZT8YHA==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-9.0.2.tgz", + "integrity": "sha512-xuW7dzkUpcJq7QnhOsnNUgtYp3xRwpt2F7abdRYIpCsAt0hhUqia0EdxyXZQQpNmGtsCzYHryaKSV3q3GJnq7w==", "dev": true, "dependencies": { "component-emitter": "^1.3.0", @@ -8300,14 +9052,13 @@ "debug": "^4.3.4", "fast-safe-stringify": "^2.1.1", "form-data": "^4.0.0", - "formidable": "^2.1.2", + "formidable": "^3.5.1", "methods": "^1.1.2", "mime": "2.6.0", - "qs": "^6.11.0", - "semver": "^7.3.8" + "qs": "^6.11.0" }, "engines": { - "node": ">=6.4.0 <13 || >=14" + "node": ">=14.18.0" } }, "node_modules/superagent/node_modules/mime": { @@ -8323,16 +9074,16 @@ } }, "node_modules/supertest": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.3.3.tgz", - "integrity": "sha512-EMCG6G8gDu5qEqRQ3JjjPs6+FYT1a7Hv5ApHvtSghmOFJYtsU5S+pSb6Y2EUeCEY3CmEL3mmQ8YWlPOzQomabA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.0.0.tgz", + "integrity": "sha512-qlsr7fIC0lSddmA3tzojvzubYxvlGtzumcdHgPwbFWMISQwL22MhM2Y3LNt+6w9Yyx7559VW5ab70dgphm8qQA==", "dev": true, "dependencies": { "methods": "^1.1.2", - "superagent": "^8.0.5" + "superagent": "^9.0.1" }, "engines": { - "node": ">=6.4.0" + "node": ">=14.18.0" } }, "node_modules/supports-color": { @@ -8371,6 +9122,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swagger-ui-dist": { + "version": "5.17.14", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.17.14.tgz", + "integrity": "sha512-CVbSfaLpstV65OnSjbXfVd6Sta3q3F7Cj/yYuvHMp1P90LztOLs6PfUnKEVAeiIVQt9u2SaPwv0LiH/OyMjHRw==" + }, "node_modules/symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", @@ -8390,9 +9146,9 @@ } }, "node_modules/tar": { - "version": "6.1.15", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.15.tgz", - "integrity": "sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -8405,14 +9161,6 @@ "node": ">=10" } }, - "node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "engines": { - "node": ">=8" - } - }, "node_modules/tar/node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -8446,13 +9194,13 @@ } }, "node_modules/terser": { - "version": "5.16.8", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.8.tgz", - "integrity": "sha512-QI5g1E/ef7d+PsDifb+a6nnVgC4F22Bg6T0xrBrz6iloVB4PUkkunp6V8nzoOOZJIzjWVdAGqCdlKlhLq/TbIA==", + "version": "5.31.6", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.6.tgz", + "integrity": "sha512-PQ4DAriWzKj+qgehQ7LK5bQqCFNMmlhjR2PFFLuqGCpuCAauxemVBWwWOxo3UIwWQx8+Pr61Df++r76wDmkQBg==", "dev": true, "dependencies": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -8464,16 +9212,16 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.7", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.7.tgz", - "integrity": "sha512-AfKwIktyP7Cu50xNjXF/6Qb5lBNzYaWpU6YfoX3uZicTx0zTy0stDDCsvjDapKsSDvOeWo5MEq4TmdBy2cNoHw==", + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", "dev": true, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.17", + "@jridgewell/trace-mapping": "^0.3.20", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", "serialize-javascript": "^6.0.1", - "terser": "^5.16.5" + "terser": "^5.26.0" }, "engines": { "node": ">= 10.13.0" @@ -8620,9 +9368,9 @@ } }, "node_modules/ts-jest": { - "version": "28.0.5", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-28.0.5.tgz", - "integrity": "sha512-Sx9FyP9pCY7pUzQpy4FgRZf2bhHY3za576HMKJFs+OnQ9jS96Du5vNsDKkyedQkik+sEabbKAnCliv9BEsHZgQ==", + "version": "28.0.8", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-28.0.8.tgz", + "integrity": "sha512-5FaG0lXmRPzApix8oFG8RKjAz4ehtm8yMKOTy5HX3fY6W8kmvOrmcY0hKDElW52FJov+clhUbrKAqofnj4mXTg==", "dev": true, "dependencies": { "bs-logger": "0.x", @@ -8642,6 +9390,7 @@ }, "peerDependencies": { "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/types": "^28.0.0", "babel-jest": "^28.0.0", "jest": "^28.0.0", "typescript": ">=4.3" @@ -8650,6 +9399,9 @@ "@babel/core": { "optional": true }, + "@jest/types": { + "optional": true + }, "babel-jest": { "optional": true }, @@ -8659,15 +9411,16 @@ } }, "node_modules/ts-loader": { - "version": "9.4.2", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.2.tgz", - "integrity": "sha512-OmlC4WVmFv5I0PpaxYb+qGeGOdm5giHU7HwDDUjw59emP2UYMHy9fFSDcYgSNoH8sXcj4hGCSEhlDZ9ULeDraA==", + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", + "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==", "dev": true, "dependencies": { "chalk": "^4.1.0", "enhanced-resolve": "^5.0.0", "micromatch": "^4.0.0", - "semver": "^7.3.4" + "semver": "^7.3.4", + "source-map": "^0.7.4" }, "engines": { "node": ">=12.0.0" @@ -8678,9 +9431,9 @@ } }, "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", @@ -8778,9 +9531,18 @@ } }, "node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", + "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" + }, + "node_modules/tsscmp": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", + "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", + "license": "MIT", + "engines": { + "node": ">=0.6.x" + } }, "node_modules/tsutils": { "version": "3.21.0", @@ -8854,22 +9616,22 @@ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/uid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.1.tgz", - "integrity": "sha512-PF+1AnZgycpAIEmNtjxGBVmKbZAQguaa4pBUq6KNaGEcpzZ2klCNZLM34tsjp76maN00TttiiUf6zkIBpJQm2A==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz", + "integrity": "sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==", "dependencies": { "@lukeed/csprng": "^1.0.0" }, @@ -8889,9 +9651,9 @@ } }, "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "engines": { "node": ">= 10.0.0" @@ -8906,9 +9668,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", + "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", "dev": true, "funding": [ { @@ -8918,14 +9680,18 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.1.2", + "picocolors": "^1.0.1" }, "bin": { - "browserslist-lint": "cli.js" + "update-browserslist-db": "cli.js" }, "peerDependencies": { "browserslist": ">= 4.21.0" @@ -8953,6 +9719,11 @@ "node": ">= 0.4.0" } }, + "node_modules/uuid-random": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/uuid-random/-/uuid-random-1.3.2.tgz", + "integrity": "sha512-UOzej0Le/UgkbWEO8flm+0y+G+ljUon1QWTEZOq1rnMAsxo2+SckbiZdKzAHHlVh6gJqI1TjC/xwgR50MuCrBQ==" + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", @@ -8960,23 +9731,29 @@ "dev": true }, "node_modules/v8-to-istanbul": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", - "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", "dev": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" + "convert-source-map": "^2.0.0" }, "engines": { "node": ">=10.12.0" } }, + "node_modules/v8-to-istanbul/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, "node_modules/validator": { - "version": "13.9.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.9.0.tgz", - "integrity": "sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==", + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", + "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", "engines": { "node": ">= 0.10" } @@ -8999,9 +9776,9 @@ } }, "node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", + "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", "dev": true, "dependencies": { "glob-to-regexp": "^0.4.1", @@ -9026,35 +9803,35 @@ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "node_modules/webpack": { - "version": "5.77.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.77.0.tgz", - "integrity": "sha512-sbGNjBr5Ya5ss91yzjeJTLKyfiwo5C628AFjEa6WSXcZa4E+F57om3Cc8xLb1Jh0b243AWuSYRf3dn7HVeFQ9Q==", + "version": "5.93.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.93.0.tgz", + "integrity": "sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA==", "dev": true, "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.3", - "@types/estree": "^0.0.51", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", + "@types/estree": "^1.0.5", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", "acorn": "^8.7.1", - "acorn-import-assertions": "^1.7.6", - "browserslist": "^4.14.5", + "acorn-import-attributes": "^1.9.5", + "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.10.0", - "es-module-lexer": "^0.9.0", + "enhanced-resolve": "^5.17.0", + "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", + "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", + "schema-utils": "^3.2.0", "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.4.0", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", "webpack-sources": "^3.2.3" }, "bin": { @@ -9185,9 +9962,9 @@ } }, "node_modules/word-wrap": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", - "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -9261,9 +10038,9 @@ } }, "node_modules/yargs": { - "version": "17.7.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", - "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "dependencies": { "cliui": "^8.0.1", @@ -9308,6917 +10085,5 @@ "url": "https://github.com/sponsors/sindresorhus" } } - }, - "dependencies": { - "@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@angular-devkit/core": { - "version": "15.2.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-15.2.4.tgz", - "integrity": "sha512-yl+0j1bMwJLKShsyCXw77tbJG8Sd21+itisPLL2MgEpLNAO252kr9zG4TLlFRJyKVftm2l1h78KjqvM5nbOXNg==", - "dev": true, - "requires": { - "ajv": "8.12.0", - "ajv-formats": "2.1.1", - "jsonc-parser": "3.2.0", - "rxjs": "6.6.7", - "source-map": "0.7.4" - }, - "dependencies": { - "rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "@angular-devkit/schematics": { - "version": "15.2.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-15.2.4.tgz", - "integrity": "sha512-/W7/vvn59PAVLzhcvD4/N/E8RDhub8ny1A7I96LTRjC5o+yvVV16YJ4YJzolrRrIEN01KmLVQJ9A58VCaweMgw==", - "dev": true, - "requires": { - "@angular-devkit/core": "15.2.4", - "jsonc-parser": "3.2.0", - "magic-string": "0.29.0", - "ora": "5.4.1", - "rxjs": "6.6.7" - }, - "dependencies": { - "rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "@angular-devkit/schematics-cli": { - "version": "15.2.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-15.2.4.tgz", - "integrity": "sha512-QTTKEH5HOkxvQtCxb2Lna2wubehkaIzA6DKUBISijPQliLomw74tzc7lXCywmMqRTbQPVRLG3kBK97hR4x67nA==", - "dev": true, - "requires": { - "@angular-devkit/core": "15.2.4", - "@angular-devkit/schematics": "15.2.4", - "ansi-colors": "4.1.3", - "inquirer": "8.2.4", - "symbol-observable": "4.0.0", - "yargs-parser": "21.1.1" - }, - "dependencies": { - "inquirer": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", - "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^7.0.0" - } - } - } - }, - "@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", - "dev": true, - "requires": { - "@babel/highlight": "^7.18.6" - } - }, - "@babel/compat-data": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz", - "integrity": "sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==", - "dev": true - }, - "@babel/core": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.3.tgz", - "integrity": "sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.3", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.21.2", - "@babel/helpers": "^7.21.0", - "@babel/parser": "^7.21.3", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.3", - "@babel/types": "^7.21.3", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.3.tgz", - "integrity": "sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==", - "dev": true, - "requires": { - "@babel/types": "^7.21.3", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - }, - "dependencies": { - "@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - } - } - }, - "@babel/helper-compilation-targets": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", - "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.21.3", - "lru-cache": "^5.1.1", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", - "dev": true - }, - "@babel/helper-function-name": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", - "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", - "dev": true, - "requires": { - "@babel/template": "^7.20.7", - "@babel/types": "^7.21.0" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-module-transforms": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", - "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.2", - "@babel/types": "^7.21.2" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", - "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", - "dev": true - }, - "@babel/helper-simple-access": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", - "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", - "dev": true, - "requires": { - "@babel/types": "^7.20.2" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", - "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", - "dev": true - }, - "@babel/helpers": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", - "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", - "dev": true, - "requires": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0" - } - }, - "@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/parser": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.3.tgz", - "integrity": "sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==", - "dev": true - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-typescript": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", - "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.19.0" - } - }, - "@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" - } - }, - "@babel/traverse": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.3.tgz", - "integrity": "sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.3", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.3", - "@babel/types": "^7.21.3", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "dependencies": { - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - } - } - }, - "@babel/types": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.3.tgz", - "integrity": "sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - } - }, - "@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "dev": true, - "optional": true - }, - "@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "dependencies": { - "@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - } - } - }, - "@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^3.3.0" - } - }, - "@eslint-community/regexpp": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.0.tgz", - "integrity": "sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ==", - "dev": true - }, - "@eslint/eslintrc": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.2.tgz", - "integrity": "sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.5.1", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - } - } - }, - "@eslint/js": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.37.0.tgz", - "integrity": "sha512-x5vzdtOOGgFVDCUs81QRB2+liax8rFg3+7hqM+QhBG0/G3F1ZsoYl97UrqgHgQ9KKT7G6c4V+aTUCgu/n22v1A==", - "dev": true - }, - "@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - } - }, - "@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true - }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } - } - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true - }, - "@jest/console": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", - "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3", - "slash": "^3.0.0" - } - }, - "@jest/core": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-28.1.3.tgz", - "integrity": "sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA==", - "dev": true, - "requires": { - "@jest/console": "^28.1.3", - "@jest/reporters": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^28.1.3", - "jest-config": "^28.1.3", - "jest-haste-map": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-regex-util": "^28.0.2", - "jest-resolve": "^28.1.3", - "jest-resolve-dependencies": "^28.1.3", - "jest-runner": "^28.1.3", - "jest-runtime": "^28.1.3", - "jest-snapshot": "^28.1.3", - "jest-util": "^28.1.3", - "jest-validate": "^28.1.3", - "jest-watcher": "^28.1.3", - "micromatch": "^4.0.4", - "pretty-format": "^28.1.3", - "rimraf": "^3.0.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "@jest/environment": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz", - "integrity": "sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==", - "dev": true, - "requires": { - "@jest/fake-timers": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "jest-mock": "^28.1.3" - } - }, - "@jest/expect": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-28.1.3.tgz", - "integrity": "sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw==", - "dev": true, - "requires": { - "expect": "^28.1.3", - "jest-snapshot": "^28.1.3" - } - }, - "@jest/expect-utils": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-28.1.3.tgz", - "integrity": "sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA==", - "dev": true, - "requires": { - "jest-get-type": "^28.0.2" - } - }, - "@jest/fake-timers": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz", - "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@sinonjs/fake-timers": "^9.1.2", - "@types/node": "*", - "jest-message-util": "^28.1.3", - "jest-mock": "^28.1.3", - "jest-util": "^28.1.3" - } - }, - "@jest/globals": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-28.1.3.tgz", - "integrity": "sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA==", - "dev": true, - "requires": { - "@jest/environment": "^28.1.3", - "@jest/expect": "^28.1.3", - "@jest/types": "^28.1.3" - } - }, - "@jest/reporters": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-28.1.3.tgz", - "integrity": "sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg==", - "dev": true, - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "@jridgewell/trace-mapping": "^0.3.13", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3", - "jest-worker": "^28.1.3", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^9.0.1" - } - }, - "@jest/schemas": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", - "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "@jest/source-map": { - "version": "28.1.2", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-28.1.2.tgz", - "integrity": "sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.13", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - } - }, - "@jest/test-result": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", - "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", - "dev": true, - "requires": { - "@jest/console": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/test-sequencer": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-28.1.3.tgz", - "integrity": "sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw==", - "dev": true, - "requires": { - "@jest/test-result": "^28.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.1.3", - "slash": "^3.0.0" - } - }, - "@jest/transform": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-28.1.3.tgz", - "integrity": "sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/types": "^28.1.3", - "@jridgewell/trace-mapping": "^0.3.13", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.1.3", - "jest-regex-util": "^28.0.2", - "jest-util": "^28.1.3", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.1" - } - }, - "@jest/types": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", - "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", - "dev": true, - "requires": { - "@jest/schemas": "^28.1.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true - }, - "@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true - }, - "@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "dependencies": { - "@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - } - } - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - } - }, - "@lukeed/csprng": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.0.1.tgz", - "integrity": "sha512-uSvJdwQU5nK+Vdf6zxcWAY2A8r7uqe+gePwLWzJ+fsQehq18pc0I2hJKwypZ2aLM90+Er9u1xn4iLJPZ+xlL4g==" - }, - "@mapbox/node-pre-gyp": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz", - "integrity": "sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==", - "requires": { - "detect-libc": "^2.0.0", - "https-proxy-agent": "^5.0.0", - "make-dir": "^3.1.0", - "node-fetch": "^2.6.7", - "nopt": "^5.0.0", - "npmlog": "^5.0.1", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.11" - } - }, - "@nestjs/cli": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-9.3.0.tgz", - "integrity": "sha512-v/E8Y3zFk30+FljETvPgpoGIUiOfWuOe6WUFw3ExGfDeWrF/A8ceupDHPWNknBAqvNtz2kVrWu5mwsZUEKGIgg==", - "dev": true, - "requires": { - "@angular-devkit/core": "15.2.4", - "@angular-devkit/schematics": "15.2.4", - "@angular-devkit/schematics-cli": "15.2.4", - "@nestjs/schematics": "^9.0.4", - "chalk": "4.1.2", - "chokidar": "3.5.3", - "cli-table3": "0.6.3", - "commander": "4.1.1", - "fork-ts-checker-webpack-plugin": "8.0.0", - "inquirer": "8.2.5", - "node-emoji": "1.11.0", - "ora": "5.4.1", - "os-name": "4.0.1", - "rimraf": "4.4.0", - "shelljs": "0.8.5", - "source-map-support": "0.5.21", - "tree-kill": "1.2.2", - "tsconfig-paths": "4.1.2", - "tsconfig-paths-webpack-plugin": "4.0.1", - "typescript": "4.9.5", - "webpack": "5.76.2", - "webpack-node-externals": "3.0.0" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "glob": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.2.tgz", - "integrity": "sha512-BTv/JhKXFEHsErMte/AnfiSv8yYOLLiyH2lTg8vn02O21zWFgHPTfxtgn1QRe7NRgggUhC8hacR2Re94svHqeA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "minimatch": "^7.4.1", - "minipass": "^4.2.4", - "path-scurry": "^1.6.1" - } - }, - "minimatch": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.3.tgz", - "integrity": "sha512-5UB4yYusDtkRPbRiy1cqZ1IpGNcJCGlEMG17RKzPddpyiPKoCdwohbED8g4QXT0ewCt8LTkQXuljsUfQ3FKM4A==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - }, - "rimraf": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.4.0.tgz", - "integrity": "sha512-X36S+qpCUR0HjXlkDe4NAOhS//aHH0Z+h8Ckf2auGJk3PTnx5rLmrHkwNdbVQuCSUhOyFrlRvFEllZOYE+yZGQ==", - "dev": true, - "requires": { - "glob": "^9.2.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true - }, - "tsconfig-paths": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.1.2.tgz", - "integrity": "sha512-uhxiMgnXQp1IR622dUXI+9Ehnws7i/y6xvpZB9IbUVOPy0muvdvgXeZOn88UcGPiT98Vp3rJPTa8bFoalZ3Qhw==", - "dev": true, - "requires": { - "json5": "^2.2.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "webpack": { - "version": "5.76.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.2.tgz", - "integrity": "sha512-Th05ggRm23rVzEOlX8y67NkYCHa9nTNcwHPBhdg+lKG+mtiW7XgggjAeeLnADAe7mLjJ6LUNfgHAuRRh+Z6J7w==", - "dev": true, - "requires": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^0.0.51", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.7.6", - "browserslist": "^4.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.10.0", - "es-module-lexer": "^0.9.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.4.0", - "webpack-sources": "^3.2.3" - } - } - } - }, - "@nestjs/common": { - "version": "9.3.12", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-9.3.12.tgz", - "integrity": "sha512-NtrUG2VgCbhmZEO1yRt/Utq16uFRV+xeHAOtdYIsfHGG0ssAV2lVLlvFFAQYh0SQ+KuYY1Gsxd3GK2JFoJCNqQ==", - "requires": { - "iterare": "1.2.1", - "tslib": "2.5.0", - "uid": "2.0.1" - } - }, - "@nestjs/core": { - "version": "9.3.12", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-9.3.12.tgz", - "integrity": "sha512-Qe0ZjJo7bOlfudn7KHLppYrt5i4k1nR1+9d5ppYat2bb5knCIT4kIqblj666n+22/2zvsHRiTo015cLyLKsLRQ==", - "requires": { - "@nuxtjs/opencollective": "0.3.2", - "fast-safe-stringify": "2.1.1", - "iterare": "1.2.1", - "path-to-regexp": "3.2.0", - "tslib": "2.5.0", - "uid": "2.0.1" - } - }, - "@nestjs/jwt": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-10.0.3.tgz", - "integrity": "sha512-WO8MI3uEMOFKpbO+SAg6l4aRCr+9KvaL+raFMZaXuEUDphXek6pqdox+4tex9242pNSJUA0trfAMaiy/yVrXQg==", - "requires": { - "@types/jsonwebtoken": "9.0.1", - "jsonwebtoken": "9.0.0" - }, - "dependencies": { - "@types/jsonwebtoken": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.1.tgz", - "integrity": "sha512-c5ltxazpWabia/4UzhIoaDcIza4KViOQhdbjRlfcIGVnsE3c3brkz9Z+F/EeJIECOQP7W7US2hNE930cWWkPiw==", - "requires": { - "@types/node": "*" - } - } - } - }, - "@nestjs/passport": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-9.0.3.tgz", - "integrity": "sha512-HplSJaimEAz1IOZEu+pdJHHJhQyBOPAYWXYHfAPQvRqWtw4FJF1VXl1Qtk9dcXQX1eKytDtH+qBzNQc19GWNEg==", - "requires": {} - }, - "@nestjs/platform-express": { - "version": "9.3.12", - "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-9.3.12.tgz", - "integrity": "sha512-iQToH9rnZHmm3a2YDKLEN7weU2qC/EVOBnuwTf1lIkqB48yLxlykSJu3KmgtlUUNDt2/HY527QIo+GZSZfCLyg==", - "requires": { - "body-parser": "1.20.2", - "cors": "2.8.5", - "express": "4.18.2", - "multer": "1.4.4-lts.1", - "tslib": "2.5.0" - } - }, - "@nestjs/schematics": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-9.0.4.tgz", - "integrity": "sha512-egurCfAc4e5i1r2TmeAF0UrOKejFmT5oTdv4b7HcOVPupc3QGU7CbEfGleL3mkM5AjrixTQeMxU9bJ00ttAbGg==", - "dev": true, - "requires": { - "@angular-devkit/core": "15.0.4", - "@angular-devkit/schematics": "15.0.4", - "fs-extra": "11.1.0", - "jsonc-parser": "3.2.0", - "pluralize": "8.0.0" - }, - "dependencies": { - "@angular-devkit/core": { - "version": "15.0.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-15.0.4.tgz", - "integrity": "sha512-4ITpRAevd652SxB+qNesIQ9qfbm7wT5UBU5kJOPPwGL77I21g8CQpkmV1n5VSacPvC9Zbz90feOWexf7w7JzcA==", - "dev": true, - "requires": { - "ajv": "8.11.0", - "ajv-formats": "2.1.1", - "jsonc-parser": "3.2.0", - "rxjs": "6.6.7", - "source-map": "0.7.4" - } - }, - "@angular-devkit/schematics": { - "version": "15.0.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-15.0.4.tgz", - "integrity": "sha512-/gXiLFS0+xFdx6wPoBpe/c6/K9I5edMpaASqPf4XheKtrsSvL+qTlIi3nsbfItzOiDXbaBmlbxGfkMHz/yg0Ig==", - "dev": true, - "requires": { - "@angular-devkit/core": "15.0.4", - "jsonc-parser": "3.2.0", - "magic-string": "0.26.7", - "ora": "5.4.1", - "rxjs": "6.6.7" - } - }, - "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "magic-string": { - "version": "0.26.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.7.tgz", - "integrity": "sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==", - "dev": true, - "requires": { - "sourcemap-codec": "^1.4.8" - } - }, - "rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "@nestjs/testing": { - "version": "9.3.12", - "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-9.3.12.tgz", - "integrity": "sha512-nH274IXEqU4hr4bcb71POe58hYLONt9RcfKKM5ZvOS7wYMnybMpKKR8DkC1WcfE1P2k2GQmQoHeSH5emPtYrBA==", - "dev": true, - "requires": { - "tslib": "2.5.0" - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@nuxtjs/opencollective": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz", - "integrity": "sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==", - "requires": { - "chalk": "^4.1.0", - "consola": "^2.15.0", - "node-fetch": "^2.6.1" - } - }, - "@prisma/client": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.16.0.tgz", - "integrity": "sha512-CBD+5IdZPiavhLkQokvsz1uz4r9ppixaqY/ajybWs4WXNnsDVMBKEqN3BiPzpSo79jiy22VKj/67pqt4VwIg9w==", - "requires": { - "@prisma/engines-version": "4.16.0-66.b20ead4d3ab9e78ac112966e242ded703f4a052c" - } - }, - "@prisma/engines": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.16.0.tgz", - "integrity": "sha512-M6XoMRXnqL0rqZGQS8ZpNiHYG4G1fKBdoqW/oBtHnr1in5UYgerZqal3CXchmd6OBD/770PE9dtjQuqcilZJUA==" - }, - "@prisma/engines-version": { - "version": "4.16.0-66.b20ead4d3ab9e78ac112966e242ded703f4a052c", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.16.0-66.b20ead4d3ab9e78ac112966e242ded703f4a052c.tgz", - "integrity": "sha512-tMWAF/qF00fbUH1HB4Yjmz6bjh7fzkb7Y3NRoUfMlHu6V+O45MGvqwYxqwBjn1BIUXkl3r04W351D4qdJjrgvA==" - }, - "@sinclair/typebox": { - "version": "0.24.51", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", - "dev": true - }, - "@sinonjs/commons": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", - "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/fake-timers": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", - "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - }, - "@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", - "dev": true - }, - "@types/babel__core": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", - "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==", - "dev": true, - "requires": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@types/babel__traverse": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", - "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", - "dev": true, - "requires": { - "@babel/types": "^7.3.0" - } - }, - "@types/bcrypt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.0.tgz", - "integrity": "sha512-agtcFKaruL8TmcvqbndlqHPSJgsolhf/qPWchFlgnW1gECTN/nKbFcoFnvKAQRFfKbh+BO6A3SWdJu9t+xF3Lw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", - "requires": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", - "requires": { - "@types/node": "*" - } - }, - "@types/cookie-parser": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.3.tgz", - "integrity": "sha512-CqSKwFwefj4PzZ5n/iwad/bow2hTCh0FlNAeWLtQM3JA/NX/iYagIpWG2cf1bQKQ2c9gU2log5VUCrn7LDOs0w==", - "requires": { - "@types/express": "*" - } - }, - "@types/cookiejar": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz", - "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==", - "dev": true - }, - "@types/eslint": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.37.0.tgz", - "integrity": "sha512-Piet7dG2JBuDIfohBngQ3rCt7MgO9xCO4xIMKxBThCq5PNRB91IjlJ10eJVwfoNtvTErmxLzwBZ7rHZtbOMmFQ==", - "dev": true, - "requires": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "@types/eslint-scope": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", - "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", - "dev": true, - "requires": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "@types/estree": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", - "dev": true - }, - "@types/express": { - "version": "4.17.17", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", - "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", - "requires": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "@types/express-serve-static-core": { - "version": "4.17.33", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz", - "integrity": "sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==", - "requires": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*" - } - }, - "@types/express-session": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.7.tgz", - "integrity": "sha512-L25080PBYoRLu472HY/HNCxaXY8AaGgqGC8/p/8+BYMhG0RDOLQ1wpXOpAzr4Gi5TGozTKyJv5BVODM5UNyVMw==", - "requires": { - "@types/express": "*" - } - }, - "@types/graceful-fs": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", - "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", - "dev": true - }, - "@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/jest": { - "version": "28.1.4", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-28.1.4.tgz", - "integrity": "sha512-telv6G5N7zRJiLcI3Rs3o+ipZ28EnE+7EvF0pSrt2pZOMnAVI/f+6/LucDxOvcBcTeTL3JMF744BbVQAVBUQRA==", - "dev": true, - "requires": { - "jest-matcher-utils": "^28.0.0", - "pretty-format": "^28.0.0" - } - }, - "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, - "@types/jsonwebtoken": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-drE6uz7QBKq1fYqqoFKTDRdFCPHd5TCub75BM+D+cMx7NU9hUz7SESLfC2fSCXVFMO5Yj8sOWHuGqPgjc+fz0Q==", - "requires": { - "@types/node": "*" - } - }, - "@types/mime": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", - "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" - }, - "@types/morgan": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.4.tgz", - "integrity": "sha512-cXoc4k+6+YAllH3ZHmx4hf7La1dzUk6keTR4bF4b4Sc0mZxU/zK4wO7l+ZzezXm/jkYj/qC+uYGZrarZdIVvyQ==", - "requires": { - "@types/node": "*" - } - }, - "@types/node": { - "version": "16.18.23", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.23.tgz", - "integrity": "sha512-XAMpaw1s1+6zM+jn2tmw8MyaRDIJfXxqmIQIS0HfoGYPuf7dUWeiUKopwq13KFX9lEp1+THGtlaaYx39Nxr58g==" - }, - "@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", - "dev": true - }, - "@types/passport": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.12.tgz", - "integrity": "sha512-QFdJ2TiAEoXfEQSNDISJR1Tm51I78CymqcBa8imbjo6dNNu+l2huDxxbDEIoFIwOSKMkOfHEikyDuZ38WwWsmw==", - "requires": { - "@types/express": "*" - } - }, - "@types/passport-jwt": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-3.0.8.tgz", - "integrity": "sha512-VKJZDJUAHFhPHHYvxdqFcc5vlDht8Q2pL1/ePvKAgqRThDaCc84lSYOTQmnx3+JIkDlN+2KfhFhXIzlcVT+Pcw==", - "requires": { - "@types/express": "*", - "@types/jsonwebtoken": "*", - "@types/passport-strategy": "*" - } - }, - "@types/passport-local": { - "version": "1.0.35", - "resolved": "https://registry.npmjs.org/@types/passport-local/-/passport-local-1.0.35.tgz", - "integrity": "sha512-K4eLTJ8R0yYW8TvCqkjB0pTKoqfUSdl5PfZdidTjV2ETV3604fQxtY6BHKjQWAx50WUS0lqzBvKv3LoI1ZBPeA==", - "requires": { - "@types/express": "*", - "@types/passport": "*", - "@types/passport-strategy": "*" - } - }, - "@types/passport-strategy": { - "version": "0.2.35", - "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.35.tgz", - "integrity": "sha512-o5D19Jy2XPFoX2rKApykY15et3Apgax00RRLf0RUotPDUsYrQa7x4howLYr9El2mlUApHmCMv5CZ1IXqKFQ2+g==", - "requires": { - "@types/express": "*", - "@types/passport": "*" - } - }, - "@types/prettier": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", - "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==", - "dev": true - }, - "@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" - }, - "@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" - }, - "@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", - "dev": true - }, - "@types/serve-static": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.1.tgz", - "integrity": "sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==", - "requires": { - "@types/mime": "*", - "@types/node": "*" - } - }, - "@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", - "dev": true - }, - "@types/superagent": { - "version": "4.1.16", - "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.16.tgz", - "integrity": "sha512-tLfnlJf6A5mB6ddqF159GqcDizfzbMUB1/DeT59/wBNqzRTNNKsaw79A/1TZ84X+f/EwWH8FeuSkjlCLyqS/zQ==", - "dev": true, - "requires": { - "@types/cookiejar": "*", - "@types/node": "*" - } - }, - "@types/supertest": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.12.tgz", - "integrity": "sha512-X3HPWTwXRerBZS7Mo1k6vMVR1Z6zmJcDVn5O/31whe0tnjE4te6ZJSJGq1RiqHPjzPdMTfjCFogDJmwng9xHaQ==", - "dev": true, - "requires": { - "@types/superagent": "*" - } - }, - "@types/validator": { - "version": "13.7.15", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.15.tgz", - "integrity": "sha512-yeinDVQunb03AEP8luErFcyf/7Lf7AzKCD0NXfgVoGCCQDNpZET8Jgq74oBgqKld3hafLbfzt/3inUdQvaFeXQ==" - }, - "@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", - "dev": true - }, - "@typescript-eslint/eslint-plugin": { - "version": "5.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.57.0.tgz", - "integrity": "sha512-itag0qpN6q2UMM6Xgk6xoHa0D0/P+M17THnr4SVgqn9Rgam5k/He33MA7/D7QoJcdMxHFyX7U9imaBonAX/6qA==", - "dev": true, - "requires": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.57.0", - "@typescript-eslint/type-utils": "5.57.0", - "@typescript-eslint/utils": "5.57.0", - "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/parser": { - "version": "5.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.57.0.tgz", - "integrity": "sha512-orrduvpWYkgLCyAdNtR1QIWovcNZlEm6yL8nwH/eTxWLd8gsP+25pdLHYzL2QdkqrieaDwLpytHqycncv0woUQ==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.57.0", - "@typescript-eslint/types": "5.57.0", - "@typescript-eslint/typescript-estree": "5.57.0", - "debug": "^4.3.4" - } - }, - "@typescript-eslint/scope-manager": { - "version": "5.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.57.0.tgz", - "integrity": "sha512-NANBNOQvllPlizl9LatX8+MHi7bx7WGIWYjPHDmQe5Si/0YEYfxSljJpoTyTWFTgRy3X8gLYSE4xQ2U+aCozSw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.57.0", - "@typescript-eslint/visitor-keys": "5.57.0" - } - }, - "@typescript-eslint/type-utils": { - "version": "5.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.57.0.tgz", - "integrity": "sha512-kxXoq9zOTbvqzLbdNKy1yFrxLC6GDJFE2Yuo3KqSwTmDOFjUGeWSakgoXT864WcK5/NAJkkONCiKb1ddsqhLXQ==", - "dev": true, - "requires": { - "@typescript-eslint/typescript-estree": "5.57.0", - "@typescript-eslint/utils": "5.57.0", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/types": { - "version": "5.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.57.0.tgz", - "integrity": "sha512-mxsod+aZRSyLT+jiqHw1KK6xrANm19/+VFALVFP5qa/aiJnlP38qpyaTd0fEKhWvQk6YeNZ5LGwI1pDpBRBhtQ==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.57.0.tgz", - "integrity": "sha512-LTzQ23TV82KpO8HPnWuxM2V7ieXW8O142I7hQTxWIHDcCEIjtkat6H96PFkYBQqGFLW/G/eVVOB9Z8rcvdY/Vw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.57.0", - "@typescript-eslint/visitor-keys": "5.57.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/utils": { - "version": "5.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.57.0.tgz", - "integrity": "sha512-ps/4WohXV7C+LTSgAL5CApxvxbMkl9B9AUZRtnEFonpIxZDIT7wC1xfvuJONMidrkB9scs4zhtRyIwHh4+18kw==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.57.0", - "@typescript-eslint/types": "5.57.0", - "@typescript-eslint/typescript-estree": "5.57.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.57.0.tgz", - "integrity": "sha512-ery2g3k0hv5BLiKpPuwYt9KBkAp2ugT6VvyShXdLOkax895EC55sP0Tx5L0fZaQueiK3fBLvHVvEl3jFS5ia+g==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.57.0", - "eslint-visitor-keys": "^3.3.0" - } - }, - "@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", - "dev": true, - "requires": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" - } - }, - "@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", - "dev": true - }, - "@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", - "dev": true - }, - "@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", - "dev": true - }, - "@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", - "dev": true, - "requires": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", - "dev": true - }, - "@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" - } - }, - "@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", - "dev": true, - "requires": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", - "dev": true, - "requires": { - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", - "dev": true - }, - "@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" - } - }, - "@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" - } - }, - "@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" - } - }, - "@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" - } - }, - "@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.11.1", - "@xtuc/long": "4.2.2" - } - }, - "@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "requires": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - } - }, - "acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", - "dev": true - }, - "acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "dev": true, - "requires": {} - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} - }, - "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true - }, - "agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "requires": { - "debug": "4" - } - }, - "aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - } - }, - "ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, - "requires": { - "ajv": "^8.0.0" - } - }, - "ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "requires": { - "type-fest": "^0.21.3" - }, - "dependencies": { - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true - } - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "append-field": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", - "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==" - }, - "aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" - }, - "are-we-there-yet": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", - "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true - }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "axios": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", - "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", - "requires": { - "follow-redirects": "^1.15.0", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "babel-jest": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-28.1.3.tgz", - "integrity": "sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q==", - "dev": true, - "requires": { - "@jest/transform": "^28.1.3", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^28.1.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - } - }, - "babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - } - }, - "babel-plugin-jest-hoist": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-28.1.3.tgz", - "integrity": "sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q==", - "dev": true, - "requires": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "requires": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - } - }, - "babel-preset-jest": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-28.1.3.tgz", - "integrity": "sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A==", - "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^28.1.3", - "babel-preset-current-node-syntax": "^1.0.0" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true - }, - "basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "requires": { - "safe-buffer": "5.1.2" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - }, - "bcrypt": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.0.tgz", - "integrity": "sha512-RHBS7HI5N5tEnGTmtR/pppX0mmDSBpQ4aCBsj7CEQfYXDcO74A8sIBYcJMuCsis2E81zDxeENYhv66oZwLiA+Q==", - "requires": { - "@mapbox/node-pre-gyp": "^1.0.10", - "node-addon-api": "^5.0.0" - } - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", - "requires": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" - } - }, - "bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "requires": { - "fast-json-stable-stringify": "2.x" - } - }, - "bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "requires": { - "node-int64": "^0.4.0" - } - }, - "buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" - }, - "buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" - }, - "busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "requires": { - "streamsearch": "^1.1.0" - } - }, - "bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "caniuse-lite": { - "version": "1.0.30001473", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001473.tgz", - "integrity": "sha512-ewDad7+D2vlyy+E4UJuVfiBsU69IL+8oVmTuZnH5Q6CIUbxNfI50uVpRHbUPDD6SUaN2o0Lh4DhTrvLG/Tn1yg==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true - }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" - }, - "chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true - }, - "ci-info": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", - "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", - "dev": true - }, - "cjs-module-lexer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", - "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", - "dev": true - }, - "class-transformer": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", - "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==" - }, - "class-validator": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.0.tgz", - "integrity": "sha512-ct3ltplN8I9fOwUd8GrP8UQixwff129BkEtuWDKL5W45cQuLd19xqmTLu5ge78YDm/fdje6FMt0hGOhl0lii3A==", - "requires": { - "@types/validator": "^13.7.10", - "libphonenumber-js": "^1.10.14", - "validator": "^13.7.0" - } - }, - "clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true - }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-spinners": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", - "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", - "dev": true - }, - "cli-table3": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", - "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", - "dev": true, - "requires": { - "@colors/colors": "1.5.0", - "string-width": "^4.2.0" - } - }, - "cli-truncate": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", - "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", - "dev": true, - "requires": { - "slice-ansi": "^5.0.0", - "string-width": "^5.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true - }, - "emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "requires": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - } - }, - "strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "requires": { - "ansi-regex": "^6.0.1" - } - } - } - }, - "cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "dev": true - }, - "cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - } - }, - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true - }, - "collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", - "dev": true - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" - }, - "colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "consola": { - "version": "2.15.3", - "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", - "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==" - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" - }, - "content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "requires": { - "safe-buffer": "5.2.1" - } - }, - "content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" - }, - "convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" - }, - "cookie-parser": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", - "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", - "requires": { - "cookie": "0.4.1", - "cookie-signature": "1.0.6" - }, - "dependencies": { - "cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" - } - } - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, - "cookiejar": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", - "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", - "dev": true - }, - "core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" - }, - "cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "requires": { - "object-assign": "^4", - "vary": "^1" - } - }, - "cosmiconfig": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", - "dev": true, - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - } - }, - "create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - }, - "dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", - "dev": true - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true - }, - "defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dev": true, - "requires": { - "clone": "^1.0.2" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" - }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" - }, - "destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" - }, - "detect-libc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", - "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==" - }, - "detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true - }, - "dezalgo": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", - "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", - "dev": true, - "requires": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, - "diff-sequences": { - "version": "28.1.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz", - "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==", - "dev": true - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "dotenv": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", - "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==" - }, - "dotenv-cli": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/dotenv-cli/-/dotenv-cli-7.2.1.tgz", - "integrity": "sha512-ODHbGTskqRtXAzZapDPvgNuDVQApu4oKX8lZW7Y0+9hKA6le1ZJlyRS687oU9FXjOVEDU/VFV6zI125HzhM1UQ==", - "requires": { - "cross-spawn": "^7.0.3", - "dotenv": "^16.0.0", - "dotenv-expand": "^10.0.0", - "minimist": "^1.2.6" - } - }, - "dotenv-expand": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz", - "integrity": "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==" - }, - "eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, - "ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "electron-to-chromium": { - "version": "1.4.345", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.345.tgz", - "integrity": "sha512-znGhOQK2TUYLICgS25uaM0a7pHy66rSxbre7l762vg9AUoCcJK+Bu+HCPWpjL/U/kK8/Hf+6E0szAUJSyVYb3Q==", - "dev": true - }, - "emittery": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", - "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "enhanced-resolve": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", - "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", - "dev": true - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "eslint": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.37.0.tgz", - "integrity": "sha512-NU3Ps9nI05GUoVMxcZx1J8CNR6xOvUT4jAUMH5+z8lpp3aEdPVCImKw6PWG4PY+Vfkpr+jvMpxs/qoE7wq0sPw==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.2", - "@eslint/js": "8.37.0", - "@humanwhocodes/config-array": "^0.11.8", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-visitor-keys": "^3.4.0", - "espree": "^9.5.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", - "js-yaml": "^4.1.0", - "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.1", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - } - } - }, - "eslint-config-prettier": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", - "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", - "dev": true, - "requires": {} - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-visitor-keys": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz", - "integrity": "sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==", - "dev": true - }, - "espree": { - "version": "9.5.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.1.tgz", - "integrity": "sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg==", - "dev": true, - "requires": { - "acorn": "^8.8.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.0" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" - }, - "events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true - }, - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true - }, - "expect": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/expect/-/expect-28.1.3.tgz", - "integrity": "sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==", - "dev": true, - "requires": { - "@jest/expect-utils": "^28.1.3", - "jest-get-type": "^28.0.2", - "jest-matcher-utils": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3" - } - }, - "express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", - "requires": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.1", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", - "requires": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" - }, - "raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "requires": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - } - } - }, - "express-session": { - "version": "1.17.3", - "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz", - "integrity": "sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==", - "requires": { - "cookie": "0.4.2", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~2.0.0", - "on-headers": "~1.0.2", - "parseurl": "~1.3.3", - "safe-buffer": "5.2.1", - "uid-safe": "~2.1.5" - }, - "dependencies": { - "cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==" - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "fast-safe-stringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" - }, - "fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "requires": { - "bser": "2.1.1" - } - }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - }, - "dependencies": { - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - } - } - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", - "dev": true - }, - "follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" - }, - "fork-ts-checker-webpack-plugin": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-8.0.0.tgz", - "integrity": "sha512-mX3qW3idpueT2klaQXBzrIM/pHw+T0B/V9KHEvNrqijTq9NFnMZU6oreVxDYcf33P8a5cW+67PjodNHthGnNVg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.16.7", - "chalk": "^4.1.2", - "chokidar": "^3.5.3", - "cosmiconfig": "^7.0.1", - "deepmerge": "^4.2.2", - "fs-extra": "^10.0.0", - "memfs": "^3.4.1", - "minimatch": "^3.0.4", - "node-abort-controller": "^3.0.1", - "schema-utils": "^3.1.1", - "semver": "^7.3.5", - "tapable": "^2.2.1" - }, - "dependencies": { - "fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - } - } - }, - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "formidable": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", - "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", - "dev": true, - "requires": { - "dezalgo": "^1.0.4", - "hexoid": "^1.0.0", - "once": "^1.4.0", - "qs": "^6.11.0" - } - }, - "forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" - }, - "fs-extra": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz", - "integrity": "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "requires": { - "minipass": "^3.0.0" - }, - "dependencies": { - "minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "requires": { - "yallist": "^4.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - } - } - }, - "fs-monkey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", - "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "gauge": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", - "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "requires": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" - } - }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - } - }, - "get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true - }, - "globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "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" - } - }, - "graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" - }, - "hexoid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", - "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", - "dev": true - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - } - }, - "https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "requires": { - "agent-base": "6", - "debug": "4" - } - }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true - }, - "husky": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", - "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", - "dev": true - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true - }, - "ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "requires": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true - }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "inquirer": { - "version": "8.2.5", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz", - "integrity": "sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^7.0.0" - } - }, - "interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "dev": true - }, - "ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-path-inside": { - "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==", - "dev": true - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true - }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - } - }, - "istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "iterare": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz", - "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==" - }, - "jest": { - "version": "28.1.2", - "resolved": "https://registry.npmjs.org/jest/-/jest-28.1.2.tgz", - "integrity": "sha512-Tuf05DwLeCh2cfWCQbcz9UxldoDyiR1E9Igaei5khjonKncYdc6LDfynKCEWozK0oLE3GD+xKAo2u8x/0s6GOg==", - "dev": true, - "requires": { - "@jest/core": "^28.1.2", - "@jest/types": "^28.1.1", - "import-local": "^3.0.2", - "jest-cli": "^28.1.2" - } - }, - "jest-changed-files": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-28.1.3.tgz", - "integrity": "sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA==", - "dev": true, - "requires": { - "execa": "^5.0.0", - "p-limit": "^3.1.0" - } - }, - "jest-circus": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-28.1.3.tgz", - "integrity": "sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow==", - "dev": true, - "requires": { - "@jest/environment": "^28.1.3", - "@jest/expect": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^28.1.3", - "jest-matcher-utils": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-runtime": "^28.1.3", - "jest-snapshot": "^28.1.3", - "jest-util": "^28.1.3", - "p-limit": "^3.1.0", - "pretty-format": "^28.1.3", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-cli": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-28.1.3.tgz", - "integrity": "sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ==", - "dev": true, - "requires": { - "@jest/core": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/types": "^28.1.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^28.1.3", - "jest-util": "^28.1.3", - "jest-validate": "^28.1.3", - "prompts": "^2.0.1", - "yargs": "^17.3.1" - } - }, - "jest-config": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-28.1.3.tgz", - "integrity": "sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^28.1.3", - "@jest/types": "^28.1.3", - "babel-jest": "^28.1.3", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^28.1.3", - "jest-environment-node": "^28.1.3", - "jest-get-type": "^28.0.2", - "jest-regex-util": "^28.0.2", - "jest-resolve": "^28.1.3", - "jest-runner": "^28.1.3", - "jest-util": "^28.1.3", - "jest-validate": "^28.1.3", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^28.1.3", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - } - }, - "jest-diff": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz", - "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^28.1.1", - "jest-get-type": "^28.0.2", - "pretty-format": "^28.1.3" - } - }, - "jest-docblock": { - "version": "28.1.1", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-28.1.1.tgz", - "integrity": "sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA==", - "dev": true, - "requires": { - "detect-newline": "^3.0.0" - } - }, - "jest-each": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-28.1.3.tgz", - "integrity": "sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "chalk": "^4.0.0", - "jest-get-type": "^28.0.2", - "jest-util": "^28.1.3", - "pretty-format": "^28.1.3" - } - }, - "jest-environment-node": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-28.1.3.tgz", - "integrity": "sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A==", - "dev": true, - "requires": { - "@jest/environment": "^28.1.3", - "@jest/fake-timers": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "jest-mock": "^28.1.3", - "jest-util": "^28.1.3" - } - }, - "jest-get-type": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", - "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", - "dev": true - }, - "jest-haste-map": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-28.1.3.tgz", - "integrity": "sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.3.2", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^28.0.2", - "jest-util": "^28.1.3", - "jest-worker": "^28.1.3", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - } - }, - "jest-leak-detector": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz", - "integrity": "sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA==", - "dev": true, - "requires": { - "jest-get-type": "^28.0.2", - "pretty-format": "^28.1.3" - } - }, - "jest-matcher-utils": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz", - "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^28.1.3", - "jest-get-type": "^28.0.2", - "pretty-format": "^28.1.3" - } - }, - "jest-message-util": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", - "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^28.1.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^28.1.3", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-mock": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz", - "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@types/node": "*" - } - }, - "jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "requires": {} - }, - "jest-regex-util": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", - "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", - "dev": true - }, - "jest-resolve": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-28.1.3.tgz", - "integrity": "sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.1.3", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^28.1.3", - "jest-validate": "^28.1.3", - "resolve": "^1.20.0", - "resolve.exports": "^1.1.0", - "slash": "^3.0.0" - } - }, - "jest-resolve-dependencies": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz", - "integrity": "sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA==", - "dev": true, - "requires": { - "jest-regex-util": "^28.0.2", - "jest-snapshot": "^28.1.3" - } - }, - "jest-runner": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-28.1.3.tgz", - "integrity": "sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA==", - "dev": true, - "requires": { - "@jest/console": "^28.1.3", - "@jest/environment": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.10.2", - "graceful-fs": "^4.2.9", - "jest-docblock": "^28.1.1", - "jest-environment-node": "^28.1.3", - "jest-haste-map": "^28.1.3", - "jest-leak-detector": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-resolve": "^28.1.3", - "jest-runtime": "^28.1.3", - "jest-util": "^28.1.3", - "jest-watcher": "^28.1.3", - "jest-worker": "^28.1.3", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - } - } - }, - "jest-runtime": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-28.1.3.tgz", - "integrity": "sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw==", - "dev": true, - "requires": { - "@jest/environment": "^28.1.3", - "@jest/fake-timers": "^28.1.3", - "@jest/globals": "^28.1.3", - "@jest/source-map": "^28.1.2", - "@jest/test-result": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "execa": "^5.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-mock": "^28.1.3", - "jest-regex-util": "^28.0.2", - "jest-resolve": "^28.1.3", - "jest-snapshot": "^28.1.3", - "jest-util": "^28.1.3", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - } - }, - "jest-snapshot": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-28.1.3.tgz", - "integrity": "sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/babel__traverse": "^7.0.6", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^28.1.3", - "graceful-fs": "^4.2.9", - "jest-diff": "^28.1.3", - "jest-get-type": "^28.0.2", - "jest-haste-map": "^28.1.3", - "jest-matcher-utils": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3", - "natural-compare": "^1.4.0", - "pretty-format": "^28.1.3", - "semver": "^7.3.5" - } - }, - "jest-util": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", - "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "jest-validate": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-28.1.3.tgz", - "integrity": "sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^28.0.2", - "leven": "^3.1.0", - "pretty-format": "^28.1.3" - }, - "dependencies": { - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - } - } - }, - "jest-watcher": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", - "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", - "dev": true, - "requires": { - "@jest/test-result": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.10.2", - "jest-util": "^28.1.3", - "string-length": "^4.0.1" - } - }, - "jest-worker": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", - "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", - "dev": true, - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "dependencies": { - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "js-sdsl": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz", - "integrity": "sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==", - "dev": true - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true - }, - "jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", - "dev": true - }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, - "jsonwebtoken": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", - "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==", - "requires": { - "jws": "^3.2.2", - "lodash": "^4.17.21", - "ms": "^2.1.1", - "semver": "^7.3.8" - } - }, - "jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "requires": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "requires": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true - }, - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "libphonenumber-js": { - "version": "1.10.28", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.28.tgz", - "integrity": "sha512-1eAgjLrZA0+2Wgw4hs+4Q/kEBycxQo8ZLYnmOvZ3AlM8ImAVAJgDPlZtISLEzD1vunc2q8s2Pn7XwB7I8U3Kzw==" - }, - "lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", - "dev": true - }, - "lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "lint-staged": { - "version": "13.2.3", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.2.3.tgz", - "integrity": "sha512-zVVEXLuQIhr1Y7R7YAWx4TZLdvuzk7DnmrsTNL0fax6Z3jrpFcas+vKbzxhhvp6TA55m1SQuWkpzI1qbfDZbAg==", - "dev": true, - "requires": { - "chalk": "5.2.0", - "cli-truncate": "^3.1.0", - "commander": "^10.0.0", - "debug": "^4.3.4", - "execa": "^7.0.0", - "lilconfig": "2.1.0", - "listr2": "^5.0.7", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-inspect": "^1.12.3", - "pidtree": "^0.6.0", - "string-argv": "^0.3.1", - "yaml": "^2.2.2" - }, - "dependencies": { - "chalk": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", - "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", - "dev": true - }, - "commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "dev": true - }, - "execa": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.1.1.tgz", - "integrity": "sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^3.0.7", - "strip-final-newline": "^3.0.0" - } - }, - "human-signals": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", - "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", - "dev": true - }, - "is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true - }, - "mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true - }, - "npm-run-path": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", - "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", - "dev": true, - "requires": { - "path-key": "^4.0.0" - } - }, - "onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, - "requires": { - "mimic-fn": "^4.0.0" - } - }, - "path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true - }, - "strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true - }, - "yaml": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", - "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", - "dev": true - } - } - }, - "listr2": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-5.0.8.tgz", - "integrity": "sha512-mC73LitKHj9w6v30nLNGPetZIlfpUniNSsxxrbaPcWOjDb92SHPzJPi/t+v1YC/lxKz/AJ9egOjww0qUuFxBpA==", - "dev": true, - "requires": { - "cli-truncate": "^2.1.0", - "colorette": "^2.0.19", - "log-update": "^4.0.0", - "p-map": "^4.0.0", - "rfdc": "^1.3.0", - "rxjs": "^7.8.0", - "through": "^2.3.8", - "wrap-ansi": "^7.0.0" - }, - "dependencies": { - "cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", - "dev": true, - "requires": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - } - }, - "slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - } - } - }, - "loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "dev": true - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - } - }, - "log-update": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", - "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", - "dev": true, - "requires": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" - }, - "dependencies": { - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - } - } - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "macos-release": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.1.tgz", - "integrity": "sha512-DXqXhEM7gW59OjZO8NIjBCz9AQ1BEMrfiOAl4AYByHCtVHRF4KoGNO8mqQeM8lRCtQe/UnJ4imO/d2HdkKsd+A==", - "dev": true - }, - "magic-string": { - "version": "0.29.0", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.29.0.tgz", - "integrity": "sha512-WcfidHrDjMY+eLjlU+8OvwREqHwpgCeKVBUpQ3OhYYuvfaYCUgcbuBzappNzZvg/v8onU3oQj+BYpkOJe9Iw4Q==", - "dev": true, - "requires": { - "@jridgewell/sourcemap-codec": "^1.4.13" - } - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "requires": { - "tmpl": "1.0.5" - } - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" - }, - "memfs": { - "version": "3.4.13", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.13.tgz", - "integrity": "sha512-omTM41g3Skpvx5dSYeZIbXKcXoAVc/AoMNwn9TKx++L/gaen/+4TTttmu8ZSch5vfVJ8uJvGbroTsIlslRg6lg==", - "dev": true, - "requires": { - "fs-monkey": "^1.0.3" - } - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" - }, - "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - } - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" - }, - "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "requires": { - "mime-db": "1.52.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" - }, - "minipass": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.5.tgz", - "integrity": "sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==", - "dev": true - }, - "minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "requires": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "dependencies": { - "minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "requires": { - "yallist": "^4.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - } - } - }, - "mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "requires": { - "minimist": "^1.2.6" - } - }, - "morgan": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", - "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", - "requires": { - "basic-auth": "~2.0.1", - "debug": "2.6.9", - "depd": "~2.0.0", - "on-finished": "~2.3.0", - "on-headers": "~1.0.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", - "requires": { - "ee-first": "1.1.1" - } - } - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "multer": { - "version": "1.4.4-lts.1", - "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.4-lts.1.tgz", - "integrity": "sha512-WeSGziVj6+Z2/MwQo3GvqzgR+9Uc+qt8SwHKh3gvNPiISKfsMfG4SvCOFYlxxgkXt7yIV2i1yczehm0EOKIxIg==", - "requires": { - "append-field": "^1.0.0", - "busboy": "^1.0.0", - "concat-stream": "^1.5.2", - "mkdirp": "^0.5.4", - "object-assign": "^4.1.1", - "type-is": "^1.6.4", - "xtend": "^4.0.0" - } - }, - "mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, - "negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" - }, - "neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "node-abort-controller": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", - "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", - "dev": true - }, - "node-addon-api": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", - "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" - }, - "node-emoji": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", - "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", - "dev": true, - "requires": { - "lodash": "^4.17.21" - } - }, - "node-fetch": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", - "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", - "requires": { - "whatwg-url": "^5.0.0" - } - }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node-releases": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", - "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", - "dev": true - }, - "nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "requires": { - "abbrev": "1" - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "npmlog": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", - "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", - "requires": { - "are-we-there-yet": "^2.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", - "set-blocking": "^2.0.0" - } - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" - }, - "object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==" - }, - "on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "requires": { - "ee-first": "1.1.1" - } - }, - "on-headers": { - "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==" - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dev": true, - "requires": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - } - }, - "os-name": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/os-name/-/os-name-4.0.1.tgz", - "integrity": "sha512-xl9MAoU97MH1Xt5K9ERft2YfCAoaO6msy1OBA0ozxEC0x0TmIoE6K3QvgJMMZA9yKGLmHXNY/YZoDbiGDj4zYw==", - "dev": true, - "requires": { - "macos-release": "^2.5.0", - "windows-release": "^4.0.0" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "requires": { - "aggregate-error": "^3.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "passport": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz", - "integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==", - "requires": { - "passport-strategy": "1.x.x", - "pause": "0.0.1", - "utils-merge": "^1.0.1" - } - }, - "passport-jwt": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.1.tgz", - "integrity": "sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==", - "requires": { - "jsonwebtoken": "^9.0.0", - "passport-strategy": "^1.0.0" - } - }, - "passport-strategy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", - "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==" - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "path-scurry": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.6.3.tgz", - "integrity": "sha512-RAmB+n30SlN+HnNx6EbcpoDy9nwdpcGPnEKrJnu6GZoDWBdIjo1UQMVtW2ybtC7LC2oKLcMq8y5g8WnKLiod9g==", - "dev": true, - "requires": { - "lru-cache": "^7.14.1", - "minipass": "^4.0.2" - }, - "dependencies": { - "lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true - } - } - }, - "path-to-regexp": { - "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-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "pause": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", - "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "pidtree": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", - "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", - "dev": true - }, - "pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", - "dev": true - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - } - } - }, - "pluralize": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", - "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", - "dev": true - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "prettier": { - "version": "2.8.7", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz", - "integrity": "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==", - "dev": true - }, - "pretty-format": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", - "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", - "dev": true, - "requires": { - "@jest/schemas": "^28.1.3", - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - } - } - }, - "prisma": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-4.16.0.tgz", - "integrity": "sha512-kSCwbTm3LCephyGfZMJYqBXpPJXdJStg5xwfzeFmR5C05zfkOURK9pQpJF6uUQvFWm3lI9ZMSNkObmFkAPnB+g==", - "requires": { - "@prisma/engines": "4.16.0" - } - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" - }, - "prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - } - }, - "proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "requires": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - } - }, - "proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "dev": true - }, - "qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "requires": { - "side-channel": "^1.0.4" - } - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, - "random-bytes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", - "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==" - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" - }, - "raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "requires": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", - "dev": true, - "requires": { - "resolve": "^1.1.6" - } - }, - "reflect-metadata": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true - }, - "resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "dev": true, - "requires": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "requires": { - "resolve-from": "^5.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "resolve.exports": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz", - "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==", - "dev": true - }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "rfdc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "requires": { - "glob": "^7.1.3" - } - }, - "run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "rxjs": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", - "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", - "requires": { - "tslib": "^2.1.0" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "requires": {} - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - } - } - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "requires": { - "lru-cache": "^6.0.0" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "requires": { - "yallist": "^4.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - } - } - }, - "send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "requires": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - } - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - } - } - }, - "serialize-javascript": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", - "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } - }, - "serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" - }, - "setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" - }, - "shelljs": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", - "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", - "dev": true, - "requires": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - } - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "slice-ansi": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", - "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", - "dev": true, - "requires": { - "ansi-styles": "^6.0.0", - "is-fullwidth-code-point": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", - "dev": true - } - } - }, - "source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true - }, - "source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "dev": true - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - } - } - }, - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" - }, - "streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==" - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } - } - }, - "string-argv": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", - "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", - "dev": true - }, - "string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "requires": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - } - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "superagent": { - "version": "8.0.9", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.0.9.tgz", - "integrity": "sha512-4C7Bh5pyHTvU33KpZgwrNKh/VQnvgtCSqPRfJAUdmrtSYePVzVg4E4OzsrbkhJj9O7SO6Bnv75K/F8XVZT8YHA==", - "dev": true, - "requires": { - "component-emitter": "^1.3.0", - "cookiejar": "^2.1.4", - "debug": "^4.3.4", - "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.0", - "formidable": "^2.1.2", - "methods": "^1.1.2", - "mime": "2.6.0", - "qs": "^6.11.0", - "semver": "^7.3.8" - }, - "dependencies": { - "mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true - } - } - }, - "supertest": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.3.3.tgz", - "integrity": "sha512-EMCG6G8gDu5qEqRQ3JjjPs6+FYT1a7Hv5ApHvtSghmOFJYtsU5S+pSb6Y2EUeCEY3CmEL3mmQ8YWlPOzQomabA==", - "dev": true, - "requires": { - "methods": "^1.1.2", - "superagent": "^8.0.5" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - }, - "supports-hyperlinks": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", - "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", - "dev": true, - "requires": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true - }, - "symbol-observable": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", - "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", - "dev": true - }, - "tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true - }, - "tar": { - "version": "6.1.15", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.15.tgz", - "integrity": "sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==", - "requires": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "dependencies": { - "minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==" - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - } - } - }, - "terminal-link": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" - } - }, - "terser": { - "version": "5.16.8", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.8.tgz", - "integrity": "sha512-QI5g1E/ef7d+PsDifb+a6nnVgC4F22Bg6T0xrBrz6iloVB4PUkkunp6V8nzoOOZJIzjWVdAGqCdlKlhLq/TbIA==", - "dev": true, - "requires": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "dependencies": { - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - } - } - }, - "terser-webpack-plugin": { - "version": "5.3.7", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.7.tgz", - "integrity": "sha512-AfKwIktyP7Cu50xNjXF/6Qb5lBNzYaWpU6YfoX3uZicTx0zTy0stDDCsvjDapKsSDvOeWo5MEq4TmdBy2cNoHw==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.17", - "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.16.5" - }, - "dependencies": { - "jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dev": true, - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - } - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" - }, - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true - }, - "ts-jest": { - "version": "28.0.5", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-28.0.5.tgz", - "integrity": "sha512-Sx9FyP9pCY7pUzQpy4FgRZf2bhHY3za576HMKJFs+OnQ9jS96Du5vNsDKkyedQkik+sEabbKAnCliv9BEsHZgQ==", - "dev": true, - "requires": { - "bs-logger": "0.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "^28.0.0", - "json5": "^2.2.1", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "7.x", - "yargs-parser": "^21.0.1" - } - }, - "ts-loader": { - "version": "9.4.2", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.2.tgz", - "integrity": "sha512-OmlC4WVmFv5I0PpaxYb+qGeGOdm5giHU7HwDDUjw59emP2UYMHy9fFSDcYgSNoH8sXcj4hGCSEhlDZ9ULeDraA==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "enhanced-resolve": "^5.0.0", - "micromatch": "^4.0.0", - "semver": "^7.3.4" - } - }, - "ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, - "requires": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - } - }, - "tsconfig-paths": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.0.0.tgz", - "integrity": "sha512-SLBg2GBKlR6bVtMgJJlud/o3waplKtL7skmLkExomIiaAtLGtVsoXIqP3SYdjbcH9lq/KVv7pMZeCBpLYOit6Q==", - "dev": true, - "requires": { - "json5": "^2.2.1", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - }, - "dependencies": { - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true - } - } - }, - "tsconfig-paths-webpack-plugin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.0.1.tgz", - "integrity": "sha512-m5//KzLoKmqu2MVix+dgLKq70MnFi8YL8sdzQZ6DblmCdfuq/y3OqvJd5vMndg2KEVCOeNz8Es4WVZhYInteLw==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "enhanced-resolve": "^5.7.0", - "tsconfig-paths": "^4.1.2" - }, - "dependencies": { - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true - }, - "tsconfig-paths": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", - "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", - "dev": true, - "requires": { - "json5": "^2.2.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - } - } - }, - "tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" - }, - "typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "dev": true - }, - "uid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.1.tgz", - "integrity": "sha512-PF+1AnZgycpAIEmNtjxGBVmKbZAQguaa4pBUq6KNaGEcpzZ2klCNZLM34tsjp76maN00TttiiUf6zkIBpJQm2A==", - "requires": { - "@lukeed/csprng": "^1.0.0" - } - }, - "uid-safe": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", - "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", - "requires": { - "random-bytes": "~1.0.0" - } - }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" - }, - "update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", - "dev": true, - "requires": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - } - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" - }, - "v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "v8-to-istanbul": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", - "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" - } - }, - "validator": { - "version": "13.9.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.9.0.tgz", - "integrity": "sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==" - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" - }, - "walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "requires": { - "makeerror": "1.0.12" - } - }, - "watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", - "dev": true, - "requires": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - } - }, - "wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dev": true, - "requires": { - "defaults": "^1.0.3" - } - }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "webpack": { - "version": "5.77.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.77.0.tgz", - "integrity": "sha512-sbGNjBr5Ya5ss91yzjeJTLKyfiwo5C628AFjEa6WSXcZa4E+F57om3Cc8xLb1Jh0b243AWuSYRf3dn7HVeFQ9Q==", - "dev": true, - "peer": true, - "requires": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^0.0.51", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.7.6", - "browserslist": "^4.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.10.0", - "es-module-lexer": "^0.9.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.4.0", - "webpack-sources": "^3.2.3" - } - }, - "webpack-node-externals": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz", - "integrity": "sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==", - "dev": true - }, - "webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "dev": true - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "requires": { - "isexe": "^2.0.0" - } - }, - "wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "requires": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "windows-release": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz", - "integrity": "sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==", - "dev": true, - "requires": { - "execa": "^4.0.2" - }, - "dependencies": { - "execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - } - }, - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", - "dev": true - } - } - }, - "word-wrap": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", - "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", - "dev": true - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - } - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true - }, - "yargs": { - "version": "17.7.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", - "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", - "dev": true, - "requires": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - } - }, - "yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true - }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true - } } } diff --git a/package.json b/package.json index ae2d5f28..e137f2ef 100644 --- a/package.json +++ b/package.json @@ -1,29 +1,33 @@ { "name": "otl-nest", - "version": "0.0.1", + "version": "3.3.1", "description": "", "author": "", "private": true, "license": "UNLICENSED", + "engines": { + "node": ">=18.20.4", + "npm": ">=10.7.0" + }, "scripts": { "prebuild": "rimraf dist", - "build": "nest build", - "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", + "build": "nest build --config src/bootstrap/nest-cli.json", + "format": "prettier --write .", "format:check": "prettier --check .", - "start:local": "NODE_ENV=local nest start --watch --config src/bootstrap/nest-cli.json", - "start:dev": "NODE_ENV=dev nest start --config src/bootstrap/nest-cli.json", + "start:local": "cross-env NODE_ENV=local nest start --watch --config src/bootstrap/nest-cli.json", + "start:dev": "cross-env NODE_ENV=dev nest start --config src/bootstrap/nest-cli.json", "start:debug": "nest start --debug --watch", - "start:prod": "node dist/main", - "prisma-push:local": "dotenv -e env/.env.local -- npx prisma db push", - "prisma-push:dev": "dotenv -e env/.env.dev -- npx prisma db push", - "prisma-pull:local": "dotenv -e env/.env.local -- npx prisma db pull --schema src/prisma/schema.prisma", - "prisma-pull:dev": "dotenv -e env/.env.dev -- npx prisma db pull --schema src/prisma/schema.prisma", - "prisma-migrate-create-dev:local": "dotenv -e env/.env.local -- npx prisma migrate dev --schema src/prisma/schema.prisma --create-only ", - "prisma-migrate-dev:local": "dotenv -e env/.env.local -- npx prisma migrate dev --schema src/prisma/schema.prisma", - "prisma-migrate-dev:dev": "dotenv -e env/.env.dev -- npx prisma migrate dev --schema src/prisma/schema.prisma", - "prisma-generate": "npx prisma generate --schema src/prisma/schema.prisma", - "prisma-status:local": "dotenv -e env/.env.local npx prisma migrate status", - "prisma-status:dev": "dotenv -e env/.env.dev npx prisma migrate status", + "start:prod": "cross-env NODE_ENV=prod node dist/src/bootstrap/bootstrap.js", + "db:push": "dotenv -e env/.env.local -- npx prisma db push", + "db:pull": "dotenv -e env/.env.local -- npx prisma db pull --schema src/prisma/schema.prisma", + "db:init": "dotenv -e env/.env.local -- npx prisma db execute --file src/prisma/migrations/0_init/migration.sql --schema src/prisma/schema.prisma && dotenv -e env/.env.local -- npx prisma migrate resolve --applied 0_init", + "db:execute": "dotenv -e env/.env.local -- npx prisma db execute --schema src/prisma/schema.prisma --file", + "migrate:dev-create": "dotenv -e env/.env.local -- npx prisma migrate dev --schema src/prisma/schema.prisma --create-only ", + "migrate:dev": "dotenv -e env/.env.local -- npx prisma migrate dev --schema src/prisma/schema.prisma", + "migrate:deploy": "dotenv -e env/.env.local -- npx prisma migrate deploy --schema src/prisma/schema.prisma", + "migrate:status": "dotenv -e env/.env.local -- npx prisma migrate status", + "migrate:resolve": "dotenv -e env/.env.local -- npx prisma migrate resolve", + "client:generate": "npx prisma generate --schema src/prisma/schema.prisma", "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", "lint:check": "eslint .", "test": "jest", @@ -31,35 +35,46 @@ "test:cov": "jest --coverage", "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", "test:e2e": "jest --config ./test/jest-e2e.json", - "prepare": "husky install" + "prepare": "husky install", + "ts-node": "cross-env NODE_ENV=local node -r ts-node/register -r tsconfig-paths/register " }, "prisma": { "schema": "src/prisma/schema.prisma" }, "dependencies": { + "@nestjs-cls/transactional": "^2.4.1", + "@nestjs-cls/transactional-adapter-prisma": "^1.2.3", "@nestjs/common": "^9.0.0", "@nestjs/core": "^9.0.0", "@nestjs/jwt": "^10.0.3", "@nestjs/passport": "^9.0.3", "@nestjs/platform-express": "^9.0.0", - "@prisma/client": "4.16.0", + "@nestjs/swagger": "^7.1.1", + "@prisma/client": "^5.18.0", "@types/cookie-parser": "^1.4.3", "@types/express-session": "^1.17.7", "@types/morgan": "^1.9.4", "@types/passport-jwt": "^3.0.6", "@types/passport-local": "^1.0.34", + "@types/sharp": "^0.32.0", "axios": "^1.4.0", "bcrypt": "^5.1.0", + "canvas": "^2.11.2", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "cookie-parser": "^1.4.6", - "dotenv": "^16.0.3", + "csurf": "^1.10.0", + "date-fns": "^3.6.0", + "dotenv": "^16.4.5", "dotenv-cli": "^7.2.1", + "enquirer": "^2.4.1", "express-session": "^1.17.3", + "ical-generator": "^7.1.0", + "moment-timezone": "^0.5.45", "morgan": "^1.10.0", + "nestjs-cls": "^4.4.0", "passport": "^0.6.0", "passport-jwt": "^4.0.1", - "prisma": "4.16.0", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", "rxjs": "^7.2.0" @@ -69,25 +84,29 @@ "@nestjs/schematics": "^9.0.0", "@nestjs/testing": "^9.0.0", "@types/bcrypt": "^5.0.0", + "@types/csurf": "^1.11.5", "@types/express": "^4.17.13", + "@types/inquirer": "^9.0.7", "@types/jest": "28.1.4", "@types/node": "^16.18.23", - "@types/supertest": "^2.0.11", + "@types/supertest": "^6.0.2", "@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/parser": "^5.0.0", + "cross-env": "^7.0.3", "eslint": "^8.0.1", "eslint-config-prettier": "^8.3.0", "husky": "^8.0.0", "jest": "28.1.2", "lint-staged": "^13.2.3", "prettier": "^2.3.2", + "prisma": "^5.18.0", "source-map-support": "^0.5.20", - "supertest": "^6.1.3", - "ts-jest": "28.0.5", + "supertest": "^7.0.0", + "ts-jest": "^28.0.5", "ts-loader": "^9.2.3", "ts-node": "^10.9.1", "tsconfig-paths": "4.0.0", - "typescript": "^4.3.5" + "typescript": "^5.0.0" }, "lint-staged": { "*.ts": "eslint --fix", @@ -99,7 +118,12 @@ "json", "ts" ], - "rootDir": "src", + "roots": [ + "" + ], + "modulePaths": [ + "" + ], "testRegex": ".*\\.spec\\.ts$", "transform": { "^.+\\.(t|j)s$": "ts-jest" @@ -107,6 +131,9 @@ "collectCoverageFrom": [ "**/*.(t|j)s" ], + "moduleNameMapper": { + "^@src(.*)$": "/src$1" + }, "coverageDirectory": "../coverage", "testEnvironment": "node" } diff --git a/src/app.controller.ts b/src/app.controller.ts index c585364b..10fe8a54 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -1,6 +1,6 @@ import { Controller, Get } from '@nestjs/common'; import { AppService } from './app.service'; -import { Public } from "./common/decorators/skip-auth.decorator"; +import { Public } from './common/decorators/skip-auth.decorator'; @Controller() export class AppController { diff --git a/src/app.module.ts b/src/app.module.ts index 4778d0af..7b916df9 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,25 +1,95 @@ -import { Module } from "@nestjs/common"; -import { AppController } from "./app.controller"; -import { AppService } from "./app.service"; -import { PrismaModule } from "./prisma/prisma.module"; -import { APP_GUARD } from "@nestjs/core"; -import { JwtCookieGuard } from "./modules/auth/guard/jwt-cookie.guard"; -import { AuthModule } from "./modules/auth/auth.module"; -import { MockAuthGuard } from "./modules/auth/guard/mock-auth-guard"; -import { JwtService } from "@nestjs/jwt"; +import { Module } from '@nestjs/common'; +import { APP_GUARD } from '@nestjs/core'; +import { JwtService } from '@nestjs/jwt'; +import { AppController } from './app.controller'; +import { AppService } from './app.service'; +import { AuthModule } from './modules/auth/auth.module'; +import { JwtCookieGuard } from './modules/auth/guard/jwt-cookie.guard'; +import { MockAuthGuard } from './modules/auth/guard/mock-auth-guard'; +import { CoursesModule } from './modules/courses/courses.module'; +import { DepartmentsModule } from './modules/departments/departments.module'; +import { FeedsModule } from './modules/feeds/feeds.module'; +import { LecturesModule } from './modules/lectures/lectures.module'; +import { NoticesModule } from './modules/notices/notices.module'; +import { PlannersModule } from './modules/planners/planners.module'; +import { RatesModule } from './modules/rates/rates.module'; +import { ReviewsModule } from './modules/reviews/reviews.module'; +import { SemestersModule } from './modules/semesters/semesters.module'; +import { SessionModule } from './modules/session/session.module'; +import { StatusModule } from './modules/status/status.module'; +import { TimetablesModule } from './modules/timetables/timetables.module'; +import { TracksModule } from './modules/tracks/tracks.module'; +import { UserModule } from './modules/user/user.module'; +import { WishlistModule } from './modules/wishlist/wishlist.module'; +import { PrismaModule } from './prisma/prisma.module'; +import { ShareModule } from './modules/share/share.module'; +import { AuthConfig } from './modules/auth/auth.config'; +import { AuthGuard } from './modules/auth/guard/auth.guard'; +import { ClsModule } from 'nestjs-cls'; +import { ClsPluginTransactional } from '@nestjs-cls/transactional'; +import { PrismaService } from '@src/prisma/prisma.service'; +import { TransactionalAdapterPrisma } from '@nestjs-cls/transactional-adapter-prisma'; @Module({ - imports: [PrismaModule, AuthModule], + imports: [ + PrismaModule, + AuthModule, + CoursesModule, + LecturesModule, + ReviewsModule, + UserModule, + SemestersModule, + TimetablesModule, + RatesModule, + StatusModule, + FeedsModule, + WishlistModule, + NoticesModule, + SessionModule, + DepartmentsModule, + PlannersModule, + TracksModule, + ShareModule, + ClsModule.forRoot({ + global: true, + middleware: { mount: true }, + plugins: [ + new ClsPluginTransactional({ + imports: [PrismaModule], + adapter: new TransactionalAdapterPrisma({ + prismaInjectionToken: PrismaService, + }), + }), + ], + }), + ], controllers: [AppController], providers: [ + // { + // provide: APP_GUARD, + // useClass: + // process.env.NODE_ENV === 'production' ? JwtCookieGuard : MockAuthGuard, + // }, { provide: APP_GUARD, - useClass: process.env.NODE_ENV === 'production' ? JwtCookieGuard : MockAuthGuard, + useFactory: async (authConfig: AuthConfig) => { + const env = + process.env.NODE_ENV === undefined ? 'prod' : process.env.NODE_ENV; + const authChain = await authConfig.config(env); + return new AuthGuard(authChain); + }, + inject: [AuthConfig], + }, + { + provide: APP_GUARD, + useFactory: () => { + const env = process.env.NODE_ENV; + }, }, JwtCookieGuard, MockAuthGuard, AppService, JwtService, - ] + ], }) export class AppModule {} diff --git a/src/app.service.ts b/src/app.service.ts index 33c039d7..402bbe47 100644 --- a/src/app.service.ts +++ b/src/app.service.ts @@ -1,17 +1,11 @@ import { Injectable } from '@nestjs/common'; -import { PrismaService } from "./prisma/prisma.service"; +import { PrismaService } from './prisma/prisma.service'; @Injectable() export class AppService { + constructor(private readonly prismaService: PrismaService) {} - constructor( - private readonly prismaService: PrismaService - ) {} - - public async prismaTest() { - - } - + public async prismaTest() {} getHello(): string { return 'Hello World!'; diff --git a/src/bootstrap/bootstrap.ts b/src/bootstrap/bootstrap.ts index cb769cea..9d30e05d 100644 --- a/src/bootstrap/bootstrap.ts +++ b/src/bootstrap/bootstrap.ts @@ -1,34 +1,25 @@ -import { ValidationPipe, VersioningType } from "@nestjs/common"; -import { HttpAdapterHost, NestFactory, Reflector } from "@nestjs/core"; -import { ExpressAdapter } from "@nestjs/platform-express"; -import express from "express"; -import { Server } from "http"; -import { AppModule } from "../app.module"; -// import { AuthGuard, MockAuthGuard } from '../../common/guards/auth.guard' -import morgan = require("morgan"); -import { PrismaService } from "../prisma/prisma.service"; -import cookieParser from "cookie-parser"; -import session from "express-session"; -import { MockAuthGuard } from "../modules/auth/guard/mock-auth-guard"; -import { JwtCookieGuard } from "../modules/auth/guard/jwt-cookie.guard"; - -let cachedServer: Server; +import { ValidationPipe, VersioningType } from '@nestjs/common'; +import { NestFactory } from '@nestjs/core'; +import cookieParser from 'cookie-parser'; +import csrf from 'csurf'; +import session from 'express-session'; +import { AppModule } from '../app.module'; +import settings from '../settings'; +import morgan = require('morgan'); async function bootstrap() { - const {NODE_ENV} = process.env; - const app = await NestFactory.create(AppModule); app.enableVersioning({ - type: VersioningType.URI + type: VersioningType.URI, }); - app.enableCors(); + app.enableCors(settings().getCorsConfig()); app.useGlobalPipes( new ValidationPipe({ whitelist: true, - forbidNonWhitelisted: true, - transform: true - }) + // forbidNonWhitelisted: true, + transform: true, + }), ); app.use( @@ -36,38 +27,52 @@ async function bootstrap() { secret: 'p@ssw0rd', resave: false, saveUninitialized: false, - }) - ) + }), + ); app.use(cookieParser()); - // Logs requests app.use( - morgan(":method :url OS/:req[client-os] Ver/:req[client-api-version]", { - // https://github.com/expressjs/morgan#immediate - immediate: true, - stream: { - write: message => { - console.info(message.trim()); - } - } - }) + '/', + csrf({ + cookie: { key: 'csrftoken' }, + ignoreMethods: [ + 'GET', + 'HEAD', + 'OPTIONS', + 'DELETE', + 'PATCH', + 'PUT', + 'POST', + ], + }), ); + // Logs requests + // app.use( + // morgan(':method :url OS/:req[client-os] Ver/:req[client-api-version]', { + // // https://github.com/expressjs/morgan#immediate + // immediate: true, + // stream: { + // write: (message) => { + // console.info(message.trim()); + // }, + // }, + // }), + // ); // Logs responses app.use( - morgan(":method :url :status :res[content-length] :response-time ms", { + morgan(':method :url :status :res[content-length] :response-time ms', { stream: { - write: message => { + write: (message) => { console.info(message.trim()); - } - } - }) + }, + }, + }), ); - const prismaService = app.get(PrismaService); - await prismaService.enableShutdownHooks(app) - return app.listen(3000); + app.enableShutdownHooks(); + return app.listen(8000); } bootstrap() - .then(() => console.log("Nest Ready")) - .catch(error => console.log(error)); + .then(() => console.log('Nest Ready')) + .catch((error) => console.log(error)); diff --git a/src/bootstrap/nest-cli.json b/src/bootstrap/nest-cli.json index 9f767d20..4ffcb8d8 100644 --- a/src/bootstrap/nest-cli.json +++ b/src/bootstrap/nest-cli.json @@ -2,4 +2,4 @@ "collection": "@nestjs/schematics", "sourceRoot": "src", "entryFile": "src/bootstrap/bootstrap" -} \ No newline at end of file +} diff --git a/src/common/decorators/get-user.decorator.ts b/src/common/decorators/get-user.decorator.ts index eb1b6585..bb9d0f88 100644 --- a/src/common/decorators/get-user.decorator.ts +++ b/src/common/decorators/get-user.decorator.ts @@ -1,5 +1,5 @@ import { createParamDecorator, ExecutionContext } from '@nestjs/common'; -import { session_userprofile } from "@prisma/client"; +import { session_userprofile } from '@prisma/client'; export const GetUser = createParamDecorator( (data, ctx: ExecutionContext): session_userprofile => { diff --git a/src/common/decorators/validators.decorator.ts b/src/common/decorators/validators.decorator.ts new file mode 100644 index 00000000..fe0edd67 --- /dev/null +++ b/src/common/decorators/validators.decorator.ts @@ -0,0 +1,106 @@ +import { + registerDecorator, + ValidationArguments, + ValidatorConstraint, + ValidatorConstraintInterface, +} from 'class-validator'; + +// TODO: Django specific한 구현이었던 것 아닐까 의심됨. prisma에 맞게 수정 필요 +export const _PROHIBITED_FIELD_PATTERN: RegExp[] = [ + /user/, + /profile/, + /owner/, + /writer/, + /__.*__/, + /\?/, +]; + +export function RegexValidator( + regexExps: RegExp[], + validationOptions?: { message?: string }, +) { + // eslint-disable-next-line @typescript-eslint/ban-types + return function (object: Object, propertyName: string) { + registerDecorator({ + name: 'regexValidator', + target: object.constructor, + propertyName: propertyName, + options: validationOptions, + constraints: regexExps, + validator: { + validate(order: string[], args: ValidationArguments): boolean { + return order.every((o) => { + return regexExps.every((p) => p.test(o)); + }); + }, + + defaultMessage(validationArguments?: ValidationArguments): string { + return 'Only alphanumeric characters are allowed'; + }, + }, + }); + }; +} + +export function InverseRegexValidator( + regexExps: RegExp[], + validationOptions?: { message?: string }, +) { + // eslint-disable-next-line @typescript-eslint/ban-types + return function (object: Object, propertyName: string) { + registerDecorator({ + name: 'regexValidator', + target: object.constructor, + propertyName: propertyName, + constraints: regexExps, + options: validationOptions, + validator: { + validate(order: string[], args: ValidationArguments): boolean { + return order.every((o) => { + return regexExps.every((p) => !p.test(o)); + }); + }, + + defaultMessage(validationArguments?: ValidationArguments): string { + return 'Only alphanumeric characters are allowed'; + }, + }, + }); + }; +} + +export function OrderDefaultValidator( + regexExps: RegExp[], + validationOptions?: { message?: string }, +) { + return function (object: object, propertyName: string) { + registerDecorator({ + name: 'OrderDefaultValidator', + target: object.constructor, + propertyName: propertyName, + options: validationOptions, + constraints: regexExps, + validator: { + validate(order: string[], args: ValidationArguments): boolean { + return order.every((o) => { + return regexExps.every((p) => !p.test(o)); + }); + }, + defaultMessage(validationArguments?: ValidationArguments): string { + return 'Only alphanumeric characters are allowed'; + }, + }, + }); + }; +} + +@ValidatorConstraint({ name: 'STRING_STRIP_LENGTH', async: false }) +export class StringStripLength implements ValidatorConstraintInterface { + validate(text: string): boolean { + return text?.trim().length > 0; + } + + defaultMessage(args: ValidationArguments): string { + return "Body 'content' did not pass validator: content must not be empty"; + } +} diff --git a/src/common/entities/ECourse.ts b/src/common/entities/ECourse.ts new file mode 100644 index 00000000..9e5787eb --- /dev/null +++ b/src/common/entities/ECourse.ts @@ -0,0 +1,34 @@ +import { Prisma } from '@prisma/client'; + +export namespace ECourse { + export const Basic = Prisma.validator()({}); + export type Basic = Prisma.subject_courseGetPayload; + + export const Extended = Prisma.validator()({ + include: { + subject_department: true, + subject_course_professors: { include: { professor: true } }, + }, + }); + export type Extended = Prisma.subject_courseGetPayload; + + export const Details = Prisma.validator()({ + include: { + ...Extended.include, + lecture: true, + }, + }); + export type Details = Prisma.subject_courseGetPayload; + + export const DetailWithIsRead = Prisma.validator()( + { + include: { + ...Details.include, + subject_courseuser: true, + }, + }, + ); + export type DetailWithIsRead = Prisma.subject_courseGetPayload< + typeof DetailWithIsRead + >; +} diff --git a/src/common/entities/EDepartment.ts b/src/common/entities/EDepartment.ts new file mode 100644 index 00000000..25d63a79 --- /dev/null +++ b/src/common/entities/EDepartment.ts @@ -0,0 +1,7 @@ +import { Prisma } from '@prisma/client'; + +export namespace EDepartment { + export const Basic = Prisma.validator()({}); + + export type Basic = Prisma.subject_departmentGetPayload; +} diff --git a/src/common/entities/EFeed.ts b/src/common/entities/EFeed.ts new file mode 100644 index 00000000..bc6e71e3 --- /dev/null +++ b/src/common/entities/EFeed.ts @@ -0,0 +1,113 @@ +import { Prisma } from '@prisma/client'; +import { ECourse } from './ECourse'; +import { ELecture } from './ELecture'; +import { EReview } from './EReview'; + +export namespace EFeed { + export const FamousHumanityReviewDetails = + Prisma.validator()({ + include: { + main_famoushumanityreviewdailyfeed_reviews: { + include: { review_review: { include: EReview.Details.include } }, + }, + }, + }); + + export const RankedReviewDetails = + Prisma.validator()({}); + + export const FamousMajorReviewDetails = + Prisma.validator()({ + include: { + subject_department: true, + main_famousmajorreviewdailyfeed_reviews: { + include: { review_review: { include: EReview.Details.include } }, + }, + }, + }); + + export const ReviewWriteDetails = + Prisma.validator()({ + include: { + subject_lecture: { + include: ELecture.Details.include, + }, + }, + }); + + export const RelatedCourseDetails = + Prisma.validator()({ + include: { + subject_course: { + include: ECourse.Details.include, + }, + }, + }); + + export const RateDailyDetails = + Prisma.validator()({}); + + export type FamousHumanityReviewDetails = + Prisma.main_famoushumanityreviewdailyfeedGetPayload< + typeof FamousHumanityReviewDetails + >; + + export type RankedReviewDetails = Prisma.main_rankedreviewdailyfeedGetPayload< + typeof RankedReviewDetails + > & { + reviews: EReview.Details[]; + }; + + export type FamousMajorReviewDetails = + Prisma.main_famousmajorreviewdailyfeedGetPayload< + typeof FamousMajorReviewDetails + >; + + export type ReviewWriteDetails = + Prisma.main_reviewwritedailyuserfeedGetPayload; + + export type RelatedCourseDetails = + Prisma.main_relatedcoursedailyuserfeedGetPayload< + typeof RelatedCourseDetails + >; + + export type RateDailyDetails = Prisma.main_ratedailyuserfeedGetPayload< + typeof RateDailyDetails + >; + + export type Details = + | FamousHumanityReviewDetails + | RankedReviewDetails + | FamousMajorReviewDetails + | ReviewWriteDetails + | RelatedCourseDetails + | RateDailyDetails; + + export const isFamousHumanityReview = ( + feed: Details, + ): feed is FamousHumanityReviewDetails => { + return 'main_famoushumanityreviewdailyfeed_reviews' in feed; + }; + + export const isFamousMajorReview = ( + feed: Details, + ): feed is FamousMajorReviewDetails => { + return 'main_famousmajorreviewdailyfeed_reviews' in feed; + }; + + export const isReviewWrite = (feed: Details): feed is ReviewWriteDetails => { + return 'subject_lecture' in feed; + }; + + export const isRelatedCourse = ( + feed: Details, + ): feed is RelatedCourseDetails => { + return 'subject_course' in feed; + }; + + export const isRankedReview = ( + feed: Details, + ): feed is RankedReviewDetails => { + return 'semester_id' in feed; + }; +} diff --git a/src/common/entities/ELecture.ts b/src/common/entities/ELecture.ts new file mode 100644 index 00000000..170746a3 --- /dev/null +++ b/src/common/entities/ELecture.ts @@ -0,0 +1,59 @@ +import { Prisma } from '@prisma/client'; + +export namespace ELecture { + export const Basic = Prisma.validator()({}); + export type Basic = Prisma.subject_lectureGetPayload; + + export const Extended = Prisma.validator()({ + include: { + subject_department: true, + subject_lecture_professors: { include: { professor: true } }, + }, + }); + export type Extended = Prisma.subject_lectureGetPayload; + + export const WithClasstime = Prisma.validator()({ + include: { + subject_classtime: true, + }, + }); + export type WithClasstime = Prisma.subject_lectureGetPayload< + typeof WithClasstime + >; + + // TODO: usage of UserTaken seems to be equal to WithClasstime. Check if it's necessary. + export const UserTaken = Prisma.validator()({ + include: { + subject_classtime: true, + subject_department: true, + }, + }); + export type UserTaken = Prisma.subject_lectureGetPayload; + + export const Details = Prisma.validator()({ + include: { + subject_department: true, + subject_lecture_professors: { include: { professor: true } }, + subject_classtime: true, + subject_examtime: true, + }, + }); + export type Details = Prisma.subject_lectureGetPayload; + + export const DetailsWithCourse = + Prisma.validator()({ + include: { + ...Details.include, + course: true, + }, + }); + export type DetailsWithCourse = Prisma.subject_lectureGetPayload< + typeof DetailsWithCourse + >; + + export function isDetails( + lecture: ELecture.Extended | ELecture.Details, + ): lecture is ELecture.Details { + return 'subject_classtime' in lecture && 'subject_examtime' in lecture; + } +} diff --git a/src/common/entities/ENotice.ts b/src/common/entities/ENotice.ts new file mode 100644 index 00000000..2639862f --- /dev/null +++ b/src/common/entities/ENotice.ts @@ -0,0 +1,7 @@ +import { Prisma } from '@prisma/client'; + +export namespace ENotice { + export const Basic = Prisma.validator()({}); + + export type Basic = Prisma.support_noticeGetPayload; +} diff --git a/src/common/entities/EPlanners.ts b/src/common/entities/EPlanners.ts new file mode 100644 index 00000000..8dde9680 --- /dev/null +++ b/src/common/entities/EPlanners.ts @@ -0,0 +1,125 @@ +import { Prisma } from '@prisma/client'; +import { ECourse } from './ECourse'; +import { ELecture } from './ELecture'; +import { ETrack } from './ETrack'; + +export namespace EPlanners { + export namespace EItems { + export namespace Future { + export const Basic = + Prisma.validator()({}); + export type Basic = Prisma.planner_futureplanneritemGetPayload< + typeof Basic + >; + + export const Extended = + Prisma.validator()({ + include: { + subject_course: ECourse.Details, + }, + }); + export type Extended = Prisma.planner_futureplanneritemGetPayload< + typeof Extended + >; + + export const Details = + Prisma.validator()({ + include: { + ...Extended.include, + planner_planner: true, + }, + }); + export type Details = Prisma.planner_futureplanneritemGetPayload< + typeof Details + >; + } + + export namespace Taken { + export const Basic = + Prisma.validator()({}); + export type Basic = Prisma.planner_takenplanneritemGetPayload< + typeof Basic + >; + + export const Extended = + Prisma.validator()({ + include: { + subject_lecture: ELecture.Details, + }, + }); + export type Extended = Prisma.planner_takenplanneritemGetPayload< + typeof Extended + >; + + export const Details = + Prisma.validator()({ + include: { + subject_lecture: { + include: { + ...ELecture.Details.include, + course: ECourse.Details, + }, + }, + }, + }); + export type Details = Prisma.planner_takenplanneritemGetPayload< + typeof Details + >; + } + export namespace Arbitrary { + export const CreateInput = + Prisma.validator()( + {}, + ); + export type CreateInput = + Prisma.planner_arbitraryplanneritemUncheckedCreateInput; + + export const Basic = + Prisma.validator()({}); + export type Basic = Prisma.planner_arbitraryplanneritemGetPayload< + typeof Basic + >; + + export const Extended = + Prisma.validator()({ + include: { + subject_department: true, + }, + }); + export type Extended = Prisma.planner_arbitraryplanneritemGetPayload< + typeof Extended + >; + + export const Details = + Prisma.validator()({ + include: { + subject_department: true, + planner_planner: true, + }, + }); + export type Details = Prisma.planner_arbitraryplanneritemGetPayload< + typeof Details + >; + } + } + + export const Basic = Prisma.validator()({}); + export type Basic = Prisma.planner_plannerGetPayload; + + export const Details = Prisma.validator()({ + include: { + planner_planner_additional_tracks: { + include: { + graduation_additionaltrack: ETrack.Additional, + }, + }, + graduation_generaltrack: true, + graduation_majortrack: ETrack.Major, + planner_takenplanneritem: EPlanners.EItems.Taken.Details, + planner_arbitraryplanneritem: EPlanners.EItems.Arbitrary.Extended, + planner_futureplanneritem: EPlanners.EItems.Future.Extended, + }, + }); + + export type Details = Prisma.planner_plannerGetPayload; +} diff --git a/src/common/entities/EReview.ts b/src/common/entities/EReview.ts new file mode 100644 index 00000000..cc234ee6 --- /dev/null +++ b/src/common/entities/EReview.ts @@ -0,0 +1,17 @@ +import { Prisma } from '@prisma/client'; +import { ECourse } from './ECourse'; +import { ELecture } from './ELecture'; + +export namespace EReview { + export const Basic = Prisma.validator()({}); + export type Basic = Prisma.review_reviewGetPayload; + + export const Details = Prisma.validator()({ + include: { + course: ECourse.Details, + lecture: ELecture.Details, + review_reviewvote: true, + }, + }); + export type Details = Prisma.review_reviewGetPayload; +} diff --git a/src/common/entities/ESSOUser.ts b/src/common/entities/ESSOUser.ts new file mode 100644 index 00000000..bcdaf66d --- /dev/null +++ b/src/common/entities/ESSOUser.ts @@ -0,0 +1,82 @@ +export namespace ESSOUser { + export class KaistInfo { + ku_std_no!: string; + + kaist_uid!: string; + + ku_psft_user_status_kor!: string; + + employeeType!: string; + + ku_person_type!: string; + + ku_kaist_org_id!: string; + + ku_psft_user_status!: string; + + ku_acad_prog_code!: string; + + ku_born_date!: string; + + ku_person_type_kor!: string; + + sn!: string; + + mail!: string; + + displayname!: string; + + givenname!: string; + + ku_sex!: string; + + ku_kname!: string; + } + + export class SSOUser { + /** 사용자가 SSO 전체에서 고유하게 받은 ID입니다. 30자를 초과하지 않습니다. */ + uid!: string; + + /** RP에서 사용자를 식별할 수 있는 고유한 값입니다. */ + sid!: string; + + /** 사용자의 이름입니다. 30자를 초과하지 않습니다. */ + first_name!: string; + + /** 사용자의 성입니다. 30자를 초과하지 않습니다. */ + last_name!: string; + + /** 사용자의 이메일 주소입니다. 인증된 이메일인지는 보장하지 않으며, 254자를 초과하지 않습니다. */ + email!: string; + + /** 사용자의 성별입니다. *M (남성), *F (여성), *H (숨김), *E (기타) 또는 30자를 초과하지 않는 성별을 나타내는 문자열입니다. */ + gender!: string; + + /** 사용자의 생일입니다. YYYY-MM-DD 형식의 날짜 또는 빈 문자열 값입니다. */ + birthday?: Date; + + /** 사용자의 포인트를 나타냅니다. 개발 전용 서비스와 실제 포인트 값은 분리되어 적용됩니다. */ + point!: number; + + /** 사용자가 특별한 권한이 있는 경우 이를 표시합니다. 사용자가 테스트 기능이 있는 경우 TEST 문자열이, SPARCS 회원인 경우 SPARCS 문자열이 포함되어 있습니다. */ + flags!: string[]; + + /** 사용자의 페이스북 고유 ID입니다. 연동하지 않았을 경우 빈 문자열입니다. */ + facebook_id!: string; + + /** 사용자의 트위터 고유 ID입니다. 연동하지 않았을 경우 빈 문자열입니다. */ + twitter_id!: string; + + /** 사용자의 KAIST 고유 ID입니다. 연동하지 않았을 경우 빈 문자열입니다. */ + kaist_id!: string; + + /** 사용자의 KAIST Portal 데이터입니다.*/ + kaist_info!: KaistInfo; + + /** kaist_info를 업데이트한 날짜입니다. YYYY-MM-DD 형식의 날짜 또는 빈 문자열 값입니다. */ + kaist_info_time?: Date; + + /** 사용자의 SPARCS ID입니다. SPARCS 회원이 아닌 경우 빈 문자열 값입니다. */ + sparcs_id!: string; + } +} diff --git a/src/common/entities/ESemester.ts b/src/common/entities/ESemester.ts new file mode 100644 index 00000000..de12d09d --- /dev/null +++ b/src/common/entities/ESemester.ts @@ -0,0 +1,6 @@ +import { Prisma } from '@prisma/client'; + +export namespace ESemester { + export const Basic = Prisma.validator()({}); + export type Basic = Prisma.subject_semesterGetPayload; +} diff --git a/src/common/entities/ETimetable.ts b/src/common/entities/ETimetable.ts new file mode 100644 index 00000000..91237fcc --- /dev/null +++ b/src/common/entities/ETimetable.ts @@ -0,0 +1,28 @@ +import { Prisma } from '@prisma/client'; +import { ELecture } from './ELecture'; + +export namespace ETimetable { + export type Basic = Prisma.timetable_timetableGetPayload; + + export const Details = Prisma.validator()({ + include: { + timetable_timetable_lectures: { + include: { + subject_lecture: ELecture.Details, + }, + }, + }, + }); + + export type Details = Prisma.timetable_timetableGetPayload; + + export const WithLectureClasstimes = + Prisma.validator()({ + include: { + subject_lecture: ELecture.WithClasstime, + }, + }); + + export type WithLectureClasstimes = + Prisma.timetable_timetable_lecturesGetPayload; +} diff --git a/src/common/entities/ETrack.ts b/src/common/entities/ETrack.ts new file mode 100644 index 00000000..c3008ef1 --- /dev/null +++ b/src/common/entities/ETrack.ts @@ -0,0 +1,24 @@ +import { Prisma } from '@prisma/client'; + +export namespace ETrack { + export const Major = Prisma.validator()({ + include: { + subject_department: true, + }, + }); + + export type Major = Prisma.graduation_majortrackGetPayload; + + export const Additional = + Prisma.validator()({ + include: { + subject_department: true, + }, + }); + + export type Additional = Prisma.graduation_additionaltrackGetPayload< + typeof Additional + >; + + export type General = Prisma.graduation_generaltrackGetPayload; +} diff --git a/src/common/entities/EWishlist.ts b/src/common/entities/EWishlist.ts new file mode 100644 index 00000000..ded1f33a --- /dev/null +++ b/src/common/entities/EWishlist.ts @@ -0,0 +1,23 @@ +import { Prisma } from '@prisma/client'; +import { ELecture } from './ELecture'; + +export namespace EWishlist { + export const WithLectures = Prisma.validator()( + { + include: { + timetable_wishlist_lectures: { + include: { + subject_lecture: { + include: ELecture.Details.include, + }, + }, + where: { subject_lecture: { deleted: false } }, + }, + }, + }, + ); + + export type WithLectures = Prisma.timetable_wishlistGetPayload< + typeof WithLectures + >; +} diff --git a/src/common/interfaces/IAuth.ts b/src/common/interfaces/IAuth.ts new file mode 100644 index 00000000..21a0df20 --- /dev/null +++ b/src/common/interfaces/IAuth.ts @@ -0,0 +1,19 @@ +import { Request as _Request, Response as _Response } from 'express'; + +export namespace IAuth { + export type Request = _Request & RequestExtra; + + export interface RequestExtra { + session: { + next: string; + + sso_state: string; + }; + } + + export interface Response extends _Response {} + + export interface JwtPayload { + sid: string; + } +} diff --git a/src/common/interfaces/ICourse.ts b/src/common/interfaces/ICourse.ts new file mode 100644 index 00000000..c9d453de --- /dev/null +++ b/src/common/interfaces/ICourse.ts @@ -0,0 +1,155 @@ +import { IDepartment } from './IDepartment'; +import { IProfessor } from './IProfessor'; + +import { Transform } from 'class-transformer'; +import { + IsArray, + IsNumber, + IsOptional, + IsString, + Max, + Min, +} from 'class-validator'; + +export namespace ICourse { + export interface Basic { + id: number; + old_code: string; + department: IDepartment.Basic; + type: string; + type_en: string; + title: string; + title_en: string; + summary: string; + review_total_weight: number; + credit: number; + credit_au: number; + num_classes: number; + num_labs: number; + } + + export interface Detail extends Basic { + related_courses_prior: Basic[]; + related_courses_posterior: Basic[]; + professors: IProfessor.Basic[]; + grade: number; + load: number; + speech: number; + } + + export interface DetailWithIsRead extends Detail { + userspecific_is_read: boolean; + } + + export interface FeedBasic { + id: number; + old_code: string; + department_id: number; + type: string; + type_en: string; + title: string; + title_en: string; + summary: string; + grade_sum: number; + load_sum: number; + speech_sum: number; + review_total_weight: number; + grade: number; + load: number; + speech: number; + title_en_no_space: string; + title_no_space: string; + } + + export interface FeedRelated extends FeedBasic { + related_courses_prior: FeedBasic[]; + related_courses_posterior: FeedBasic[]; + } + + export class AutocompleteQueryDto { + @IsString() + keyword!: string; + } + + export class Query { + @IsOptional() + @Transform(({ value }) => (typeof value === 'string' ? [value] : value)) + @IsArray() + @IsString({ each: true }) + department?: string[]; + + @IsOptional() + @Transform(({ value }) => (typeof value === 'string' ? [value] : value)) + @IsArray() + @IsString({ each: true }) + type?: string[]; + + @IsOptional() + @Transform(({ value }) => (typeof value === 'string' ? [value] : value)) + @IsArray() + //@todo: @Transform() + @IsString({ each: true }) + level?: string[]; + + @IsOptional() + @Transform(({ value }) => (typeof value === 'string' ? [value] : value)) + @IsArray() + @IsString({ each: true }) + group?: string[]; + + @IsOptional() + @IsString() + keyword?: string; + + @IsOptional() + @Transform(({ value }) => (typeof value === 'string' ? [value] : value)) + @IsArray() + @IsString({ each: true }) + term?: string[]; + + @IsOptional() + @Transform(({ value }) => (typeof value === 'string' ? [value] : value)) + @IsArray() + @IsString({ each: true }) + order?: string[]; + + @IsOptional() + @IsNumber() + @Transform(({ value }) => parseInt(value)) + offset?: number; + + @IsOptional() + @IsNumber() + @Transform(({ value }) => parseInt(value)) + limit?: number; + } + + export class LectureQueryDto { + @IsOptional() + @Transform(({ value }) => (typeof value === 'string' ? [value] : value)) + @IsArray() + @IsString({ each: true }) + order?: string[]; + } + + export class ReviewQueryDto { + @IsOptional() + @Transform(({ value }) => (typeof value === 'string' ? [value] : value)) + @IsArray() + @IsString({ each: true }) + order?: string[]; + + @IsOptional() + @IsNumber() + @Transform(({ value }) => parseInt(value)) + @Min(0, { message: 'Offset must be a non-negative number' }) + offset?: number; + + @IsOptional() + @IsNumber() + @Transform(({ value }) => parseInt(value)) + @Min(0, { message: 'limit must be a non-negative number' }) + @Max(100, { message: 'limit must be less than or equal to 100' }) + limit?: number; + } +} diff --git a/src/common/interfaces/IDepartment.ts b/src/common/interfaces/IDepartment.ts new file mode 100644 index 00000000..e9d23cc5 --- /dev/null +++ b/src/common/interfaces/IDepartment.ts @@ -0,0 +1,8 @@ +export namespace IDepartment { + export interface Basic { + id: number; + name: string; + name_en: string; + code: string; + } +} diff --git a/src/common/interfaces/IFeed.ts b/src/common/interfaces/IFeed.ts new file mode 100644 index 00000000..1ff5cabf --- /dev/null +++ b/src/common/interfaces/IFeed.ts @@ -0,0 +1,47 @@ +import { IsDateString } from 'class-validator'; +import { ICourse } from './ICourse'; +import { IDepartment } from './IDepartment'; +import { ILecture } from './ILecture'; +import { IReview } from './IReview'; + +export namespace IFeed { + export interface Basic { + type: string; + date: Date; + priority: number; + } + + export interface FamousHumanityReview extends Basic { + reviews: IReview.Basic[]; + } + + export interface FamousMajorReview extends Basic { + reviews: IReview.Basic[]; + department: IDepartment.Basic; + } + + export interface ReviewWrite extends Basic { + lecture: ILecture.Basic; + } + + export interface RelatedCourse extends Basic { + course: ICourse.FeedRelated; + } + + export interface RankedReview extends Basic { + reviews: IReview.Basic[]; + } + + export type Details = + | Basic + | FamousHumanityReview + | FamousMajorReview + | ReviewWrite + | RelatedCourse + | RankedReview; + + export class QueryDto { + @IsDateString() + date!: string; + } +} diff --git a/src/common/interfaces/ILecture.ts b/src/common/interfaces/ILecture.ts new file mode 100644 index 00000000..5c27cdbe --- /dev/null +++ b/src/common/interfaces/ILecture.ts @@ -0,0 +1,159 @@ +import { Type } from 'class-transformer'; +import { IsInt, IsString } from 'class-validator'; +import { ICourse } from './ICourse'; +import { IProfessor } from './IProfessor'; + +import { Transform } from 'class-transformer'; +import { IsArray, IsNumber, IsOptional } from 'class-validator'; +import { ITimetable } from './ITimetable'; + +export namespace ILecture { + export interface Classtime { + building_code: string; + room_name: string; + classroom: string; + classroom_en: string; + classroom_short: string; + classroom_short_en: string; + day: number; + begin: number; + end: number; + } + + export interface ExamTime { + day: number; + str: string; + str_en: string; + begin: number; + end: number; + } + + export interface Raw { + id: number; + code: string; + old_code: string; + year: number; + semester: number; + department_id: number; + class_no: string; + title: string; + title_en: string; + type: string; + type_en: string; + audience: number; + credit: number; + title_en_no_space: string; + title_no_space: string; + num_classes: number; + num_labs: number; + credit_au: number; + limit: number; + num_people: number | null; // Allow num_people to be null + is_english: boolean; + deleted: boolean; + course_id: number; + grade_sum: number; + load_sum: number; + speech_sum: number; + grade: number; + load: number; + speech: number; + review_total_weight: number; + class_title: string | null; + class_title_en: string | null; + common_title: string | null; + common_title_en: string | null; + subject_classtime: ITimetable.IClasstime[]; + // professor_names: string[] | null; // 교수 이름 목록 + // professor_names_en: string[] | null; // 교수 영문 이름 목록 + // classroom_str: string | null; // 강의실 문자열 + // classroom_str_en: string | null; // 강의실 영문 문자열 + // classroom_short: string | null; // 강의실 축약 문자열 + // classroom_short_en: string | null; // 강의실 영문 축약 문자열 + // Additional properties as needed + } + + export interface Basic { + id: number; + title: string; + title_en: string; + course: number; + old_code: string; + class_no: string; + year: number; + semester: number; + code: string; + department: number; + department_code: string; + department_name: string; + department_name_en: string; + type: string; + type_en: string; + limit: number; + num_people: number; + is_english: boolean; + num_classes: number; + num_labs: number; + credit: number; + credit_au: number; + common_title: string; + common_title_en: string; + class_title: string; + class_title_en: string; + review_total_weight: number; + professors: IProfessor.Basic[]; + } + + export interface Detail extends Basic { + grade: number; + load: number; + speech: number; + classtimes: Classtime[]; + examtimes: ExamTime[]; + } + + export class QueryDto extends ICourse.Query { + @IsOptional() + // @Transform(({ value }) => (typeof value === 'string' ? [value] : value)) + @Transform(({ value }) => parseInt(value)) + @IsNumber() + year?: number; + + @IsOptional() + // @Transform(({ value }) => (typeof value === 'string' ? [value] : value)) + @Transform(({ value }) => parseInt(value)) + @IsNumber() + semester?: number; + + @IsOptional() + // @Transform(({ value }) => (typeof value === 'string' ? [value] : value)) + @Transform(({ value }) => parseInt(value)) + @IsNumber() + day?: number; + + @IsOptional() + // @Transform(({ value }) => (typeof value === 'string' ? [value] : value)) + @Transform(({ value }) => parseInt(value)) + @IsNumber() + begin?: number; + + @IsOptional() + // @Transform(({ value }) => (typeof value === 'string' ? [value] : value)) + @Transform(({ value }) => parseInt(value)) + @IsNumber() + end?: number; + } + + export class AutocompleteQueryDto { + @IsInt() + @Type(() => Number) + year!: number; + + @IsInt() + @Type(() => Number) + semester!: number; + + @IsString() + keyword!: string; + } +} diff --git a/src/common/interfaces/INotice.ts b/src/common/interfaces/INotice.ts new file mode 100644 index 00000000..55ff5b3d --- /dev/null +++ b/src/common/interfaces/INotice.ts @@ -0,0 +1,18 @@ +export namespace INotice { + export interface Basic { + title: string; + content: string; + start_time: Date; + end_time: Date; + } + + /** + * @deprecated + */ + export class GetDto { + // @IsString() + // time?: string; + // @IsString() + // order?: string; + } +} diff --git a/src/common/interfaces/IPlanner.ts b/src/common/interfaces/IPlanner.ts new file mode 100644 index 00000000..df643a2e --- /dev/null +++ b/src/common/interfaces/IPlanner.ts @@ -0,0 +1,264 @@ +import { Transform, Type } from 'class-transformer'; +import { + IsArray, + IsBoolean, + IsEnum, + IsIn, + IsInt, + IsNumber, + IsOptional, + IsString, +} from 'class-validator'; +import { + OrderDefaultValidator, + _PROHIBITED_FIELD_PATTERN, +} from '../decorators/validators.decorator'; +import { ICourse } from './ICourse'; +import { IDepartment } from './IDepartment'; +import { ILecture } from './ILecture'; +import { AdditionalTrackType } from './constants/additional.track.response.dto'; +import { PlannerItemType, PlannerItemTypeEnum } from './constants/planner'; + +export namespace IPlanner { + export namespace ITrack { + export interface Additional { + id: number; + start_year: number; + end_year: number; + type: AdditionalTrackType; + department: IDepartment.Basic | null; + major_required: number; + major_elective: number; + } + + export interface General { + id: number; + start_year: number; + end_year: number; + is_foreign: boolean; + total_credit: number; + total_au: number; + basic_required: number; + basic_elective: number; + thesis_study: number; + thesis_study_doublemajor: number; + general_required_credit: number; + general_required_au: number; + humanities: number; + humanities_doublemajor: number; + } + + export interface Major { + id: number; + start_year: number; + end_year: number; + department: IDepartment.Basic; + basic_elective_doublemajor: number; + major_required: number; + major_elective: number; + } + } + + export namespace IItem { + export type IMutate = + IT extends PlannerItemTypeEnum.Taken + ? IPlanner.IItem.Taken + : IT extends PlannerItemTypeEnum.Future + ? IPlanner.IItem.Future + : IT extends PlannerItemTypeEnum.Arbitrary + ? IPlanner.IItem.Arbitrary + : + | IPlanner.IItem.Taken + | IPlanner.IItem.Future + | IPlanner.IItem.Arbitrary; + + export interface Basic { + id: number; + item_type: PlannerItemType; + is_excluded: boolean; + } + + export interface Future extends Basic { + item_type: 'FUTURE'; + year: number; + semester: number; + course: ICourse.Basic; + } + + export interface Taken extends Basic { + item_type: 'TAKEN'; + lecture: ILecture.Basic; + course: ICourse.Basic; + } + + export interface Arbitrary extends Basic { + item_type: 'ARBITRARY'; + year: number; + semester: number; + department: IDepartment.Basic | null; + type: string; + type_en: string; + credit: number; + credit_au: number; + } + } + + export interface Detail { + id: number; + start_year: number; + end_year: number; + general_track: ITrack.General; + major_track: ITrack.Major; + additional_tracks: ITrack.Additional[]; + taken_items: IItem.Taken[]; + future_items: IItem.Future[]; + arbitrary_items: IItem.Arbitrary[]; + arrange_order: number; + } + + export class QueryDto { + @IsOptional() + @Transform(({ value }) => (typeof value === 'string' ? [value] : value)) + @IsArray() + @IsString({ each: true }) + @OrderDefaultValidator(_PROHIBITED_FIELD_PATTERN) + order?: string[]; + + @IsOptional() + @IsNumber() + @Type(() => Number) + offset?: number; + + @IsOptional() + @IsNumber() + @Type(() => Number) + limit?: number; + } + + export class CreateBodyDto { + @IsInt() + start_year!: number; + @IsInt() + end_year!: number; + @IsInt() + general_track!: number; + @IsInt() + major_track!: number; + @IsOptional() + @IsArray() + @IsInt({ each: true }) + additional_tracks?: number[]; + @IsOptional() + @IsBoolean() + should_update_taken_semesters?: boolean; + @IsArray() + @IsInt({ each: true }) + taken_items_to_copy!: number[]; + @IsArray() + @IsInt({ each: true }) + future_items_to_copy!: number[]; + @IsArray() + @IsInt({ each: true }) + arbitrary_items_to_copy!: number[]; + } + + export class RemoveItemBodyDto { + @IsInt() + item!: number; + @IsEnum(['TAKEN', 'FUTURE', 'ARBITRARY']) + item_type!: 'TAKEN' | 'FUTURE' | 'ARBITRARY'; + } + + export class ReorderBodyDto { + @IsInt() + arrange_order!: number; + } + + export class UpdateItemBodyDto { + @IsInt() + item!: number; + + @IsEnum(PlannerItemType) + item_type!: PlannerItemType; + + @IsInt() + @IsOptional() + semester?: number; + + @IsBoolean() + @IsOptional() + is_excluded?: boolean; + } + + export class FuturePlannerItemDto { + @IsInt() + @Type(() => Number) + course!: number; + + @IsInt() + @Type(() => Number) + year!: number; + + @IsIn([1, 2, 3, 4]) + @Type(() => Number) + semester!: number; + } + + export class AddArbitraryItemDto { + // year, semester, department, type, type_en, credit, credit_au + + @IsInt() + @Type(() => Number) + year!: number; + + @IsIn([1, 2, 3, 4]) + @Type(() => Number) + semester!: 1 | 2 | 3 | 4; + + @IsInt() + @Type(() => Number) + department!: number; + + @IsString() + type!: string; + + @IsString() + type_en!: string; + + @IsInt() + @Type(() => Number) + credit!: number; + + @IsInt() + @Type(() => Number) + credit_au!: number; + } + + export class UpdateBodyDto { + // @Todo start_year cannot be greater than end_year, also start_year cannot be greater than now + @IsInt() + @Type(() => Number) + start_year!: number; + + @IsInt() + @Type(() => Number) + end_year!: number; + + @IsInt() + @Type(() => Number) + general_track!: number; + + @IsInt() + @Type(() => Number) + major_track!: number; + + @Transform(({ value }) => (typeof value === 'number' ? [value] : value)) + @IsArray() + @IsInt({ each: true }) + additional_tracks!: number[]; + + @IsOptional() + @IsBoolean() + should_update_taken_semesters?: boolean; + } +} diff --git a/src/common/interfaces/IPrismaMiddleware.ts b/src/common/interfaces/IPrismaMiddleware.ts new file mode 100644 index 00000000..bd99f8e6 --- /dev/null +++ b/src/common/interfaces/IPrismaMiddleware.ts @@ -0,0 +1,27 @@ +export namespace IPrismaMiddleware { + export type operationType = + | 'findUnique' + | 'findUniqueOrThrow' + | 'findFirst' + | 'findFirstOrThrow' + | 'findMany' + | 'create' + | 'createMany' + | 'update' + | 'updateMany' + | 'upsert' + | 'delete' + | 'deleteMany' + | 'groupBy' + | 'count' + | 'aggregate'; + export interface IPrismaMiddleware { + preExecute: (operations: operationType, args: any) => Promise; + + postExecute: ( + operations: operationType, + args: any, + result: any, + ) => Promise; + } +} diff --git a/src/common/interfaces/IProfessor.ts b/src/common/interfaces/IProfessor.ts new file mode 100644 index 00000000..7f34b934 --- /dev/null +++ b/src/common/interfaces/IProfessor.ts @@ -0,0 +1,8 @@ +export namespace IProfessor { + export interface Basic { + name: string; + name_en: string; + professor_id: number; + review_total_weight: number; + } +} diff --git a/src/common/interfaces/IRate.ts b/src/common/interfaces/IRate.ts new file mode 100644 index 00000000..0994e93e --- /dev/null +++ b/src/common/interfaces/IRate.ts @@ -0,0 +1,20 @@ +import { Type } from 'class-transformer'; +import { IsNumber, Max, Min } from 'class-validator'; + +export namespace IRate { + export interface Basic { + id: number; + user_id: number; + score: number; + version: string; + created_datetime: Date | null; + } + + export class CreateDto { + @IsNumber() + @Max(5) + @Min(1) + @Type(() => Number) + score!: number; + } +} diff --git a/src/common/interfaces/IReview.ts b/src/common/interfaces/IReview.ts new file mode 100644 index 00000000..14511459 --- /dev/null +++ b/src/common/interfaces/IReview.ts @@ -0,0 +1,178 @@ +import { ApiProperty, OmitType, PartialType } from '@nestjs/swagger'; +import { Transform, Type } from 'class-transformer'; +import { + IsArray, + IsInt, + IsNotEmpty, + IsNumber, + IsOptional, + IsString, + Max, + Min, + Validate, +} from 'class-validator'; +import { + OrderDefaultValidator, + StringStripLength, + _PROHIBITED_FIELD_PATTERN, +} from '../decorators/validators.decorator'; +import { ICourse } from './ICourse'; +import { ILecture } from './ILecture'; + +export namespace IReview { + export interface Basic { + id: number; + + course: ICourse.Basic; + + lecture: ILecture.Basic; + + content: string; + + like: number; + + is_deleted: number; + + grade: number; + + load: number; + + speech: number; + + userspecific_is_liked: boolean; + } + + export interface FeedBasic { + id: number; + + course: ICourse.FeedBasic; + + lecture: ILecture.Detail; + + content: string; + + like: number; + + is_deleted: number; + + grade: number; + + load: number; + + speech: number; + + userspecific_is_liked: boolean; + } + + export interface reCalcScoreReturn { + reviewNum: number; + totalWeight: number; + sums: { + gradeSum: number; + loadSum: number; + speechSum: number; + }; + avgs: { + grade: number; + load: number; + speech: number; + }; + } + + export class LectureReviewsQueryDto { + @IsOptional() + @Transform(({ value }) => (typeof value === 'string' ? [value] : value)) + @IsArray() + @IsString({ each: true }) + @OrderDefaultValidator(_PROHIBITED_FIELD_PATTERN) + order?: string[]; + + @IsOptional() + @IsInt() + @Min(0) + @Type(() => Number) + offset?: number; + + @IsOptional() + @IsInt() + @Min(0) + @Max(100) + @Type(() => Number) + limit?: number; + } + + export class QueryDto { + @IsOptional() + @IsNumber() + @Type(() => Number) + lecture_year?: number; + + @IsOptional() + @IsNumber() + @Type(() => Number) + lecture_semester?: number; + + @IsOptional() + @IsString() + response_type?: string; + + @IsOptional() + @Transform(({ value }) => (typeof value === 'string' ? [value] : value)) + @IsArray() + @IsString({ each: true }) + @OrderDefaultValidator(_PROHIBITED_FIELD_PATTERN) + order?: string[]; + + @IsOptional() + @IsNumber() + @Type(() => Number) + offset?: number; + + @IsOptional() + @IsNumber() + @Type(() => Number) + limit?: number; + } + + export class CreateDto { + @ApiProperty() + @IsString() + @IsNotEmpty() + @Validate(StringStripLength) + content!: string; + + @ApiProperty() + @IsNumber() + @IsNotEmpty() + @Type(() => Number) + lecture!: number; + + @ApiProperty() + @IsNumber() + @IsNotEmpty() + @Min(1) + @Max(5) + @Type(() => Number) + grade!: number; + + @ApiProperty() + @IsNumber() + @IsNotEmpty() + @Min(1) + @Max(5) + @Type(() => Number) + load!: number; + + @ApiProperty() + @IsNumber() + @IsNotEmpty() + @Min(1) + @Max(5) + @Type(() => Number) + speech!: number; + } + + export class UpdateDto extends PartialType( + OmitType(IReview.CreateDto, ['lecture']), + ) {} +} diff --git a/src/common/interfaces/ISemester.ts b/src/common/interfaces/ISemester.ts new file mode 100644 index 00000000..db24ebff --- /dev/null +++ b/src/common/interfaces/ISemester.ts @@ -0,0 +1,17 @@ +import { IsArray, IsOptional } from 'class-validator'; +import { + OrderDefaultValidator, + _PROHIBITED_FIELD_PATTERN, +} from '../decorators/validators.decorator'; +import { ESemester } from '../entities/ESemester'; + +export namespace ISemester { + export class QueryDto { + @IsOptional() + @IsArray() + @OrderDefaultValidator(_PROHIBITED_FIELD_PATTERN) + order?: string[]; + } + + export type Response = Omit; +} diff --git a/src/common/interfaces/ISession.ts b/src/common/interfaces/ISession.ts new file mode 100644 index 00000000..b44a7d9c --- /dev/null +++ b/src/common/interfaces/ISession.ts @@ -0,0 +1,8 @@ +import { IsString } from 'class-validator'; + +export namespace ISession { + export class FavoriteDepartmentsDto { + @IsString({ each: true }) + fav_department!: string[]; + } +} diff --git a/src/common/interfaces/IShare.ts b/src/common/interfaces/IShare.ts new file mode 100644 index 00000000..18c2e646 --- /dev/null +++ b/src/common/interfaces/IShare.ts @@ -0,0 +1,85 @@ +import { CanvasRenderingContext2D } from 'canvas'; +import { Type } from 'class-transformer'; +import { IsNumber, IsOptional, IsString } from 'class-validator'; +import { ELecture } from '../entities/ELecture'; + +export namespace IShare { + export interface RoundedRectangleOptions { + ctx: CanvasRenderingContext2D; + x: number; + y: number; + width: number; + height: number; + radius: number; + color: string; + } + + export interface TextOptions { + ctx: CanvasRenderingContext2D; + x: number; + y: number; + text: string; + font: string; + fontSize: number; + color: string; + align?: 'right' | 'left' | 'center'; // Optional parameter + } + + export interface DrawTileOptions { + ctx: CanvasRenderingContext2D; + x: number; + y: number; + width: number; + height: number; + title: string; + professor: string; + location: string; + font: string; + fontSize: number; + } + + export interface drawTimetableDatas { + lectures: ELecture.UserTaken[]; + timetableType: string; + semesterName: string; + isEnglish: boolean; + semesterFontSize: number; + tileFontSize: number; + } + + export class TimetableImageQueryDto { + @Type(() => Number) + @IsNumber() + timetable!: number; + + @Type(() => Number) + @IsNumber() + year!: number; + + @Type(() => Number) + @IsNumber() + semester!: number; + + @IsString() + @IsOptional() + language?: string; + } + + export class TimetableIcalQueryDto { + @Type(() => Number) + @IsNumber() + timetable!: number; + + @Type(() => Number) + @IsNumber() + year!: number; + + @Type(() => Number) + @IsNumber() + semester!: number; + + @IsString() + @IsOptional() + language?: string; + } +} diff --git a/src/common/interfaces/ITimetable.ts b/src/common/interfaces/ITimetable.ts new file mode 100644 index 00000000..dcfcef1c --- /dev/null +++ b/src/common/interfaces/ITimetable.ts @@ -0,0 +1,84 @@ +import { Transform, Type } from 'class-transformer'; +import { IsArray, IsNumber, IsOptional, IsString } from 'class-validator'; +import { + OrderDefaultValidator, + _PROHIBITED_FIELD_PATTERN, +} from '../decorators/validators.decorator'; +import { ILecture } from './ILecture'; + +export const TIMETABLE_MAX_LIMIT = 50; + +export namespace ITimetable { + export interface IClasstime { + id: number; + day: number; + begin: Date; + end: Date; + type: string; + building_id: string | null; + building_full_name: string | null; + building_full_name_en: string | null; + room_name: string | null; + unit_time: number | null; + lecture_id: number | null; + } + + export interface Response { + id: number; + lectures: ILecture.Detail[]; + arrange_order: number; + } + + export class QueryDto { + @IsOptional() + @IsNumber() + @Type(() => Number) + year?: number; + + @IsOptional() + @IsNumber() + @Type(() => Number) + semester?: number; + + @OrderDefaultValidator(_PROHIBITED_FIELD_PATTERN) + @IsOptional() + @Transform(({ value }) => (typeof value === 'string' ? [value] : value)) + @IsArray() + @IsString({ each: true }) + order?: string[]; + + @IsOptional() + @Transform(({ value }) => value ?? 0) + @IsNumber() + offset?: number; + + @IsOptional() + @Transform(({ value }) => value ?? TIMETABLE_MAX_LIMIT) + @IsNumber() + limit?: number; + } + + export class CreateDto { + @IsNumber() + year!: number; + + @IsNumber() + semester!: number; + + @IsArray() + @IsNumber({}, { each: true }) + lectures!: number[]; + } + + export class AddLectureDto { + @IsNumber() + @Type(() => Number) + lecture!: number; + } + + export class ReorderTimetableDto { + @IsNumber() + @Type(() => Number) + arrange_order!: number; + } +} diff --git a/src/common/interfaces/IUser.ts b/src/common/interfaces/IUser.ts new file mode 100644 index 00000000..6dbb2318 --- /dev/null +++ b/src/common/interfaces/IUser.ts @@ -0,0 +1,54 @@ +import { Transform, Type } from 'class-transformer'; +import { IsArray, IsNumber, IsOptional, IsString } from 'class-validator'; +import { + OrderDefaultValidator, + _PROHIBITED_FIELD_PATTERN, +} from 'src/common/decorators/validators.decorator'; +import { IDepartment } from './IDepartment'; +import { ILecture } from './ILecture'; +import { IReview } from './IReview'; + +export namespace IUser { + export class TakenCoursesQueryDto { + @IsOptional() + @Transform(({ value }) => (typeof value === 'string' ? [value] : value)) + @IsArray() + @IsString({ each: true }) + @OrderDefaultValidator(_PROHIBITED_FIELD_PATTERN) + order?: string[]; + } + + export class ReviewLikedQueryDto { + @IsOptional() + @Transform(({ value }) => (typeof value === 'string' ? [value] : value)) + @IsArray() + @IsString({ each: true }) + @OrderDefaultValidator(_PROHIBITED_FIELD_PATTERN) + order?: string[]; + + @IsOptional() + @IsNumber() + @Type(() => Number) + offset?: number; + + @IsOptional() + @IsNumber() + @Type(() => Number) + limit?: number; + } + + export interface Profile { + id: number; + email: string; + student_id: string; + firstName: string; + lastName: string; + department: IDepartment.Basic | null; + majors: IDepartment.Basic[]; + departments: IDepartment.Basic[]; + favorite_departments: IDepartment.Basic[]; + review_writable_lectures: ILecture.Detail[]; + my_timetable_lectures: ILecture.Detail[]; + reviews: IReview.Basic[]; + } +} diff --git a/src/common/interfaces/IWishlist.ts b/src/common/interfaces/IWishlist.ts new file mode 100644 index 00000000..161b43c7 --- /dev/null +++ b/src/common/interfaces/IWishlist.ts @@ -0,0 +1,21 @@ +import { Type } from 'class-transformer'; +import { IsNumber } from 'class-validator'; +import { ILecture } from './ILecture'; + +export namespace IWishlist { + export interface WithLectures { + lectures: ILecture.Basic[]; + } + + export class AddLectureDto { + @IsNumber() + @Type(() => Number) + lecture!: number; + } + + export class RemoveLectureDto { + @IsNumber() + @Type(() => Number) + lecture!: number; + } +} diff --git a/src/common/interfaces/constants/additional.track.response.dto.ts b/src/common/interfaces/constants/additional.track.response.dto.ts new file mode 100644 index 00000000..bd0f06b5 --- /dev/null +++ b/src/common/interfaces/constants/additional.track.response.dto.ts @@ -0,0 +1,5 @@ +import { generationUnionTypeChecker } from 'src/common/utils/method.utils'; + +const types = ['DOUBLE', 'MINOR', 'ADVANCED', 'INTERDISCIPLINARY'] as const; +export type AdditionalTrackType = (typeof types)[number]; +export const AddtionalTrackTypeNarrower = generationUnionTypeChecker(...types); diff --git a/src/common/interfaces/constants/feed.ts b/src/common/interfaces/constants/feed.ts new file mode 100644 index 00000000..43673d20 --- /dev/null +++ b/src/common/interfaces/constants/feed.ts @@ -0,0 +1,23 @@ +import { Union } from '../../utils/method.utils'; + +export const FeedType = { + FamousHumanityReview: 'FAMOUS_HUMANITY_REVIEW', + FamousMajorReview: 'FAMOUS_MAJOR_REVIEW', + ReviewWrite: 'REVIEW_WRITE', + RelatedCourse: 'RELATED_COURSE', + RankedReview: 'RANKED_REVIEW', + Rate: 'RATE', +}; +export type FeedType = Union; + +export const FeedVisibleRate = { + FamousHumanityReview: 0.5, + FamousMajorReview: 0.6, + ReviewWrite: 0.6, + RelatedCourse: 0.45, + RankedReview: 0.15, + Rate: 0.25, +}; +export type FeedVisibleRate = Union; + +export const FeedRateMinDays = 3; diff --git a/src/common/interfaces/constants/lecture.ts b/src/common/interfaces/constants/lecture.ts new file mode 100644 index 00000000..2193eded --- /dev/null +++ b/src/common/interfaces/constants/lecture.ts @@ -0,0 +1,8 @@ +import { Union } from '../../utils/method.utils'; + +export const ResearchLecture = { + IndividualStudy: 'Individual Study', + UnderThesisStudy: 'Thesis Study(Undergraduate)', + ThesisResearch: 'Thesis Research(MA/phD)', +}; +export type ResearchLecture = Union; diff --git a/src/common/interfaces/constants/planner.ts b/src/common/interfaces/constants/planner.ts new file mode 100644 index 00000000..52cefbf7 --- /dev/null +++ b/src/common/interfaces/constants/planner.ts @@ -0,0 +1,14 @@ +import { Union } from '../../utils/method.utils'; + +export const PlannerItemType = { + Taken: 'TAKEN', + Future: 'FUTURE', + Arbitrary: 'ARBITRARY', +}; +export type PlannerItemType = Union; + +export enum PlannerItemTypeEnum { + Taken = 'TAKEN', + Future = 'FUTURE', + Arbitrary = 'ARBITRARY', +} diff --git a/src/common/interfaces/dto/auth/sso.dto.ts b/src/common/interfaces/dto/auth/sso.dto.ts deleted file mode 100644 index f1631c99..00000000 --- a/src/common/interfaces/dto/auth/sso.dto.ts +++ /dev/null @@ -1,36 +0,0 @@ -export class SSOUser { - uid: string; // 사용자가 SSO 전체에서 고유하게 받은 ID입니다. 30자를 초과하지 않습니다. - sid: string; // RP에서 사용자를 식별할 수 있는 고유한 값입니다. - first_name: string; // 사용자의 이름입니다. 30자를 초과하지 않습니다. - last_name: string; // 사용자의 성입니다. 30자를 초과하지 않습니다. - email: string; // 사용자의 이메일 주소입니다. 인증된 이메일인지는 보장하지 않으며, 254자를 초과하지 않습니다. - gender: string; // 사용자의 성별입니다. *M (남성), *F (여성), *H (숨김), *E (기타) 또는 30자를 초과하지 않는 성별을 나타내는 문자열입니다. - birthday?: Date; // 사용자의 생일입니다. YYYY-MM-DD 형식의 날짜 또는 빈 문자열 값입니다. - point: number; // 사용자의 포인트를 나타냅니다. 개발 전용 서비스와 실제 포인트 값은 분리되어 적용됩니다. - flags: string[]; // 사용자가 특별한 권한이 있는 경우 이를 표시합니다. 사용자가 테스트 기능이 있는 경우 TEST 문자열이, SPARCS 회원인 경우 SPARCS 문자열이 포함되어 있습니다. - facebook_id: string; // 사용자의 페이스북 고유 ID입니다. 연동하지 않았을 경우 빈 문자열입니다. - twitter_id: string; // 사용자의 트위터 고유 ID입니다. 연동하지 않았을 경우 빈 문자열입니다. - kaist_id: string; // 사용자의 KAIST 고유 ID입니다. 연동하지 않았을 경우 빈 문자열입니다. - kaist_info: KaistInfo; // 사용자의 KAIST Portal 데이터입니다. ku_std_no, kaist_uid, ku_psft_user_status_kor, employeeType, ku_person_type, ku_kaist_org_id, ku_psft_user_status, ku_acad_prog_code, ku_born_date, ku_person_type_kor, sn, mail, displayname, givenname, ku_sex, ku_kname의 데이터가 포함되어 있습니다. - kaist_info_time?: Date; // kaist_info를 업데이트한 날짜입니다. YYYY-MM-DD 형식의 날짜 또는 빈 문자열 값입니다. - sparcs_id: string; // 사용자의 SPARCS ID입니다. SPARCS 회원이 아닌 경우 빈 문자열 값입니다. -} - -export class KaistInfo { - ku_std_no: string; - kaist_uid: string; - ku_psft_user_status_kor: string; - employeeType: string; - ku_person_type: string; - ku_kaist_org_id: string; - ku_psft_user_status: string; - ku_acad_prog_code: string; - ku_born_date: string; - ku_person_type_kor: string; - sn: string; - mail: string; - displayname: string; - givenname: string; - ku_sex: string; - ku_kname: string; -} \ No newline at end of file diff --git a/src/common/interfaces/dto/department/department.response.dto.ts b/src/common/interfaces/dto/department/department.response.dto.ts deleted file mode 100644 index 71a4dfde..00000000 --- a/src/common/interfaces/dto/department/department.response.dto.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { Prisma, session_userprofile_majors} from "@prisma/client"; - - -export type DepartmentResponseDto = Partial; \ No newline at end of file diff --git a/src/common/interfaces/dto/user/user.response.dto.ts b/src/common/interfaces/dto/user/user.response.dto.ts deleted file mode 100644 index ee593038..00000000 --- a/src/common/interfaces/dto/user/user.response.dto.ts +++ /dev/null @@ -1,11 +0,0 @@ - -export interface ProfileDto { - id: string; - email: string; - student_id: string; - firstName: string; - lastName: string; - /* - @todo: add more fields for relations - */ -} \ No newline at end of file diff --git a/src/common/interfaces/index.ts b/src/common/interfaces/index.ts new file mode 100644 index 00000000..3e849083 --- /dev/null +++ b/src/common/interfaces/index.ts @@ -0,0 +1,6 @@ +export * from './IAuth'; +export * from './ICourse'; +export * from './IFeed'; +export * from './ITimetable'; +export * from './ILecture'; +export * from './IShare'; diff --git a/src/common/interfaces/serializer/classtime.serializer.ts b/src/common/interfaces/serializer/classtime.serializer.ts new file mode 100644 index 00000000..749df3ec --- /dev/null +++ b/src/common/interfaces/serializer/classtime.serializer.ts @@ -0,0 +1,68 @@ +import { subject_classtime } from '@prisma/client'; +import { getTimeNumeric } from 'src/common/utils/time.utils'; +import { ILecture } from '../ILecture'; + +export const toJsonClasstime = ( + classtime: subject_classtime, +): ILecture.Classtime => { + const classroomInfo = getClassroomStrs(classtime); + return Object.assign(classroomInfo, { + day: classtime.day, + begin: getTimeNumeric(classtime.begin), + end: getTimeNumeric(classtime.end), + }); +}; + +const getClassroomStrs = (classtime: subject_classtime) => { + const buildingFullName = classtime.building_full_name; + const buildingFullNameEn = classtime.building_full_name_en; + + if (!buildingFullName) { + return { + building_code: '', + room_name: '', + classroom: '정보 없음', + classroom_en: 'Unknown', + classroom_short: '정보 없음', + classroom_short_en: 'Unknown', + }; + } else if (buildingFullName[0] == '(') { + const rightParanthesisIndex = buildingFullName.indexOf(')'); + const buildingCode = buildingFullName.slice(1, rightParanthesisIndex); + const buildingName = buildingFullName.slice(rightParanthesisIndex + 1); + const buildingNameEn = buildingFullNameEn + ? buildingFullNameEn.slice(rightParanthesisIndex + 1) + : buildingName; + const roomName = classtime.room_name ?? ''; + const classroom = '(' + buildingCode + ') ' + buildingName + ' ' + roomName; + const classroomEn = + '(' + buildingCode + ') ' + buildingNameEn + ' ' + roomName; + const classroomShort = '(' + buildingCode + ') ' + roomName; + const classroomShortEn = '(' + buildingCode + ') ' + roomName; + + return { + building_code: buildingCode, + room_name: roomName, + classroom: classroom, + classroom_en: classroomEn, + classroom_short: classroomShort, + classroom_short_en: classroomShortEn, + }; + } else { + const buildingCode = ''; + const roomName = classtime.room_name ?? ''; + const classroom = buildingFullName + ' ' + roomName; + const classroomEn = buildingFullNameEn + ' ' + roomName; + const classroomShort = buildingFullName + ' ' + roomName; + const classroomShortEn = buildingFullNameEn + ' ' + roomName; + + return { + building_code: buildingCode, + room_name: roomName, + classroom: classroom, + classroom_en: classroomEn, + classroom_short: classroomShort, + classroom_short_en: classroomShortEn, + }; + } +}; diff --git a/src/common/interfaces/serializer/course.serializer.ts b/src/common/interfaces/serializer/course.serializer.ts new file mode 100644 index 00000000..464574d4 --- /dev/null +++ b/src/common/interfaces/serializer/course.serializer.ts @@ -0,0 +1,88 @@ +import { + subject_course, + subject_lecture, + subject_professor, +} from '@prisma/client'; +import { ECourse } from 'src/common/entities/ECourse'; +import { applyOrder } from '../../utils/search.utils'; +import { ICourse } from '../ICourse'; +import { IProfessor } from '../IProfessor'; +import { toJsonDepartment } from './department.serializer'; +import { toJsonProfessors } from './professor.serializer'; + +export function toJsonFeedBasic(course: subject_course): ICourse.FeedBasic { + return { + id: course.id, + old_code: course.old_code, + department_id: course.department_id, + type: course.type, + type_en: course.type_en, + title: course.title, + title_en: course.title_en, + summary: course.summury, + grade_sum: course.grade_sum + 0.00001, + load_sum: course.load_sum + 0.00001, + speech_sum: course.speech_sum + 0.000001, + review_total_weight: course.review_total_weight + 0.000001, + grade: course.grade + 0.000001, + load: course.load + 0.00001, + speech: course.speech + 0.00001, + title_en_no_space: course.title_en_no_space, + title_no_space: course.title_no_space, + }; +} + +export function toJsonFeedRelated(course: subject_course): ICourse.FeedRelated { + return { + ...toJsonFeedBasic(course), + related_courses_prior: [], // TODO: related courses 비어있는게 맞는지 확인 + related_courses_posterior: [], + }; +} + +export function toJsonCourseBasic( + course: Omit, + lecture: subject_lecture, +): ICourse.Basic { + return { + id: course.id, + old_code: course.old_code, + department: toJsonDepartment(course.subject_department, true), + type: course.type, + type_en: course.type_en, + title: course.title, + title_en: course.title_en, + summary: course.summury, // Todo: fix summury typo in db. + review_total_weight: course.review_total_weight + 0.000001, + credit: lecture.credit ?? 0, + credit_au: lecture.credit_au ?? 0, + num_classes: lecture.num_classes ?? 0, + num_labs: lecture.num_labs ?? 0, + }; +} + +export function toJsonCourseDetail( + course: ECourse.Details, + lecture: subject_lecture, + professor: subject_professor[], +): ICourse.Detail { + const basic = toJsonCourseBasic(course, lecture); + const professorJson: IProfessor.Basic[] = toJsonProfessors(professor, true); + const professorSorted = applyOrder(professorJson, ['name']); + return { + ...basic, + related_courses_prior: [], + related_courses_posterior: [], + professors: professorSorted, + grade: course.grade + 0.000001, + load: course.load + 0.000001, + speech: course.speech + 0.000001, + }; +} + +export function addIsRead( + course: ICourse.Detail, + isRead: boolean, +): ICourse.DetailWithIsRead { + return Object.assign(course, { userspecific_is_read: isRead }); +} diff --git a/src/common/interfaces/serializer/department.serializer.ts b/src/common/interfaces/serializer/department.serializer.ts new file mode 100644 index 00000000..927d1187 --- /dev/null +++ b/src/common/interfaces/serializer/department.serializer.ts @@ -0,0 +1,14 @@ +import { subject_department } from '@prisma/client'; +import { IDepartment } from '../IDepartment'; + +export const toJsonDepartment = ( + department: subject_department, + nested = false, +): IDepartment.Basic => { + return { + id: department.id, + name: department.name, + name_en: department.name_en ?? '', + code: department.code, + }; +}; diff --git a/src/common/interfaces/serializer/examtime.serializer.ts b/src/common/interfaces/serializer/examtime.serializer.ts new file mode 100644 index 00000000..67231764 --- /dev/null +++ b/src/common/interfaces/serializer/examtime.serializer.ts @@ -0,0 +1,41 @@ +import { subject_examtime } from '@prisma/client'; +import { getTimeNumeric } from 'src/common/utils/time.utils'; + +export const toJsonExamtime = (examtime: subject_examtime) => { + const DAY_STR = [ + '월요일', + '화요일', + '수요일', + '목요일', + '금요일', + '토요일', + '일요일', + ]; + const DAY_STR_EN = [ + 'Monday', + 'Tuesday', + 'Wednesday', + 'Thursday', + 'Friday', + 'Saturday', + 'Sunday', + ]; + return { + day: examtime.day, + str: `${DAY_STR[examtime.day]} ${timeFormatter( + examtime.begin, + )} ~ ${timeFormatter(examtime.end)}`, + str_en: `${DAY_STR_EN[examtime.day]} ${timeFormatter( + examtime.begin, + )} ~ ${timeFormatter(examtime.end)}`, + begin: getTimeNumeric(examtime.begin, false), + end: getTimeNumeric(examtime.end, false), + }; +}; + +const timeFormatter = (time: Date) => { + return `${time.getUTCHours().toString().padStart(2, '0')}:${time + .getUTCMinutes() + .toString() + .padStart(2, '0')}`; +}; diff --git a/src/common/interfaces/serializer/feeds.serializer.ts b/src/common/interfaces/serializer/feeds.serializer.ts new file mode 100644 index 00000000..715f6928 --- /dev/null +++ b/src/common/interfaces/serializer/feeds.serializer.ts @@ -0,0 +1,56 @@ +import { session_userprofile } from '@prisma/client'; +import { EFeed } from 'src/common/entities/EFeed'; +import { IFeed } from '../IFeed'; +import { FeedType } from '../constants/feed'; +import { toJsonFeedRelated } from './course.serializer'; +import { toJsonDepartment } from './department.serializer'; +import { toJsonLectureBasic } from './lecture.serializer'; +import { toJsonReview } from './review.serializer'; + +export const toJsonFeedDetails = ( + feed: EFeed.Details, + user: session_userprofile, +): IFeed.Details => { + if (EFeed.isFamousHumanityReview(feed)) { + return { + type: FeedType.FamousHumanityReview, + date: feed.date, + priority: feed.priority, + reviews: feed.main_famoushumanityreviewdailyfeed_reviews.map( + (feedReview) => toJsonReview(feedReview.review_review, user), + ), + }; + } else if (EFeed.isFamousMajorReview(feed)) { + return { + type: FeedType.FamousMajorReview, + date: feed.date, + priority: feed.priority, + reviews: feed.main_famousmajorreviewdailyfeed_reviews.map((feedReview) => + toJsonReview(feedReview.review_review, user), + ), + department: toJsonDepartment(feed.subject_department), + }; + } else if (EFeed.isReviewWrite(feed)) { + return { + type: FeedType.ReviewWrite, + date: feed.date, + priority: feed.priority, + lecture: toJsonLectureBasic(feed.subject_lecture), + }; + } else if (EFeed.isRelatedCourse(feed)) { + return { + type: FeedType.RelatedCourse, + date: feed.date, + priority: feed.priority, + course: toJsonFeedRelated(feed.subject_course), + }; + } else if (EFeed.isRankedReview(feed)) { + return { + type: FeedType.RankedReview, + date: feed.date, + priority: feed.priority, + reviews: feed.reviews.map((review) => toJsonReview(review, user)), + }; + } + return { type: FeedType.Rate, date: feed.date, priority: feed.priority }; +}; diff --git a/src/common/interfaces/serializer/lecture.serializer.ts b/src/common/interfaces/serializer/lecture.serializer.ts new file mode 100644 index 00000000..f55b9881 --- /dev/null +++ b/src/common/interfaces/serializer/lecture.serializer.ts @@ -0,0 +1,63 @@ +import { ELecture } from 'src/common/entities/ELecture'; +import { applyOrder } from 'src/common/utils/search.utils'; +import { ILecture } from '../ILecture'; +import { toJsonClasstime } from './classtime.serializer'; +import { toJsonExamtime } from './examtime.serializer'; +import { toJsonProfessors } from './professor.serializer'; + +export function toJsonLectureBasic(lecture: ELecture.Extended): ILecture.Basic { + const professors = lecture.subject_lecture_professors.map((x) => x.professor); + const ordered_professors = applyOrder(professors, ['professor_name']); + + return { + id: lecture.id, + title: lecture.title, + title_en: lecture.title_en, + course: lecture.course_id, + old_code: lecture.old_code, + class_no: lecture.class_no, + year: lecture.year, + semester: lecture.semester, + code: lecture.code, + department: lecture.department_id, + department_code: lecture.subject_department.code, + department_name: lecture.subject_department.name, + department_name_en: + lecture.subject_department.name_en ?? lecture.subject_department.name, + type: lecture.type, + type_en: lecture.type_en, + limit: lecture.limit, + num_people: lecture.num_people ?? 0, + is_english: lecture.is_english, + num_classes: lecture.num_classes, + num_labs: lecture.num_labs, + credit: lecture.credit, + credit_au: lecture.credit_au, + common_title: lecture.common_title ?? '', + common_title_en: lecture.common_title_en ?? '', + class_title: lecture.class_title ?? '', + class_title_en: lecture.class_title_en ?? '', + review_total_weight: lecture.review_total_weight + 0.000001, + professors: toJsonProfessors(ordered_professors), + }; +} + +export function toJsonLectureDetail( + lecture: ELecture.Details, +): ILecture.Detail { + const basic = toJsonLectureBasic(lecture); + if (!ELecture.isDetails(lecture)) + throw new Error("Lecture is not of type 'ELecture.Details'"); + + return Object.assign(basic, { + grade: lecture.grade + 0.000001, + load: lecture.load + 0.000001, + speech: lecture.speech + 0.000001, + classtimes: lecture.subject_classtime.map((classtime) => + toJsonClasstime(classtime), + ), + examtimes: lecture.subject_examtime.map((examtime) => + toJsonExamtime(examtime), + ), + }); +} diff --git a/src/common/interfaces/serializer/notices.serializer.ts b/src/common/interfaces/serializer/notices.serializer.ts new file mode 100644 index 00000000..b0a39235 --- /dev/null +++ b/src/common/interfaces/serializer/notices.serializer.ts @@ -0,0 +1,11 @@ +import { ENotice } from 'src/common/entities/ENotice'; +import { INotice } from '../INotice'; + +export function toJsonNoticeBasic(notice: ENotice.Basic): INotice.Basic { + return { + title: notice.title, + content: notice.content, + start_time: notice.start_time, + end_time: notice.end_time, + }; +} diff --git a/src/common/interfaces/serializer/planner.item.serializer.ts b/src/common/interfaces/serializer/planner.item.serializer.ts new file mode 100644 index 00000000..8c8ddf2e --- /dev/null +++ b/src/common/interfaces/serializer/planner.item.serializer.ts @@ -0,0 +1,82 @@ +import { BadRequestException } from '@nestjs/common'; +import { EPlanners } from '../../entities/EPlanners'; +import { IPlanner } from '../IPlanner'; +import { PlannerItemType } from '../constants/planner'; +import { toJsonCourseDetail } from './course.serializer'; +import { toJsonDepartment } from './department.serializer'; +import { toJsonLectureDetail } from './lecture.serializer'; + +export function toJsonPlannerItem( + item: + | EPlanners.EItems.Taken.Details + | EPlanners.EItems.Future.Extended + | EPlanners.EItems.Arbitrary.Extended, + item_type: IT, +): IPlanner.IItem.IMutate { + if (item_type === PlannerItemType.Taken) { + return toJsonTakenItem(item as EPlanners.EItems.Taken.Details); + } else if (item_type === PlannerItemType.Future) { + return toJsonFutureItem(item as EPlanners.EItems.Future.Extended); + } else if (item_type === PlannerItemType.Arbitrary) { + return toJsonArbitraryItem(item as EPlanners.EItems.Arbitrary.Extended); + } else { + throw new BadRequestException('Invalid Planner Item Type'); + } +} + +export const toJsonTakenItem = ( + taken_item: EPlanners.EItems.Taken.Details, +): IPlanner.IItem.Taken => { + return { + id: taken_item.id, + item_type: 'TAKEN', + is_excluded: taken_item.is_excluded, + lecture: toJsonLectureDetail(taken_item.subject_lecture), + course: toJsonCourseDetail( + taken_item.subject_lecture.course, + taken_item.subject_lecture, + taken_item.subject_lecture.course.subject_course_professors.map( + (x) => x.professor, + ), + ), + }; +}; + +export const toJsonArbitraryItem = ( + arbitrary_item: EPlanners.EItems.Arbitrary.Extended, +): IPlanner.IItem.Arbitrary => { + return { + id: arbitrary_item.id, + item_type: 'ARBITRARY', + is_excluded: arbitrary_item.is_excluded, + year: arbitrary_item.year, + semester: arbitrary_item.semester, + department: + arbitrary_item.subject_department !== null + ? toJsonDepartment(arbitrary_item.subject_department) + : null, + type: arbitrary_item.type, + type_en: arbitrary_item.type_en, + credit: arbitrary_item.credit, + credit_au: arbitrary_item.credit_au, + }; +}; + +export const toJsonFutureItem = ( + future_item: EPlanners.EItems.Future.Extended, +): IPlanner.IItem.Future => { + return { + id: future_item.id, + item_type: 'FUTURE', + is_excluded: future_item.is_excluded, + year: future_item.year, + semester: future_item.semester, + course: toJsonCourseDetail( + future_item.subject_course, + future_item.subject_course.lecture[0], + future_item.subject_course.subject_course_professors.map( + (x) => x.professor, + ), + ), + }; +}; diff --git a/src/common/interfaces/serializer/planner.serializer.ts b/src/common/interfaces/serializer/planner.serializer.ts new file mode 100644 index 00000000..3106431f --- /dev/null +++ b/src/common/interfaces/serializer/planner.serializer.ts @@ -0,0 +1,36 @@ +import { EPlanners } from 'src/common/entities/EPlanners'; +import { IPlanner } from '../IPlanner'; +import { + toJsonArbitraryItem, + toJsonFutureItem, + toJsonTakenItem, +} from './planner.item.serializer'; +import { + toJsonAdditionalTrack, + toJsonGeneralTrack, + toJsonMajorTrack, +} from './track.serializer'; + +export const toJsonPlanner = (planner: EPlanners.Details): IPlanner.Detail => { + return { + id: planner.id, + start_year: planner.start_year, + end_year: planner.end_year, + general_track: toJsonGeneralTrack(planner.graduation_generaltrack), + major_track: toJsonMajorTrack(planner.graduation_majortrack), + additional_tracks: planner.planner_planner_additional_tracks.map( + (additional_track) => + toJsonAdditionalTrack(additional_track.graduation_additionaltrack), + ), + taken_items: planner.planner_takenplanneritem.map((item) => + toJsonTakenItem(item), + ), + future_items: planner.planner_futureplanneritem.map((item) => + toJsonFutureItem(item), + ), + arbitrary_items: planner.planner_arbitraryplanneritem.map((item) => + toJsonArbitraryItem(item), + ), + arrange_order: planner.arrange_order, + }; +}; diff --git a/src/common/interfaces/serializer/professor.serializer.ts b/src/common/interfaces/serializer/professor.serializer.ts new file mode 100644 index 00000000..283d0784 --- /dev/null +++ b/src/common/interfaces/serializer/professor.serializer.ts @@ -0,0 +1,24 @@ +import { subject_professor } from '@prisma/client'; +import { IProfessor } from '../IProfessor'; + +export const toJsonProfessors = ( + professors: subject_professor[], + nested = false, +): IProfessor.Basic[] => { + const result = professors.map((professor) => { + return { + name: professor.professor_name, + name_en: professor.professor_name_en ?? '', + professor_id: professor.professor_id, + review_total_weight: professor.review_total_weight + 0.000001, + }; + }); + + if (nested) { + return result; + } + + return result.map((professor) => { + return professor; //todo: add necessary infos + }); +}; diff --git a/src/common/interfaces/serializer/rate.serializer.ts b/src/common/interfaces/serializer/rate.serializer.ts new file mode 100644 index 00000000..9c0abef8 --- /dev/null +++ b/src/common/interfaces/serializer/rate.serializer.ts @@ -0,0 +1,12 @@ +import { support_rate } from '@prisma/client'; +import { IRate } from '../IRate'; + +export function toJsonRate(rate: support_rate): IRate.Basic { + return { + id: rate.id, + score: rate.score, + user_id: rate.user_id, + version: rate.version, + created_datetime: rate.created_datetime, + }; +} diff --git a/src/common/interfaces/serializer/review.serializer.ts b/src/common/interfaces/serializer/review.serializer.ts new file mode 100644 index 00000000..e5e092cd --- /dev/null +++ b/src/common/interfaces/serializer/review.serializer.ts @@ -0,0 +1,42 @@ +import { session_userprofile } from '@prisma/client'; +import { EReview } from 'src/common/entities/EReview'; +import { getRepresentativeLecture } from 'src/common/utils/lecture.utils'; +import { IReview } from '../IReview'; +import { toJsonCourseBasic } from './course.serializer'; +import { toJsonLectureBasic } from './lecture.serializer'; + +export const toJsonReview = ( + review: EReview.Details, + user?: session_userprofile, +): IReview.Basic => { + const representativeLecture = getRepresentativeLecture(review.course.lecture); + + let isLiked = true; + if (!user || !review.review_reviewvote) { + isLiked = false; + } else if ( + !review.review_reviewvote.find( + (reviewvote) => reviewvote.userprofile_id === user.id, + ) + ) { + isLiked = false; + } + + const courseResult = toJsonCourseBasic(review.course, representativeLecture); + + const result = { + id: review.id, + course: courseResult, + lecture: toJsonLectureBasic(review.lecture), + content: review.is_deleted + ? '관리자에 의해 삭제된 코멘트입니다.' + : review.content, + like: Math.round(review.like), + is_deleted: Math.round(review.is_deleted), + grade: Math.round(review.grade), + load: Math.round(review.load), + speech: Math.round(review.speech), + userspecific_is_liked: isLiked, + }; + return result; +}; diff --git a/src/common/interfaces/serializer/semester.serializer.ts b/src/common/interfaces/serializer/semester.serializer.ts new file mode 100644 index 00000000..43f2f21f --- /dev/null +++ b/src/common/interfaces/serializer/semester.serializer.ts @@ -0,0 +1,20 @@ +import { ESemester } from 'src/common/entities/ESemester'; +import { ISemester } from '../ISemester'; + +export const toJsonSemester = ( + semester: ESemester.Basic, +): ISemester.Response => { + return { + year: semester.year, + semester: semester.semester, + beginning: semester.beginning, + end: semester.end, + courseDesciptionSubmission: semester.courseDesciptionSubmission, + courseRegistrationPeriodStart: semester.courseRegistrationPeriodStart, + courseRegistrationPeriodEnd: semester.courseRegistrationPeriodEnd, + courseAddDropPeriodEnd: semester.courseAddDropPeriodEnd, + courseDropDeadline: semester.courseDropDeadline, + courseEvaluationDeadline: semester.courseEvaluationDeadline, + gradePosting: semester.gradePosting, + }; +}; diff --git a/src/common/interfaces/serializer/timetable.serializer.ts b/src/common/interfaces/serializer/timetable.serializer.ts new file mode 100644 index 00000000..80310e22 --- /dev/null +++ b/src/common/interfaces/serializer/timetable.serializer.ts @@ -0,0 +1,22 @@ +import { ELecture } from 'src/common/entities/ELecture'; +import { ETimetable } from 'src/common/entities/ETimetable'; +import { ITimetable } from '../ITimetable'; +import { toJsonLectureDetail } from './lecture.serializer'; + +export const toJsonTimetable = ( + timetable: ETimetable.Details | ETimetable.Basic, + lectures?: ELecture.Details[], +): ITimetable.Response => { + const lecturesList = + 'timetable_timetable_lectures' in timetable + ? timetable.timetable_timetable_lectures.map((x) => x.subject_lecture) + : lectures; + if (lecturesList === undefined) { + throw new Error('lecturesList is undefined'); + } + return { + id: timetable.id, + lectures: lecturesList.map((lecture) => toJsonLectureDetail(lecture)), + arrange_order: timetable.arrange_order, + }; +}; diff --git a/src/common/interfaces/serializer/track.serializer.ts b/src/common/interfaces/serializer/track.serializer.ts new file mode 100644 index 00000000..225a02bb --- /dev/null +++ b/src/common/interfaces/serializer/track.serializer.ts @@ -0,0 +1,62 @@ +import { ETrack } from 'src/common/entities/ETrack'; +import { IPlanner } from '../IPlanner'; +import { AddtionalTrackTypeNarrower } from '../constants/additional.track.response.dto'; +import { toJsonDepartment } from './department.serializer'; + +export const toJsonGeneralTrack = ( + generalTrack: ETrack.General, +): IPlanner.ITrack.General => { + return { + id: generalTrack.id, + start_year: generalTrack.start_year, + end_year: generalTrack.end_year, + is_foreign: generalTrack.is_foreign, + total_credit: generalTrack.total_credit, + total_au: generalTrack.total_au, + basic_required: generalTrack.basic_required, + basic_elective: generalTrack.basic_elective, + thesis_study: generalTrack.thesis_study, + thesis_study_doublemajor: generalTrack.thesis_study_doublemajor, + general_required_credit: generalTrack.general_required_credit, + general_required_au: generalTrack.general_required_au, + humanities: generalTrack.humanities, + humanities_doublemajor: generalTrack.humanities_doublemajor, + }; +}; + +export const toJsonMajorTrack = ( + majorTrack: ETrack.Major, +): IPlanner.ITrack.Major => { + return { + id: majorTrack.id, + start_year: majorTrack.start_year, + end_year: majorTrack.end_year, + department: toJsonDepartment(majorTrack.subject_department), + basic_elective_doublemajor: majorTrack.basic_elective_doublemajor, + major_required: majorTrack.major_required, + major_elective: majorTrack.major_elective, + }; +}; + +export const toJsonAdditionalTrack = ( + additionalTrack: ETrack.Additional, +): IPlanner.ITrack.Additional => { + const type = AddtionalTrackTypeNarrower(additionalTrack.type); + + if (type instanceof Error) { + throw type; + } + + return { + id: additionalTrack.id, + start_year: additionalTrack.start_year, + end_year: additionalTrack.end_year, + type, + department: + additionalTrack.subject_department === null + ? null + : toJsonDepartment(additionalTrack.subject_department), + major_required: additionalTrack.major_required, + major_elective: additionalTrack.major_elective, + }; +}; diff --git a/src/common/interfaces/serializer/wishlist.serializer.ts b/src/common/interfaces/serializer/wishlist.serializer.ts new file mode 100644 index 00000000..83a66e5b --- /dev/null +++ b/src/common/interfaces/serializer/wishlist.serializer.ts @@ -0,0 +1,13 @@ +import { EWishlist } from 'src/common/entities/EWishlist'; +import { IWishlist } from '../IWishlist'; +import { toJsonLectureDetail } from './lecture.serializer'; + +export const toJsonWishlist = ( + wishlist: EWishlist.WithLectures, +): IWishlist.WithLectures => { + return { + lectures: wishlist.timetable_wishlist_lectures.map((lecture) => + toJsonLectureDetail(lecture.subject_lecture), + ), + }; +}; diff --git a/src/common/pipe/courseId.pipe.ts b/src/common/pipe/courseId.pipe.ts new file mode 100644 index 00000000..8c2e8549 --- /dev/null +++ b/src/common/pipe/courseId.pipe.ts @@ -0,0 +1,29 @@ +import { + BadRequestException, + Injectable, + PipeTransform, + ArgumentMetadata, +} from '@nestjs/common'; +import { PrismaService } from '@src/prisma/prisma.service'; + +@Injectable() +export class CourseIdPipe implements PipeTransform { + constructor(private prismaService: PrismaService) {} + + async transform(value: any, metadata: ArgumentMetadata): Promise { + const courseId = parseInt(value, 10); + if (isNaN(courseId)) { + throw new BadRequestException('Invalid course ID'); + } + + const course = await this.prismaService.subject_course.findUnique({ + where: { id: courseId }, + }); + + if (!course) { + throw new BadRequestException('Course not found'); + } + + return courseId; + } +} diff --git a/src/common/pipe/planner.pipe.ts b/src/common/pipe/planner.pipe.ts new file mode 100644 index 00000000..02806f41 --- /dev/null +++ b/src/common/pipe/planner.pipe.ts @@ -0,0 +1,29 @@ +import { + BadRequestException, + Injectable, + PipeTransform, + ArgumentMetadata, +} from '@nestjs/common'; +import { PrismaService } from '@src/prisma/prisma.service'; + +@Injectable() +export class PlannerPipe implements PipeTransform { + constructor(private prismaService: PrismaService) {} + + async transform(value: any, metadata: ArgumentMetadata): Promise { + const plannerId = parseInt(value, 10); + if (isNaN(plannerId)) { + throw new BadRequestException('Invalid planner ID'); + } + + const planner = await this.prismaService.planner_planner.findUnique({ + where: { id: plannerId }, + }); + + if (!planner) { + throw new BadRequestException('Planner not found'); + } + + return plannerId; + } +} diff --git a/src/common/scholarDB/scripts.ts b/src/common/scholarDB/scripts.ts index 5a37b5cf..40f2d050 100644 --- a/src/common/scholarDB/scripts.ts +++ b/src/common/scholarDB/scripts.ts @@ -1,9 +1,9 @@ - - -export const import_student_lectures = async (studentId: string): Promise => { +export const import_student_lectures = async ( + studentId: string, +): Promise => { /* @Todo implement this function. this function uses sort of python script. I don't know what it is -larry */ -} +}; diff --git a/src/common/types/types.ts b/src/common/types/types.ts new file mode 100644 index 00000000..8ab40321 --- /dev/null +++ b/src/common/types/types.ts @@ -0,0 +1,11 @@ +type Without = { [P in Exclude]?: never }; +export type XOR = (T & Without) | (U & Without); +// XOR 구현한 타입, XOR 이면 A, B둘중 하나의 타입만을 갖는다는 뜻. + +export namespace FilterType { + export type SubjectClasstimeFilter = { + day?: { equals: number }; + begin?: { gte: Date | undefined }; + end?: { lte: Date | undefined }; + }; +} diff --git a/src/common/utils/lecture.utils.ts b/src/common/utils/lecture.utils.ts new file mode 100644 index 00000000..df6aae17 --- /dev/null +++ b/src/common/utils/lecture.utils.ts @@ -0,0 +1,12 @@ +import { subject_lecture } from '@prisma/client'; +import { applyOrder } from './search.utils'; + +export const getRepresentativeLecture = ( + lectures: subject_lecture[], +): subject_lecture => { + const orderedLectures = applyOrder(lectures, [ + 'year', + 'semester', + ]); + return orderedLectures[0]; +}; diff --git a/src/common/utils/method.utils.ts b/src/common/utils/method.utils.ts index 7c7cbf84..866059df 100644 --- a/src/common/utils/method.utils.ts +++ b/src/common/utils/method.utils.ts @@ -5,13 +5,48 @@ export function normalizeArray( defaultObj?: { [key: string]: T | undefined }, ): { [key: string | number | symbol]: T | undefined } { const normalizeObj: { [key: string | number | symbol]: T | undefined } = - defaultObj || {} + defaultObj || {}; - arr.forEach(data => { - const key = selector(data) - if (key !== null) normalizeObj[key] = data - }) + arr.forEach((data) => { + const key = selector(data); + if (key !== null) normalizeObj[key] = data; + }); - return normalizeObj + return normalizeObj; } +export function groupBy( + arr: T[], + selector: (i: T) => K, +): Record { + return arr.reduce((groups: Record, item) => { + (groups[selector(item)] ??= []).push(item); + return groups; + }, {} as Record); +} + +type ValueType = string | number | boolean; + +export type Union< + T extends { [key: string]: ValueType } | ReadonlyArray, +> = T extends ReadonlyArray + ? T[number] + : T extends { [key: string]: infer U } + ? U + : never; + +export function getRandomChoice(choices: T[]): T { + const randomIndex = Math.floor(Math.random() * choices.length); + return choices[randomIndex]; +} + +export function generationUnionTypeChecker( + ...values: UnionType[] +) { + return function (value: unknown): UnionType | Error { + if (typeof value !== 'string') return new Error('Invalid value: ' + value); + return values.includes(value as UnionType) + ? (value as UnionType) + : new Error('Invalid value: ' + value); + }; +} diff --git a/src/common/utils/search.utils.ts b/src/common/utils/search.utils.ts new file mode 100644 index 00000000..949c47d4 --- /dev/null +++ b/src/common/utils/search.utils.ts @@ -0,0 +1,87 @@ +import { SemesterRepository } from '../../prisma/repositories/semester.repository'; + +export function applyOrder(query: T[], order_opt: (keyof T)[]): T[] { + if (order_opt.length == 0) { + return query; + } + return query.sort((a: T, b: T): 1 | 0 | -1 => { + for (const order of order_opt) { + if (a[order] === b[order]) { + continue; + } + return a[order] > b[order] ? 1 : -1; + } + return 0; + }); +} + +export function applyOffset(query: T[], offset: number) { + if (!offset) { + return query; + } else { + return query?.slice(offset) ?? []; + } +} + +export function semesterFilter( + year: number, + semester: number, +): { year?: number; semester?: number } { + let semesterFilter: object = {}; + if (year) { + semesterFilter = { ...semesterFilter, year: year }; + } + if (semester) { + semesterFilter = { ...semesterFilter, semester: semester }; + } + return semesterFilter; +} + +export type orderFilterType = { + [key: string]: orderFilterType | string; +}; + +export function orderFilter(order: string[] | undefined): orderFilterType[] { + // TODO: unit test + if (order === undefined) return []; + + const orderFilter: orderFilterType[] = []; + order.forEach((orderList) => { + const orderBy = orderList.split('-'); + const order = orderBy[0] == '' ? 'desc' : 'asc'; + + const orderDict = orderDictHelper( + orderBy[orderBy.length - 1].split('__'), + order, + ); + orderFilter.push(orderDict); + }); + return orderFilter; +} + +function orderDictHelper(orderList: string[], order: string): orderFilterType { + if (orderList.length == 0) { + return {}; + } else if (orderList[0] == '') { + return orderDictHelper(orderList.slice(1), order); + } else if (orderList.length == 1) { + return { [orderList[0]]: order }; + } else { + return { [orderList[0]]: orderDictHelper(orderList.slice(1), order) }; + } +} + +export async function validateYearAndSemester( + year: number, + semester: number, + semesterRepo: SemesterRepository, +) { + const existsSemester: boolean = await semesterRepo.existsSemester( + year, + semester, + ); + return ( + existsSemester || + (2009 < year && year < 2018 && semester && [1, 3].includes(semester)) + ); +} diff --git a/src/common/utils/time.utils.ts b/src/common/utils/time.utils.ts new file mode 100644 index 00000000..e266a289 --- /dev/null +++ b/src/common/utils/time.utils.ts @@ -0,0 +1,8 @@ +export const getTimeNumeric = (time: Date, isClass = true) => { + const beginNumeric = time.getUTCHours() * 60 + time.getUTCMinutes(); + if (beginNumeric % 30 && isClass) { + return beginNumeric - (beginNumeric % 30); + } else { + return beginNumeric; + } +}; diff --git a/src/dotenv-options.ts b/src/dotenv-options.ts index 1ca4e6f1..e70f0a8b 100644 --- a/src/dotenv-options.ts +++ b/src/dotenv-options.ts @@ -1,7 +1,7 @@ import path from 'path'; const env = process.env.NODE_ENV || 'local'; -const envFilePath = path.join( process.cwd(), `env/.env.${env}`); +const envFilePath = path.join(process.cwd(), `env/.env.${env}`); console.log(`Loading environment from ${envFilePath}`); const dotEnvOptions = { path: envFilePath, diff --git a/src/main.ts b/src/main.ts index 356a1b6d..75696bb4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,6 +1,3 @@ -import { NestFactory } from '@nestjs/core'; -import { AppModule } from './app.module'; - // async function bootstrap() { // const app = await NestFactory.create(AppModule); // await app.listen(3000); diff --git a/src/modules/auth/auth.chain.ts b/src/modules/auth/auth.chain.ts new file mode 100644 index 00000000..e9a5f101 --- /dev/null +++ b/src/modules/auth/auth.chain.ts @@ -0,0 +1,42 @@ +import { + ExecutionContext, + ForbiddenException, + Injectable, + UnauthorizedException, +} from '@nestjs/common'; +import { AuthCommand, AuthResult } from './auth.command'; + +@Injectable() +export class AuthChain { + private authChain: AuthCommand[]; + + constructor() { + this.authChain = []; + } + + public register(command: AuthCommand) { + this.authChain.push(command); + return this; + } + + public async execute(context: ExecutionContext) { + let result = { + authorization: false, + authentication: false, + isPublic: false, + }; + for (const command of this.authChain) { + result = await command.next(context, result); + } + return this.handleException(result); + } + + private async handleException(result: AuthResult) { + if (result.isPublic) return true; + else { + if (!result.authentication) throw new UnauthorizedException(); + if (!result.authorization) throw new ForbiddenException(); + return true; + } + } +} diff --git a/src/modules/auth/auth.command.ts b/src/modules/auth/auth.command.ts new file mode 100644 index 00000000..33ff0ae9 --- /dev/null +++ b/src/modules/auth/auth.command.ts @@ -0,0 +1,11 @@ +import { ExecutionContext } from '@nestjs/common'; + +export interface AuthResult { + authentication: boolean; + authorization: boolean; + isPublic: boolean; +} + +export interface AuthCommand { + next(context: ExecutionContext, prevResult: AuthResult): Promise; +} diff --git a/src/modules/auth/auth.config.ts b/src/modules/auth/auth.config.ts new file mode 100644 index 00000000..ee8dcb21 --- /dev/null +++ b/src/modules/auth/auth.config.ts @@ -0,0 +1,45 @@ +import { ExecutionContext, Injectable } from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { AuthService } from './auth.service'; +import { JwtService } from '@nestjs/jwt'; +import { JwtCommand } from './command/jwt.command'; +import { SidCommand } from './command/sid.command'; +import { IsPublicCommand } from './command/isPublic.command'; +import { AuthChain } from './auth.chain'; + +@Injectable() +export class AuthConfig { + constructor( + private authChain: AuthChain, + private readonly jwtCommand: JwtCommand, + private readonly sidCommand: SidCommand, + private readonly isPublicCommand: IsPublicCommand, + ) {} + + public async config(env: string) { + if (env == 'local') return this.getLocalGuardConfig(); + if (env == 'dev') return this.getDevGuardConfig(); + if (env == 'prod') return this.getProdGuardConfig(); + else return this.getProdGuardConfig(); + } + + private getLocalGuardConfig = () => { + return this.authChain + .register(this.isPublicCommand) + .register(this.sidCommand) + .register(this.jwtCommand); + }; + + private getDevGuardConfig = () => { + return this.authChain + .register(this.isPublicCommand) + .register(this.sidCommand) + .register(this.jwtCommand); + }; + + private getProdGuardConfig = () => { + return this.authChain + .register(this.jwtCommand) + .register(this.isPublicCommand); + }; +} diff --git a/src/modules/auth/auth.controller.ts b/src/modules/auth/auth.controller.ts index 23651edf..cdfb4fa5 100644 --- a/src/modules/auth/auth.controller.ts +++ b/src/modules/auth/auth.controller.ts @@ -1,21 +1,23 @@ -import { Controller, Get, Query, Req, Res, Session } from "@nestjs/common"; -import { AuthService } from "./auth.service"; -import { Response } from "express"; -import { Client } from "./utils/sparcs-sso"; -import settings from "../../settings"; -import { UserService } from "../user/user.service"; -import { Public } from "../../common/decorators/skip-auth.decorator"; -import { GetUser } from "../../common/decorators/get-user.decorator"; -import { session_userprofile } from "@prisma/client"; -import { SSOUser } from "../../common/interfaces/dto/auth/sso.dto"; +import { Controller, Get, Query, Req, Res, Session } from '@nestjs/common'; +import { session_userprofile } from '@prisma/client'; +import { GetUser } from 'src/common/decorators/get-user.decorator'; +import { ESSOUser } from 'src/common/entities/ESSOUser'; +import { IAuth } from 'src/common/interfaces'; +import { IUser } from 'src/common/interfaces/IUser'; +import { Public } from '../../common/decorators/skip-auth.decorator'; +import settings from '../../settings'; +import { UserService } from '../user/user.service'; +import { AuthService } from './auth.service'; +import { Client } from './utils/sparcs-sso'; +import { request } from 'http'; -@Controller("session") +@Controller('session') export class AuthController { private readonly ssoClient; constructor( private readonly authService: AuthService, - private readonly userService: UserService + private readonly userService: UserService, ) { const ssoConfig = settings().getSsoConfig(); const ssoClient = new Client( @@ -26,70 +28,103 @@ export class AuthController { this.ssoClient = ssoClient; } - @Public() @Get('login') user_login( - @Query('next') next, - @Query('social_login') social_login, - @Req() req, - @Res() res, + @Query('next') next: string, + @Query('social_login') social_login: string, + @Req() req: IAuth.Request, + @Res() res: IAuth.Response, ) { if (req.user) { - return res.redirect(next ?? "/"); + return res.redirect(next ?? '/'); } - req.session["next"] = next ?? "/"; - const { url, state } = this.ssoClient.get_login_params(); - console.log(url, state); - req.session["sso_state"] = state; - if (social_login === "0") { - return res.redirect(url + "&social_enabled=0&show_disabled_button=0"); + req.session['next'] = next ?? '/'; + const request_url = req.get('host') ?? 'otl.kaist.ac.kr'; + console.log(request_url); + const { url, state } = this.ssoClient.get_login_params(request_url); + req.session['sso_state'] = state; + if (social_login === '0') { + return res.redirect(url + '&social_enabled=0&show_disabled_button=0'); } return res.redirect(url); } @Public() - @Get("login/callback") + @Get('login/callback') async loginCallback( - @Query("state") state: string, - @Query("code") code: string, + @Query('state') state: string, + @Query('code') code: string, @Session() session: Record, - @Res() response: Response) { - const stateBefore = session["sso_state"]; + @Res() response: IAuth.Response, + ) { + const stateBefore = session['sso_state']; if (!stateBefore || stateBefore != state) { - response.redirect("/error/invalid-login"); + response.redirect('/error/invalid-login'); } - const ssoProfile: SSOUser = await this.ssoClient.get_user_info(code); + const ssoProfile: ESSOUser.SSOUser = await this.ssoClient.get_user_info( + code, + ); const { accessToken, accessTokenOptions, refreshToken, - refreshTokenOptions + refreshTokenOptions, } = await this.authService.ssoLogin(ssoProfile); - response.cookie("accessToken", accessToken, accessTokenOptions); - response.cookie("refreshToken", refreshToken, refreshTokenOptions); + response.cookie('accessToken', accessToken, accessTokenOptions); + response.cookie('refreshToken', refreshToken, refreshTokenOptions); /* @Todo call import_student_lectures(studentId) */ - /* - @Todo - save refreshToken in session_userprofile - */ - const next_url = session["next"] ?? "/"; + const next_url = session['next'] ?? '/'; response.redirect(next_url); } - @Get('info') - async getUserProfile(@GetUser() user: session_userprofile) { + async getUserProfile( + @GetUser() user: session_userprofile, + ): Promise { /* @Todo implement userSerializer, before that, we'd like to architect the dto types */ - return user; + const profile = await this.userService.getProfile(user); + return profile; + } + + @Public() + @Get('/') + async home(@Req() req: IAuth.Request, @Res() res: IAuth.Response) { + return res.redirect('/session/login'); + } + + @Public() + @Get('logout') + async logout( + @Req() req: IAuth.Request, + @Res() res: IAuth.Response, + @Query('next') next: string, + @GetUser() user: session_userprofile, + ) { + const webURL = process.env.WEB_URL; + if (user) { + const sid = user.sid; + const protocol = req.protocol; + const host = req.get('host'); + const originalUrl = req.originalUrl; + const absoluteUrl = `${protocol}://${host}${originalUrl}`; + const logoutUrl = this.ssoClient.get_logout_url(sid, absoluteUrl); + + res.clearCookie('accessToken', { path: '/', maxAge: 0, httpOnly: true }); + res.clearCookie('refreshToken', { path: '/', maxAge: 0, httpOnly: true }); + + return res.redirect(logoutUrl); + } + + return res.redirect(webURL + '/'); } } diff --git a/src/modules/auth/auth.module.ts b/src/modules/auth/auth.module.ts index 27bfef0f..e65e0f3a 100644 --- a/src/modules/auth/auth.module.ts +++ b/src/modules/auth/auth.module.ts @@ -1,13 +1,17 @@ -import { PrismaModule } from "../../prisma/prisma.module"; -import { Module } from "@nestjs/common"; -import { AuthController } from "./auth.controller"; -import { AuthService } from "./auth.service"; -import { JwtCookieStrategy } from "./strategy/jwt-cookie.strategy"; -import { UserRepository } from "../../prisma/repositories/user.repository"; -import { JwtModule } from "@nestjs/jwt"; -import settings from "../../settings"; -import { UserService } from "../user/user.service"; -import { PassportModule } from "@nestjs/passport"; +import { Module } from '@nestjs/common'; +import { JwtModule } from '@nestjs/jwt'; +import { PassportModule } from '@nestjs/passport'; +import { PrismaModule } from '../../prisma/prisma.module'; +import { UserRepository } from '../../prisma/repositories/user.repository'; +import { UserService } from '../user/user.service'; +import { AuthController } from './auth.controller'; +import { AuthService } from './auth.service'; +import { JwtCookieStrategy } from './strategy/jwt-cookie.strategy'; +import { AuthChain } from './auth.chain'; +import { IsPublicCommand } from './command/isPublic.command'; +import { JwtCommand } from './command/jwt.command'; +import { SidCommand } from './command/sid.command'; +import { AuthConfig } from './auth.config'; @Module({ imports: [ @@ -21,7 +25,12 @@ import { PassportModule } from "@nestjs/passport"; JwtCookieStrategy, UserService, UserRepository, + AuthChain, + IsPublicCommand, + JwtCommand, + SidCommand, + AuthConfig, ], - exports: [AuthService], + exports: [AuthService, AuthConfig, AuthChain], }) export class AuthModule {} diff --git a/src/modules/auth/auth.service.ts b/src/modules/auth/auth.service.ts index 165bdaee..a6849510 100644 --- a/src/modules/auth/auth.service.ts +++ b/src/modules/auth/auth.service.ts @@ -1,131 +1,137 @@ -import { PrismaService } from "../../prisma/prisma.service"; -import { Injectable } from "@nestjs/common"; -import { UserRepository } from "../../prisma/repositories/user.repository"; -import { Prisma, session_userprofile } from "@prisma/client"; -import { JwtService } from "@nestjs/jwt"; -import settings from "../../settings"; -import * as bcrypt from "bcrypt"; -import session from "express-session"; -import { SSOUser } from "../../common/interfaces/dto/auth/sso.dto"; -import { import_student_lectures } from "../../common/scholarDB/scripts"; +import { Injectable } from '@nestjs/common'; +import { JwtService } from '@nestjs/jwt'; +import { Prisma, session_userprofile } from '@prisma/client'; +import * as bcrypt from 'bcrypt'; +import { ESSOUser } from 'src/common/entities/ESSOUser'; +import { import_student_lectures } from '../../common/scholarDB/scripts'; +import { UserRepository } from '../../prisma/repositories/user.repository'; +import settings from '../../settings'; @Injectable() -export class AuthService{ - constructor( - private readonly userRepository: UserRepository, - private readonly jwtService: JwtService, - ){} - - public async findBySid(sid: string){ - return this.userRepository.findBySid(sid); +export class AuthService { + constructor( + private readonly userRepository: UserRepository, + private readonly jwtService: JwtService, + ) {} + + public async findBySid(sid: string) { + return this.userRepository.findBySid(sid); + } + + public async ssoLogin(ssoProfile: ESSOUser.SSOUser) { + const sid = ssoProfile.sid; + let user = await this.findBySid(sid); + + const kaistInfo = ssoProfile.kaist_info; + const studentId = kaistInfo.ku_std_no ?? ''; + + const { accessToken, ...accessTokenOptions } = + this.getCookieWithAccessToken(sid); + const { refreshToken, ...refreshTokenOptions } = + this.getCookieWithRefreshToken(sid); + + const salt = await bcrypt.genSalt(Number(process.env.saltRounds)); + const encryptedRefreshToken = await bcrypt.hash(refreshToken, salt); + + if (!user) { + user = await this.createUser( + sid, + ssoProfile['email'], + studentId, + ssoProfile['first_name'], + ssoProfile['last_name'], + encryptedRefreshToken, + ); + } else { + if (user.student_id != studentId) { + await import_student_lectures(studentId); + } + + const updateData = { + first_name: ssoProfile['first_name'], + last_name: ssoProfile['last_name'], + student_id: studentId, + refresh_token: encryptedRefreshToken, + }; + user = await this.updateUser(user.id, updateData); } - public async ssoLogin(ssoProfile: SSOUser){ - const sid = ssoProfile.sid; - let user = await this.findBySid(sid); - - const kaistInfo = ssoProfile.kaist_info; - const studentId = kaistInfo.ku_std_no ?? ''; - - - const { accessToken, ...accessTokenOptions } = - this.getCookieWithAccessToken(sid); - const { refreshToken, ...refreshTokenOptions } = - this.getCookieWithRefreshToken(sid); - - const salt = await bcrypt.genSalt(Number(process.env.saltRounds)); - const encryptedRefreshToken = await bcrypt.hash(refreshToken, salt); - - if (!user) { - user = await this.createUser( - sid, - ssoProfile['email'], - studentId, - ssoProfile['first_name'], - ssoProfile['last_name'], - encryptedRefreshToken, - ); - } else { - if (user.student_id != studentId) { - await import_student_lectures(studentId); - } - - const updateData = { - first_name: ssoProfile['first_name'], - last_name: ssoProfile['last_name'], - student_id: studentId, - refresh_token: encryptedRefreshToken - }; - user = await this.updateUser(user.id, updateData); - } - - - return { - accessToken, - accessTokenOptions, - refreshToken, - refreshTokenOptions, - } - - } - - public getCookieWithToken(sid: string){ - - } - - public getCookieWithAccessToken(sid: string) { - const payload = { - sid: sid - }; - - const jwtConfig = settings().getJwtConfig(); - const token = this.jwtService.sign(payload, { - secret: jwtConfig.secret, - expiresIn: jwtConfig.signOptions.expiresIn + 's', - }); - return { - accessToken: token, - path: '/', - httpOnly: true, - maxAge: Number(jwtConfig.signOptions.expiresIn) * 1000, - }; - } - - public getCookieWithRefreshToken(sid: string) { - const payload = { - sid: sid - }; - - const jwtConfig = settings().getJwtConfig(); - const refreshToken = this.jwtService.sign(payload, { - secret: jwtConfig.secret, - expiresIn: jwtConfig.signOptions.refreshExpiresIn + 's', - }); - return { - refreshToken: refreshToken, - path: '/', - httpOnly: true, - maxAge: Number(jwtConfig.signOptions.refreshExpiresIn) * 1000, - }; - } - - - - - async createUser(sid:string, email:string, studentId: string, firstName: string, lastName: string, refreshToken: string): Promise { - const user = { - sid: sid, - email: email, - first_name: firstName, - last_name: lastName, - date_joined: new Date(), - student_id: studentId, - refreshToken: refreshToken - } - return await this.userRepository.createUser(user) - } - - async updateUser(userId, user: Prisma.session_userprofileUpdateInput): Promise{ - return await this.userRepository.updateUser(userId, user); - } -} \ No newline at end of file + return { + accessToken, + accessTokenOptions, + refreshToken, + refreshTokenOptions, + }; + } + + public getCookieWithToken( + sid: string, + ) {} + + public getCookieWithAccessToken(sid: string) { + const payload = { + sid: sid, + }; + + const jwtConfig = settings().getJwtConfig(); + const token = this.jwtService.sign(payload, { + secret: jwtConfig.secret, + expiresIn: jwtConfig.signOptions.expiresIn + 's', + }); + return { + accessToken: token, + path: '/', + httpOnly: true, + sameSite: 'none' as const, + maxAge: Number(jwtConfig.signOptions.expiresIn) * 1000, + secure: true, + }; + } + + public getCookieWithRefreshToken(sid: string) { + const payload = { + sid: sid, + }; + + const jwtConfig = settings().getJwtConfig(); + const refreshToken = this.jwtService.sign(payload, { + secret: jwtConfig.secret, + expiresIn: jwtConfig.signOptions.refreshExpiresIn + 's', + }); + return { + refreshToken: refreshToken, + path: '/', + httpOnly: true, + sameSite: 'none' as const, + maxAge: Number(jwtConfig.signOptions.refreshExpiresIn) * 1000, + secure: true, + }; + } + + async createUser( + sid: string, + email: string, + studentId: string, + firstName: string, + lastName: string, + refreshToken: string, + ): Promise { + const user = { + sid: sid, + email: email, + first_name: firstName, + last_name: lastName, + date_joined: new Date(), + student_id: studentId, + refresh_token: refreshToken, + }; + return await this.userRepository.createUser(user); + } + + async updateUser( + userId: number, + user: Prisma.session_userprofileUpdateInput, + ): Promise { + return await this.userRepository.updateUser(userId, user); + } +} diff --git a/src/modules/auth/command/isPublic.command.ts b/src/modules/auth/command/isPublic.command.ts new file mode 100644 index 00000000..ca958964 --- /dev/null +++ b/src/modules/auth/command/isPublic.command.ts @@ -0,0 +1,25 @@ +import { ExecutionContext, Injectable } from '@nestjs/common'; +import { IS_PUBLIC_KEY } from '../../../common/decorators/skip-auth.decorator'; +import { Reflector } from '@nestjs/core'; +import { AuthCommand, AuthResult } from '../auth.command'; + +@Injectable() +export class IsPublicCommand implements AuthCommand { + constructor(private reflector: Reflector) {} + + public next( + context: ExecutionContext, + prevResult: AuthResult, + ): Promise { + const isPublic = this.reflector.getAllAndOverride(IS_PUBLIC_KEY, [ + context.getHandler(), + context.getClass(), + ]); + + if (isPublic) { + prevResult.isPublic = true; + return Promise.resolve(prevResult); + } + return Promise.resolve(prevResult); + } +} diff --git a/src/modules/auth/command/jwt.command.ts b/src/modules/auth/command/jwt.command.ts new file mode 100644 index 00000000..92b48e1c --- /dev/null +++ b/src/modules/auth/command/jwt.command.ts @@ -0,0 +1,98 @@ +import { AuthChain } from '../auth.chain'; +import { + ExecutionContext, + Injectable, + InternalServerErrorException, + NotFoundException, + UnauthorizedException, +} from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { Request } from 'express'; +import settings from '../../../settings'; +import * as bcrypt from 'bcrypt'; +import { AuthService } from '../auth.service'; +import { JwtService } from '@nestjs/jwt'; +import { AuthCommand, AuthResult } from '../auth.command'; + +@Injectable() +export class JwtCommand implements AuthCommand { + constructor( + private reflector: Reflector, + private authService: AuthService, + private jwtService: JwtService, + ) {} + + public async next( + context: ExecutionContext, + prevResult: AuthResult, + ): Promise { + const request = context.switchToHttp().getRequest(); + const response = context.switchToHttp().getResponse(); + const accessToken = this.extractTokenFromCookie(request, 'accessToken'); + + try { + if (!accessToken) throw new Error('jwt expired'); + const payload = await this.jwtService.verify(accessToken, { + secret: settings().getJwtConfig().secret, + ignoreExpiration: false, + }); + const user = this.authService.findBySid(payload.sid); + request['user'] = user; + prevResult.authentication = true; + prevResult.authorization = true; + return prevResult; + } catch (e: any) { + if (e.message === 'jwt expired') { + try { + const refreshToken = this.extractTokenFromCookie( + request, + 'refreshToken', + ); + if (!refreshToken) throw new UnauthorizedException(); + const payload = await this.jwtService.verify(refreshToken, { + secret: settings().getJwtConfig().secret, + ignoreExpiration: false, + }); + const user = await this.authService.findBySid(payload.sid); + if (!user) { + throw new NotFoundException('user is not found'); + } + if ( + user.refresh_token && + (await bcrypt.compare(refreshToken, user.refresh_token)) + ) { + const { accessToken, ...accessTokenOptions } = + this.authService.getCookieWithAccessToken(payload.sid); + + if (!request.res) { + throw new InternalServerErrorException( + 'res property is not found in request', + ); + } + request.res.cookie('accessToken', accessToken, accessTokenOptions); + request['user'] = user; + prevResult.authentication = true; + prevResult.authorization = true; + return prevResult; + } + return prevResult; + } catch (e) { + // console.error(e); + return prevResult; + } + } + return prevResult; + } + } + + private extractTokenFromCookie( + request: Request, + type: 'accessToken' | 'refreshToken', + ): string | undefined { + const cookie = request.cookies[type]; + if (cookie) { + return cookie; + } + return undefined; + } +} diff --git a/src/modules/auth/command/sid.command.ts b/src/modules/auth/command/sid.command.ts new file mode 100644 index 00000000..952e70e8 --- /dev/null +++ b/src/modules/auth/command/sid.command.ts @@ -0,0 +1,39 @@ +import { AuthChain } from '../auth.chain'; +import { Reflector } from '@nestjs/core'; +import { + ExecutionContext, + Injectable, + NotFoundException, +} from '@nestjs/common'; +import { Request } from 'express'; +import { AuthService } from '../auth.service'; +import { AuthCommand, AuthResult } from '../auth.command'; + +@Injectable() +export class SidCommand implements AuthCommand { + constructor( + private reflector: Reflector, + private readonly authService: AuthService, + ) {} + + public async next( + context: ExecutionContext, + prevResult: AuthResult, + ): Promise { + const request = context.switchToHttp().getRequest(); + const response = context.switchToHttp().getResponse(); + const sid = request.cookies['auth-cookie']; + if (sid) { + const user = await this.authService.findBySid(sid); + if (!user) { + return Promise.resolve(prevResult); + } + request['user'] = user; + prevResult.authentication = true; + prevResult.authorization = true; + return Promise.resolve(prevResult); + } else { + return Promise.resolve(prevResult); + } + } +} diff --git a/src/modules/auth/guard/auth.guard.ts b/src/modules/auth/guard/auth.guard.ts new file mode 100644 index 00000000..b0af20be --- /dev/null +++ b/src/modules/auth/guard/auth.guard.ts @@ -0,0 +1,10 @@ +import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; +import { AuthChain } from '../auth.chain'; + +export class AuthGuard implements CanActivate { + constructor(private readonly authChain: AuthChain) {} + + async canActivate(context: ExecutionContext) { + return this.authChain.execute(context); + } +} diff --git a/src/modules/auth/guard/jwt-cookie.guard.ts b/src/modules/auth/guard/jwt-cookie.guard.ts index fdd96ed3..360999f4 100644 --- a/src/modules/auth/guard/jwt-cookie.guard.ts +++ b/src/modules/auth/guard/jwt-cookie.guard.ts @@ -1,22 +1,22 @@ -import { AuthGuard } from "@nestjs/passport"; -import { ExecutionContext, Injectable } from "@nestjs/common"; -import { Reflector } from "@nestjs/core"; -import { IS_PUBLIC_KEY } from "../../../common/decorators/skip-auth.decorator"; -import { Request } from "express"; -import { AuthService } from "../auth.service"; +import { ExecutionContext, Injectable } from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { AuthGuard } from '@nestjs/passport'; +import { IS_PUBLIC_KEY } from '../../../common/decorators/skip-auth.decorator'; +import { AuthService } from '../auth.service'; @Injectable() -export class JwtCookieGuard extends AuthGuard("jwt-cookie") { +export class JwtCookieGuard extends AuthGuard('jwt-cookie') { constructor( private reflector: Reflector, - private readonly authService: AuthService) { + private readonly authService: AuthService, + ) { super(); } canActivate(context: ExecutionContext) { const isPublic = this.reflector.getAllAndOverride(IS_PUBLIC_KEY, [ context.getHandler(), - context.getClass() + context.getClass(), ]); if (isPublic) { return true; diff --git a/src/modules/auth/guard/mock-auth-guard.ts b/src/modules/auth/guard/mock-auth-guard.ts index 4a823c2c..f28c6817 100644 --- a/src/modules/auth/guard/mock-auth-guard.ts +++ b/src/modules/auth/guard/mock-auth-guard.ts @@ -1,81 +1,121 @@ -import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from "@nestjs/common"; -import { Reflector } from "@nestjs/core"; -import { IS_PUBLIC_KEY } from "../../../common/decorators/skip-auth.decorator"; -import { Request } from "express"; -import * as bcrypt from "bcrypt"; -import { AuthService } from "../auth.service"; -import { JwtService } from "@nestjs/jwt"; -import settings from "../../../settings"; +import { + CanActivate, + ExecutionContext, + Injectable, + InternalServerErrorException, + NotFoundException, + UnauthorizedException, +} from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { JwtService } from '@nestjs/jwt'; +import * as bcrypt from 'bcrypt'; +import { Request } from 'express'; +import { IS_PUBLIC_KEY } from '../../../common/decorators/skip-auth.decorator'; +import settings from '../../../settings'; +import { AuthService } from '../auth.service'; @Injectable() -export class MockAuthGuard implements CanActivate{ +export class MockAuthGuard implements CanActivate { constructor( private reflector: Reflector, private readonly authService: AuthService, - private jwtService: JwtService - ) { - } + private jwtService: JwtService, + ) {} async canActivate(context: ExecutionContext) { - const isPublic = this.reflector.getAllAndOverride(IS_PUBLIC_KEY, [ - context.getHandler(), - context.getClass() - ]); - - if (isPublic) { - return true; - } - const request = context.switchToHttp().getRequest(); const response = context.switchToHttp().getResponse(); - const sid = request.cookies["auth-cookie"]; + const sid = request.cookies['auth-cookie']; if (sid) { const user = await this.authService.findBySid(sid); - request["user"] = user; - return true; - }else{ - const accessToken = this.extractTokenFromCookie(request, "accessToken"); + if (!user) { + throw new NotFoundException('user is not found'); + } + + request['user'] = user; + return this.determineAuth(context, true); + } else { + const accessToken = this.extractTokenFromCookie(request, 'accessToken'); + try { - if (!accessToken) throw new Error("jwt expired"); - const payload = await this.jwtService.verify( - accessToken, - { - secret: settings().getJwtConfig().secret - } - ); - console.log("payload", payload); + if (!accessToken) throw new Error('jwt expired'); + const payload = await this.jwtService.verify(accessToken, { + secret: settings().getJwtConfig().secret, + ignoreExpiration: false, + }); const user = this.authService.findBySid(payload.sid); - request["user"] = user; + request['user'] = user; return true; - } catch (e) { - if (e.message === "jwt expired") { - const refreshToken = this.extractTokenFromCookie(request, "refreshToken"); - if (!refreshToken) throw new UnauthorizedException(); + } catch (e: any) { + if (e.message === 'jwt expired') { try { - const payload = await this.jwtService.verify( - refreshToken, - { - secret: settings().getJwtConfig().secret - } + const refreshToken = this.extractTokenFromCookie( + request, + 'refreshToken', ); + if (!refreshToken) throw new UnauthorizedException(); + const payload = await this.jwtService.verify(refreshToken, { + secret: settings().getJwtConfig().secret, + ignoreExpiration: false, + }); const user = await this.authService.findBySid(payload.sid); - if (await bcrypt.compare(refreshToken, user.refresh_token)) { - const { accessToken, ...accessTokenOptions } = this.authService.getCookieWithAccessToken(payload.sid); - request.res.cookie("accessToken", accessToken, accessTokenOptions); - request["user"] = user; - return true; + if (!user) { + throw new NotFoundException('user is not found'); } - return false; + if ( + user.refresh_token && + (await bcrypt.compare(refreshToken, user.refresh_token)) + ) { + const { accessToken, ...accessTokenOptions } = + this.authService.getCookieWithAccessToken(payload.sid); + + if (!request.res) { + throw new InternalServerErrorException( + 'res property is not found in request', + ); + } + request.res.cookie( + 'accessToken', + accessToken, + accessTokenOptions, + ); + request['user'] = user; + return this.determineAuth(context, true); + } + return this.determineAuth(context, false); } catch (e) { + const result = this.determineAuth(context, false); + if (result) { + return result; + } throw new UnauthorizedException(); } } + const result = this.determineAuth(context, false); + if (result) { + return result; + } throw new UnauthorizedException(); } } } - private extractTokenFromCookie(request: Request, type: "accessToken" | "refreshToken"): string | undefined { + private determineAuth(context: ExecutionContext, result: boolean): boolean { + const isPublic = this.reflector.getAllAndOverride(IS_PUBLIC_KEY, [ + context.getHandler(), + context.getClass(), + ]); + + if (isPublic) { + return true; + } + return result; + } + + private extractTokenFromCookie( + request: Request, + type: 'accessToken' | 'refreshToken', + ): string | undefined { const cookie = request.cookies[type]; if (cookie) { return cookie; diff --git a/src/modules/auth/strategy/jwt-cookie.strategy.ts b/src/modules/auth/strategy/jwt-cookie.strategy.ts index 74460201..e2892a2b 100644 --- a/src/modules/auth/strategy/jwt-cookie.strategy.ts +++ b/src/modules/auth/strategy/jwt-cookie.strategy.ts @@ -1,10 +1,9 @@ -import { Injectable, Logger, UnauthorizedException } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { PassportStrategy } from '@nestjs/passport'; -import { AuthService } from '../auth.service'; import { ExtractJwt, Strategy } from 'passport-jwt'; -import { UserService } from "../../user/user.service"; -import settings from "../../../settings"; - +import { IAuth } from 'src/common/interfaces'; +import settings from '../../../settings'; +import { AuthService } from '../auth.service'; /* @@ -17,9 +16,7 @@ export class JwtCookieStrategy extends PassportStrategy( Strategy, 'jwt-cookie', ) { - constructor( - private readonly authService: AuthService, - ) { + constructor(private readonly authService: AuthService) { super({ secretOrKey: settings().getJwtConfig().secret, ignoreExpiration: false, @@ -31,8 +28,7 @@ export class JwtCookieStrategy extends PassportStrategy( }); } - async validate(payload) { - console.log(payload); + async validate(payload: IAuth.JwtPayload) { return this.authService.findBySid(payload.sid); } } diff --git a/src/modules/auth/utils/sparcs-sso.ts b/src/modules/auth/utils/sparcs-sso.ts index 9da89446..ce2019ee 100644 --- a/src/modules/auth/utils/sparcs-sso.ts +++ b/src/modules/auth/utils/sparcs-sso.ts @@ -1,7 +1,7 @@ +import axios, { AxiosResponse } from 'axios'; import * as crypto from 'crypto'; import * as querystring from 'querystring'; -import axios, { AxiosResponse } from 'axios'; -import { KaistInfo, SSOUser } from "../../../common/interfaces/dto/auth/sso.dto"; +import { ESSOUser } from 'src/common/entities/ESSOUser'; // CONVERT SPARCS SSO V2 Client Version 1.1 TO TYPESCRIPT // VALID ONLY AFTER ----(NOT VALID) ---- @@ -44,8 +44,6 @@ export class Client { :param secret_key: your secret key :param is_beta: true iff you want to use SPARCS SSO beta server :param server_addr: SPARCS SSO server addr (only for testing)*/ - console.log(is_beta) - console.log(is_beta ? true : false); this.DOMAIN = is_beta ? this.BETA_DOMAIN : this.SERVER_DOMAIN; this.DOMAIN = server_addr || this.DOMAIN; @@ -96,7 +94,7 @@ export class Client { return true; } - private async _post_data(url: any, data: any): Promise { + private async _post_data(url: any, data: any): Promise { /** *@SSO *querystring.stringify(data)인지 .toString('utf8')붙여야 하는지 확인 필요 @@ -114,17 +112,18 @@ export class Client { throw new Error('UNKNOWN_ERROR'); } - const result = r.data - console.log(result) - result.kaist_info = result.kaist_info ? JSON.parse(result.kaist_info) : {} - return result as SSOUser; + const result = r.data; + result.kaist_info = result.kaist_info + ? JSON.parse(result.kaist_info) + : {}; + return result as ESSOUser.SSOUser; } catch (e) { console.error(e); throw new Error('INVALID_OBJECT'); } } - public get_login_params(): {url: string, state: string} { + public get_login_params(request_url: string): { url: string; state: string } { /* Get login parameters for SPARCS SSO login :returns: [url, state] where url is a url to redirect user, @@ -135,18 +134,27 @@ export class Client { * randomBytes에 10? 5? 둘중 어떤걸 넘겨줄지. gpt는 5라고 하고 파이썬은 token_hex(10) 10 같긴 한데....혹시나 해서 */ const state: string = crypto.randomBytes(10).toString('hex'); - const params: Params = { client_id: this.client_id, state: state }; - console.log(this.client_id) - console.log(state); - console.log(this.URLS['token_require']) + const allowedPreferredUris: { [key: string]: string } = { + 'otl.sparcs.org': 'https://otl.sparcs.org/session/login/callback/', + 'otl.kaist.ac.kr': 'https://otl.kaist.ac.kr/session/login/callback/', + 'otl-stage.sparcsandbox.com': + 'https://otl-stage.sparcsandbox.com/session/login/callback/', + }; + const preferred_url = + allowedPreferredUris[request_url] || + 'https://otl.sparcs.org/session/login/callback/'; + const params: Params = { + client_id: this.client_id, + state: state, + preferred_url: preferred_url, + }; const url: string = `${this.URLS['token_require']}?${querystring.stringify( params, )}`; - console.log('url',url) - return {url, state} + return { url, state }; } - public async get_user_info(code: string): Promise { + public async get_user_info(code: string): Promise { /* Exchange a code to user information :param code: the code that given by SPARCS SSO server @@ -183,7 +191,6 @@ export class Client { return `${this.URLS['logout']}?${querystring.stringify(params)}`; } - public async get_notice( offset: number = 0, limit: number = 3, diff --git a/src/modules/courses/courses.controller.ts b/src/modules/courses/courses.controller.ts new file mode 100644 index 00000000..99a56b2a --- /dev/null +++ b/src/modules/courses/courses.controller.ts @@ -0,0 +1,73 @@ +import { + BadRequestException, + Controller, + Get, + Param, + Post, + Query, +} from '@nestjs/common'; +import { session_userprofile } from '@prisma/client'; +import { GetUser } from 'src/common/decorators/get-user.decorator'; +import { Public } from 'src/common/decorators/skip-auth.decorator'; +import { ICourse } from 'src/common/interfaces'; +import { CoursesService } from './courses.service'; +import { CourseIdPipe } from '@src/common/pipe/courseId.pipe'; +import LectureQueryDto = ICourse.LectureQueryDto; + +@Controller('api/courses') +export class CourseController { + constructor(private readonly coursesService: CoursesService) {} + + @Public() + @Get() + async getCourses( + @Query() query: ICourse.Query, + @GetUser() user: session_userprofile, + ) { + const courses = await this.coursesService.getCourses(query, user); + return courses; + } + + @Public() + @Get('autocomplete') + async getCourseAutocomplete(@Query() query: ICourse.AutocompleteQueryDto) { + return await this.coursesService.getCourseAutocomplete(query); + } + + @Public() + @Get(':id') + async getCourseById( + @Param('id', CourseIdPipe) id: number, + @GetUser() user: session_userprofile, + ) { + if (isNaN(id)) throw new BadRequestException('Invalid course id'); + return await this.coursesService.getCourseById(id, user); + } + + @Public() + @Get(':id/lectures') + async getLecturesByCourseId( + @Query() query: LectureQueryDto, + @Param('id', CourseIdPipe) id: number, + ) { + return await this.coursesService.getLecturesByCourseId(query, id); + } + + @Public() + @Get(':id/reviews') + async getReviewByCourseId( + @Query() query: ICourse.ReviewQueryDto, + @Param('id', CourseIdPipe) id: number, + @GetUser() user: session_userprofile, + ) { + return await this.coursesService.getReviewsByCourseId(query, id, user); + } + + @Post(':id/read') + async readCourse( + @Param('id', CourseIdPipe) id: number, + @GetUser() user: session_userprofile, + ) { + await this.coursesService.readCourse(user.id, id); + } +} diff --git a/src/modules/courses/courses.module.ts b/src/modules/courses/courses.module.ts new file mode 100644 index 00000000..877b9e23 --- /dev/null +++ b/src/modules/courses/courses.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { PrismaModule } from 'src/prisma/prisma.module'; +import { CourseController } from './courses.controller'; +import { CoursesService } from './courses.service'; + +@Module({ + imports: [PrismaModule], + controllers: [CourseController], + providers: [CoursesService], +}) +export class CoursesModule {} diff --git a/src/modules/courses/courses.service.ts b/src/modules/courses/courses.service.ts new file mode 100644 index 00000000..c931145b --- /dev/null +++ b/src/modules/courses/courses.service.ts @@ -0,0 +1,148 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { session_userprofile } from '@prisma/client'; +import { ECourse } from 'src/common/entities/ECourse'; +import { ICourse } from 'src/common/interfaces'; +import { toJsonLectureDetail } from 'src/common/interfaces/serializer/lecture.serializer'; +import { toJsonReview } from 'src/common/interfaces/serializer/review.serializer'; +import { + addIsRead, + toJsonCourseDetail, +} from '../../common/interfaces/serializer/course.serializer'; +import { getRepresentativeLecture } from '../../common/utils/lecture.utils'; +import { CourseRepository } from './../../prisma/repositories/course.repository'; +import { Transactional } from '@nestjs-cls/transactional'; +import LectureQueryDto = ICourse.LectureQueryDto; + +@Injectable() +export class CoursesService { + constructor(private readonly courseRepository: CourseRepository) {} + + @Transactional() + public async getCourses( + query: ICourse.Query, + user: session_userprofile, + ): Promise { + const queryResult = await this.courseRepository.getCourses(query); + return Promise.all( + queryResult.map(async (course) => { + const representativeLecture = getRepresentativeLecture(course.lecture); + const professorRaw = course.subject_course_professors.map( + (x) => x.professor, + ); + const result = toJsonCourseDetail( + course, + representativeLecture, + professorRaw, + ); + + const userspecific_is_read = user + ? await this.courseRepository.isUserSpecificRead(course.id, user.id) + : false; + + return addIsRead(result, userspecific_is_read); + }), + ); + } + + @Transactional() + public async getCourseByIds(ids: number[], user: session_userprofile) { + return Promise.all( + ids.map((id) => this.courseRepository.getCourseById(id)), + ); + } + + @Transactional() + public async getCourseById(id: number, user: session_userprofile) { + const course = await this.courseRepository.getCourseById(id); + if (!course) { + throw new NotFoundException(); + } + const representativeLecture = getRepresentativeLecture(course.lecture); + const professorRaw = course.subject_course_professors.map( + (x) => x.professor, + ); + const result = toJsonCourseDetail( + course, + representativeLecture, + professorRaw, + ); + + const userspecific_is_read = user + ? await this.courseRepository.isUserSpecificRead(course.id, user.id) + : false; + + return addIsRead(result, userspecific_is_read); + } + + public async getLecturesByCourseId(query: LectureQueryDto, id: number) { + const lectures = await this.courseRepository.getLecturesByCourseId( + query, + id, + ); + if (!lectures) { + throw new NotFoundException(); + } + + return lectures.map((lecture) => toJsonLectureDetail(lecture)); + } + + public async getReviewsByCourseId( + query: ICourse.ReviewQueryDto, + id: number, + user: session_userprofile, + ) { + query.limit = query.limit ?? 100; + query.offset = query.offset ?? 0; + query.order = query.order ?? [ + '-lecture__year', + '-lecture__semester', + '-written_datetime', + '-id', + ]; + const reviews = await this.courseRepository.getReviewsByCourseId(query, id); + if (!reviews) { + throw new NotFoundException(); + } + + return reviews.map((review) => toJsonReview(review, user)); + } + + async getCourseAutocomplete(dto: ICourse.AutocompleteQueryDto) { + const candidate = await this.courseRepository.getCourseAutocomplete(dto); + if (!candidate) return dto.keyword; + return this.findAutocompleteFromCandidate(candidate, dto.keyword); + } + + private findAutocompleteFromCandidate( + candidate: ECourse.Extended, + keyword: string, + ) { + const keywordLower = keyword.toLowerCase(); + if (candidate.subject_department.name.startsWith(keyword)) + return candidate.subject_department.name; + if ( + candidate.subject_department.name_en + ?.toLowerCase() + .startsWith(keywordLower) + ) + return candidate.subject_department.name_en; + if (candidate.title.startsWith(keyword)) return candidate.title; + if (candidate.title_en.toLowerCase().startsWith(keywordLower)) + return candidate.title_en; + for (const professor of candidate.subject_course_professors) { + if (professor.professor.professor_name.startsWith(keyword)) + return professor.professor.professor_name; + if ( + professor.professor.professor_name_en + ?.toLowerCase() + .startsWith(keywordLower) + ) + return professor.professor.professor_name_en; + } + } + + @Transactional() + async readCourse(userId: number, courseId: number) { + await this.courseRepository.readCourse(userId, courseId); + } +} diff --git a/src/modules/departments/departments.module.ts b/src/modules/departments/departments.module.ts new file mode 100644 index 00000000..02164bdc --- /dev/null +++ b/src/modules/departments/departments.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { PrismaModule } from 'src/prisma/prisma.module'; +import { DepartmentsService } from './departments.service'; + +@Module({ + imports: [PrismaModule], + providers: [DepartmentsService], + exports: [DepartmentsService], +}) +export class DepartmentsModule {} diff --git a/src/modules/departments/departments.service.ts b/src/modules/departments/departments.service.ts new file mode 100644 index 00000000..c6ea9f3e --- /dev/null +++ b/src/modules/departments/departments.service.ts @@ -0,0 +1,79 @@ +import { Injectable } from '@nestjs/common'; +import { EDepartment } from 'src/common/entities/EDepartment'; +import { DepartmentRepository } from 'src/prisma/repositories/department.repository'; + +const UNDERGRADUATE_DEPARTMENTS = [ + 'CE', + 'MSB', + 'ME', + 'PH', + 'BiS', + 'IE', + 'ID', + 'BS', + 'CBE', + 'MAS', + 'MS', + 'NQE', + 'HSS', + 'EE', + 'CS', + 'AE', + 'CH', + 'TS', +]; + +const EXCLUDED_DEPARTMENTS = [ + 'AA', + 'KSA', + 'URP', + 'ED', + 'INT', + 'KJ', + 'CWENA', + 'C', + 'E', + 'S', + 'PSY', + 'SK', + 'BIO', + 'CLT', + 'PHYS', +]; + +@Injectable() +export class DepartmentsService { + constructor(private readonly departmentRepository: DepartmentRepository) {} + async getDepartmentOptions() { + const yearThreshold = new Date().getFullYear() - 2; + const [departments, recentDepartmentCodes] = await Promise.all([ + this.departmentRepository.getAllDepartmentOptions(EXCLUDED_DEPARTMENTS), + this.departmentRepository.getDepartmentCodesOfRecentLectures( + yearThreshold, + ), + ]); + + if (recentDepartmentCodes.length === 0) + console.error( + 'recentDepartmentCodes is empty, which indicates something is wrong', + ); + + const result = { + undergraduate: [] as EDepartment.Basic[], + recent: [] as EDepartment.Basic[], + other: [] as EDepartment.Basic[], + }; + + departments.forEach((department) => { + if (UNDERGRADUATE_DEPARTMENTS.includes(department.code)) { + result.undergraduate.push(department); + } else if (recentDepartmentCodes.includes(department.code)) { + result.recent.push(department); + } else { + result.other.push(department); + } + }); + + return result; + } +} diff --git a/src/modules/feeds/feeds.controller.ts b/src/modules/feeds/feeds.controller.ts new file mode 100644 index 00000000..5f3b57f3 --- /dev/null +++ b/src/modules/feeds/feeds.controller.ts @@ -0,0 +1,20 @@ +import { Controller, Get, Query } from '@nestjs/common'; +import { session_userprofile } from '@prisma/client'; +import { GetUser } from 'src/common/decorators/get-user.decorator'; +import { IFeed } from 'src/common/interfaces/IFeed'; +import { toJsonFeedDetails } from 'src/common/interfaces/serializer/feeds.serializer'; +import { FeedsService } from './feeds.service'; + +@Controller('api/users/:userId/feeds') +export class FeedsController { + constructor(private readonly feedsService: FeedsService) {} + + @Get() + async getUserFeeds( + @Query() query: IFeed.QueryDto, + @GetUser() user: session_userprofile, + ): Promise { + const feeds = await this.feedsService.getFeeds(query, user); + return feeds.map((feed) => toJsonFeedDetails(feed, user)); + } +} diff --git a/src/modules/feeds/feeds.module.ts b/src/modules/feeds/feeds.module.ts new file mode 100644 index 00000000..c7538460 --- /dev/null +++ b/src/modules/feeds/feeds.module.ts @@ -0,0 +1,13 @@ +import { Module } from '@nestjs/common'; +import { PrismaModule } from 'src/prisma/prisma.module'; +import { FeedsRepository } from 'src/prisma/repositories/feeds.repository'; +import { FeedsController } from './feeds.controller'; +import { FeedsService } from './feeds.service'; + +@Module({ + imports: [PrismaModule], + controllers: [FeedsController], + providers: [FeedsService, FeedsRepository], + exports: [FeedsService], +}) +export class FeedsModule {} diff --git a/src/modules/feeds/feeds.service.ts b/src/modules/feeds/feeds.service.ts new file mode 100644 index 00000000..2330f117 --- /dev/null +++ b/src/modules/feeds/feeds.service.ts @@ -0,0 +1,179 @@ +import { Injectable } from '@nestjs/common'; +import { session_userprofile, subject_department } from '@prisma/client'; +import { EFeed } from 'src/common/entities/EFeed'; +import { IFeed } from 'src/common/interfaces/IFeed'; +import { getRandomChoice } from 'src/common/utils/method.utils'; +import { DepartmentRepository } from 'src/prisma/repositories/department.repository'; +import { FeedsRepository } from 'src/prisma/repositories/feeds.repository'; +import { ReviewsRepository } from 'src/prisma/repositories/review.repository'; +import { SemesterRepository } from 'src/prisma/repositories/semester.repository'; +import { UserRepository } from 'src/prisma/repositories/user.repository'; + +@Injectable() +export class FeedsService { + constructor( + private readonly departmentRepository: DepartmentRepository, + private readonly feedsRepository: FeedsRepository, + private readonly reviewsRepository: ReviewsRepository, + private readonly userRepository: UserRepository, + private readonly semesterRepository: SemesterRepository, + ) {} + + private filterFeeds(feeds: EFeed.Details[], feed: EFeed.Details | null) { + if (feed && feed.visible) { + feeds.push(feed); + } + } + + private async getFamousHumanityReview(date: Date) { + let feed = await this.feedsRepository.getFamousHumanityReview(date); + if (!feed) { + const humanityBestReviews = + await this.reviewsRepository.getRandomNHumanityBestReviews(3); + + feed = await this.feedsRepository.createFamousHumanityReview( + date, + humanityBestReviews, + ); + } + + return feed; + } + + private async getRankedReview(date: Date) { + let feed = await this.feedsRepository.getRankedReview(date); + + if (!feed) { + feed = await this.feedsRepository.createRankedReview(date); + } + return feed; + } + + private async getFamousMajorReviews( + date: Date, + departments: subject_department[], + ) { + return await Promise.all( + departments.map(async (department) => { + let feed = await this.feedsRepository.getFamousMajorReview( + date, + department, + ); + + if (!feed) { + const majorBestReviews = + await this.reviewsRepository.getRandomNMajorBestReviews( + 3, + department, + ); + + feed = await this.feedsRepository.createFamousMajorReview( + date, + department, + majorBestReviews, + ); + } + + return feed; + }), + ); + } + + private async getReviewWrite(date: Date, userId: number) { + let feed = await this.feedsRepository.getReviewWrite(date, userId); + + if (!feed) { + const notWritableSemester = + await this.semesterRepository.getNotWritableSemester(); + const takenLecture = getRandomChoice( + await this.userRepository.getTakenLectures(userId, notWritableSemester), + ); + if (!takenLecture) { + return null; + } + + feed = await this.feedsRepository.createReviewWrite( + date, + userId, + takenLecture.lecture_id, + ); + } + + return feed; + } + + private async getRelatedCourses(date: Date, userId: number) { + let feed = await this.feedsRepository.getRelatedCourse(date, userId); + + if (!feed) { + const takenLecture = getRandomChoice( + await this.userRepository.getTakenLectures(userId), + ); + + if (!takenLecture) { + return null; + } + + feed = await this.feedsRepository.createRelatedCourse( + date, + userId, + takenLecture.lecture.course_id, + ); + } + + return feed; + } + + public async getFeeds(query: IFeed.QueryDto, user: session_userprofile) { + const { date: dateString } = query; + const date = new Date(dateString); + const departments = await this.departmentRepository.getRelatedDepartments( + user, + ); + const feeds: EFeed.Details[] = []; + + const famousHumanityReview = await this.getFamousHumanityReview(date); + this.filterFeeds(feeds, famousHumanityReview); + + /** + * "RANKED_REVIEW" does not require RankedReview + * Always shows TOP 3 liked reviews. + */ + const rankedReview = await this.getRankedReview(date); + const top3LikedReviews = await this.reviewsRepository.getTopLikedReviews(3); + + /** + * RankedReview schema dose not have relation with review_review. + * So, manually add reviews in app level. + */ + const rankedReviewWithReviews = { + ...rankedReview, + ...{ reviews: top3LikedReviews }, + }; + this.filterFeeds(feeds, rankedReviewWithReviews); + + const famousMajorReviews = await this.getFamousMajorReviews( + date, + departments, + ); + famousMajorReviews.forEach((feed) => { + this.filterFeeds(feeds, feed); + }); + + const reviewWrite = await this.getReviewWrite(date, user.id); + this.filterFeeds(feeds, reviewWrite); + + /** + * @NOTE + * RelatedCourse does not have Datas of posterior or prior courses. + * Comment out below until having enough Datas. + */ + // const relatedCourse = await this.getRelatedCourses(date, user.id); + // this.filterFeeds(feeds, relatedCourse); + + const rateDaily = await this.feedsRepository.getOrCreateRate(date, user.id); + this.filterFeeds(feeds, rateDaily); + + return feeds; + } +} diff --git a/src/modules/lectures/lectures.controller.ts b/src/modules/lectures/lectures.controller.ts new file mode 100644 index 00000000..3911fd03 --- /dev/null +++ b/src/modules/lectures/lectures.controller.ts @@ -0,0 +1,56 @@ +import { Controller, Get, Param, Query } from '@nestjs/common'; +import { session_userprofile } from '@prisma/client'; +import { GetUser } from 'src/common/decorators/get-user.decorator'; +import { Public } from 'src/common/decorators/skip-auth.decorator'; +import { ILecture } from 'src/common/interfaces/ILecture'; +import { IReview } from 'src/common/interfaces/IReview'; +import { LecturesService } from './lectures.service'; + +@Controller('api/lectures') +export class LecturesController { + constructor(private readonly LectureService: LecturesService) {} + + @Public() + @Get() + async getLectures(@Query() query: ILecture.QueryDto) { + return await this.LectureService.getLectureByFilter(query); + } + + @Public() + @Get('autocomplete') + async getLectureAutocomplete(@Query() query: ILecture.AutocompleteQueryDto) { + return await this.LectureService.getLectureAutocomplete(query); + } + + @Public() + @Get(':id') + async getLectureById(@Param('id') id: number) { + return await this.LectureService.getLectureById(id); + } + + @Public() + @Get(':lectureId/reviews') + async getLectureReviews( + @Query() query: IReview.LectureReviewsQueryDto, + @Param('lectureId') lectureId: number, + @GetUser() user: session_userprofile, + // TODO: Consider using IReview.Basic + ): Promise<(IReview.Basic & { userspecific_is_liked: boolean })[]> { + return await this.LectureService.getLectureReviews(user, lectureId, query); + } + + @Public() + @Get(':lectureId/related-reviews') + async getLectureRelatedReviews( + @Query() query: IReview.LectureReviewsQueryDto, + @Param('lectureId') lectureId: number, + @GetUser() user: session_userprofile, + // TODO: Consider using IReview.Basic + ): Promise<(IReview.Basic & { userspecific_is_liked: boolean })[]> { + return await this.LectureService.getLectureRelatedReviews( + user, + lectureId, + query, + ); + } +} diff --git a/src/modules/lectures/lectures.module.ts b/src/modules/lectures/lectures.module.ts new file mode 100644 index 00000000..1fd4a38e --- /dev/null +++ b/src/modules/lectures/lectures.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { PrismaModule } from 'src/prisma/prisma.module'; +import { LecturesController } from './lectures.controller'; +import { LecturesService } from './lectures.service'; + +@Module({ + imports: [PrismaModule], + controllers: [LecturesController], + providers: [LecturesService], + exports: [LecturesService], +}) +export class LecturesModule {} diff --git a/src/modules/lectures/lectures.service.ts b/src/modules/lectures/lectures.service.ts new file mode 100644 index 00000000..c542c437 --- /dev/null +++ b/src/modules/lectures/lectures.service.ts @@ -0,0 +1,216 @@ +import { Injectable } from '@nestjs/common'; +import { session_userprofile } from '@prisma/client'; +import { ELecture } from 'src/common/entities/ELecture'; +import { EReview } from 'src/common/entities/EReview'; +import { ILecture } from 'src/common/interfaces/ILecture'; +import { IReview } from 'src/common/interfaces/IReview'; +import { toJsonLectureDetail } from 'src/common/interfaces/serializer/lecture.serializer'; +import { toJsonReview } from 'src/common/interfaces/serializer/review.serializer'; +import { PrismaService } from 'src/prisma/prisma.service'; +import { ReviewsRepository } from 'src/prisma/repositories/review.repository'; +import { LectureRepository } from './../../prisma/repositories/lecture.repository'; + +@Injectable() +export class LecturesService { + constructor( + private LectureRepository: LectureRepository, + private reviewsRepository: ReviewsRepository, + private prisma: PrismaService, + ) {} + + public async getLectureByFilter( + query: ILecture.QueryDto, + ): Promise { + const queryResult = await this.LectureRepository.filterByRequest(query); + return queryResult.map((lecture) => toJsonLectureDetail(lecture)); + } + + public async getLectureById(id: number): Promise { + const queryResult = await this.LectureRepository.getLectureById(id); + return toJsonLectureDetail(queryResult); + } + + public async getLectureReviews( + user: session_userprofile, + lectureId: number, + query: IReview.LectureReviewsQueryDto, + ): Promise<(IReview.Basic & { userspecific_is_liked: boolean })[]> { + const MAX_LIMIT = 100; + const DEFAULT_ORDER = ['-written_datetime', '-id']; + const reviews = await this.reviewsRepository.getReviewsOfLecture( + lectureId, + query.order ?? DEFAULT_ORDER, + query.offset ?? 0, + query.limit ?? MAX_LIMIT, + ); + + // TODO: Make this efficient. Get this info together in getReviewsOfLecture + return await Promise.all( + reviews.map(async (review) => { + const result = toJsonReview(review); + if (user) { + const isLiked: boolean = await this.reviewsRepository.isLiked( + review.id, + user.id, + ); + return Object.assign(result, { + userspecific_is_liked: isLiked, + }); + } else { + return Object.assign(result, { + userspecific_is_liked: false, + }); + } + }), + ); + } + + public async getLectureRelatedReviews( + user: session_userprofile, + lectureId: number, + query: IReview.LectureReviewsQueryDto, + ): Promise<(IReview.Basic & { userspecific_is_liked: boolean })[]> { + const DEFAULT_LIMIT = 100; + const DEFAULT_ORDER = ['-written_datetime', '-id']; + + const lecture = await this.LectureRepository.getLectureById(lectureId); + const reviews: EReview.Details[] = + await this.reviewsRepository.getRelatedReviewsOfLecture( + query.order ?? DEFAULT_ORDER, + query.offset ?? 0, + query.limit ?? DEFAULT_LIMIT, + lecture, + ); + + return await Promise.all( + reviews.map(async (review) => { + const result = toJsonReview(review); + if (user) { + const isLiked: boolean = await this.reviewsRepository.isLiked( + review.id, + user.id, + ); + return Object.assign(result, { + userspecific_is_liked: isLiked, + }); + } else { + return Object.assign(result, { + userspecific_is_liked: false, + }); + } + }), + ); + } + + public async getLecturesByIds(ids: number[]): Promise { + return await this.LectureRepository.getLectureByIds(ids); + } + + async getLectureAutocomplete(dto: ILecture.AutocompleteQueryDto) { + const candidate = await this.LectureRepository.getLectureAutocomplete(dto); + if (!candidate) return dto.keyword; + return this.findAutocompleteFromCandidate(candidate, dto.keyword); + } + + private findAutocompleteFromCandidate( + candidate: ELecture.Extended, + keyword: string, + ) { + const keywordLower = keyword.toLowerCase(); + if (candidate.subject_department.name.startsWith(keyword)) + return candidate.subject_department.name; + if ( + candidate.subject_department.name_en + ?.toLowerCase() + .startsWith(keywordLower) + ) + return candidate.subject_department.name_en; + if (candidate.title.startsWith(keyword)) return candidate.title; + if (candidate.title_en.toLowerCase().startsWith(keywordLower)) + return candidate.title_en; + for (const professor of candidate.subject_lecture_professors) { + if (professor.professor.professor_name.startsWith(keyword)) + return professor.professor.professor_name; + if ( + professor.professor.professor_name_en + ?.toLowerCase() + .startsWith(keywordLower) + ) + return professor.professor.professor_name_en; + } + } + + async getLectureDetailsForTimetable( + lectureIds: number[], + isEnglish: boolean, + ): Promise< + Map + > { + const lectureDetails = + await this.LectureRepository.getLectureDetailsForTimetable(lectureIds); + + const lectureDetailsMap = new Map< + number, + { professorText: string; classroomShortStr: string } + >(); + + lectureDetails.forEach((lecture) => { + const professorShortStr = lecture.subject_lecture_professors.map((lp) => + isEnglish + ? lp.professor.professor_name_en + : lp.professor.professor_name, + ); + const professorText = + professorShortStr.length <= 2 + ? professorShortStr.join(', ') + : isEnglish + ? `${professorShortStr[0]} and ${professorShortStr.length - 1} others` + : `${professorShortStr[0]} 외 ${professorShortStr.length - 1} 명`; + + const classtime = lecture.subject_classtime[0]; + let classroomShortStr = ''; + + if (classtime) { + const { building_full_name, building_full_name_en, room_name } = + classtime; + + if (!building_full_name) { + classroomShortStr = isEnglish ? 'Unknown' : '정보 없음'; + } else { + if (building_full_name.startsWith('(')) { + const buildingCode = building_full_name.substring( + 1, + building_full_name.indexOf(')'), + ); + classroomShortStr = `(${buildingCode}) ${room_name || ''}`; + } else { + classroomShortStr = `${building_full_name} ${room_name || ''}`; + } + + if (building_full_name_en && building_full_name_en.startsWith('(')) { + const buildingCodeEn = building_full_name_en.substring( + 1, + building_full_name_en.indexOf(')'), + ); + classroomShortStr = isEnglish + ? `(${buildingCodeEn}) ${room_name || ''}` + : classroomShortStr; + } else { + classroomShortStr = isEnglish + ? `${building_full_name_en} ${room_name || ''}` + : classroomShortStr; + } + } + } else { + classroomShortStr = isEnglish ? 'Unknown' : '정보 없음'; + } + + lectureDetailsMap.set(lecture.id, { + professorText, + classroomShortStr, + }); + }); + + return lectureDetailsMap; + } +} diff --git a/src/modules/notices/notices.controller.ts b/src/modules/notices/notices.controller.ts new file mode 100644 index 00000000..0dc17673 --- /dev/null +++ b/src/modules/notices/notices.controller.ts @@ -0,0 +1,17 @@ +import { Controller, Get } from '@nestjs/common'; +import { Public } from 'src/common/decorators/skip-auth.decorator'; +import { toJsonNoticeBasic } from 'src/common/interfaces/serializer/notices.serializer'; +import { NoticesService } from './notices.service'; + +@Controller('api/notices') +export class NoticesController { + constructor(private readonly noticesService: NoticesService) {} + + @Public() + @Get() + async getNotices() { + const notices = await this.noticesService.getNotices(); + + return notices.map(toJsonNoticeBasic); + } +} diff --git a/src/modules/notices/notices.module.ts b/src/modules/notices/notices.module.ts new file mode 100644 index 00000000..b3bb22df --- /dev/null +++ b/src/modules/notices/notices.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { PrismaModule } from 'src/prisma/prisma.module'; +import { NoticesController } from './notices.controller'; +import { NoticesService } from './notices.service'; + +@Module({ + imports: [PrismaModule], + controllers: [NoticesController], + providers: [NoticesService], +}) +export class NoticesModule {} diff --git a/src/modules/notices/notices.service.ts b/src/modules/notices/notices.service.ts new file mode 100644 index 00000000..3d102df9 --- /dev/null +++ b/src/modules/notices/notices.service.ts @@ -0,0 +1,11 @@ +import { Injectable } from '@nestjs/common'; +import { NoticesRepository } from 'src/prisma/repositories/notices.repository'; + +@Injectable() +export class NoticesService { + constructor(private readonly noticesRepository: NoticesRepository) {} + + public async getNotices() { + return await this.noticesRepository.getNotices(new Date()); + } +} diff --git a/src/modules/planners/planners.controller.ts b/src/modules/planners/planners.controller.ts new file mode 100644 index 00000000..3b55cfe0 --- /dev/null +++ b/src/modules/planners/planners.controller.ts @@ -0,0 +1,161 @@ +import { + Body, + Controller, + Delete, + Get, + Param, + Patch, + Post, + Query, + UnauthorizedException, +} from '@nestjs/common'; +import { session_userprofile } from '@prisma/client'; +import { GetUser } from 'src/common/decorators/get-user.decorator'; +import { IPlanner } from 'src/common/interfaces/IPlanner'; +import { toJsonPlannerItem } from '@src/common/interfaces/serializer/planner.item.serializer'; +import { PlannersService } from './planners.service'; +import { toJsonPlanner } from '@src/common/interfaces/serializer/planner.serializer'; +import { PlannerPipe } from '@src/common/pipe/planner.pipe'; + +@Controller('api/users/:id/planners') +export class PlannersController { + constructor(private readonly plannersService: PlannersService) {} + + @Get() + async getPlanners( + @Query() query: IPlanner.QueryDto, + @Param('id') id: number, + @GetUser() user: session_userprofile, + ) { + if (id !== user.id) { + throw new UnauthorizedException(); + } + const planners = await this.plannersService.getPlannerByUser(query, user); + return planners; + } + + @Patch(':plannerId') + async updatePlanner( + @Param('plannerId', PlannerPipe) plannerId: number, + @Body() planner: IPlanner.UpdateBodyDto, + @GetUser() user: session_userprofile, + ) { + if (planner.should_update_taken_semesters) { + await this.plannersService.updateTakenLectures( + user, + plannerId, + planner.start_year, + planner.end_year, + ); + } + const updatedPlanner = await this.plannersService.updatePlanner( + plannerId, + planner, + user, + ); + return toJsonPlanner(updatedPlanner); + } + + @Delete(':plannerId') + async deletePlanner( + @Param('plannerId', PlannerPipe) plannerId: number, + @GetUser() user: session_userprofile, + ) { + await this.plannersService.deletePlanner(plannerId); + return { message: 'Planner deleted' }; + } + + @Post() + async postPlanner( + @Body() planner: IPlanner.CreateBodyDto, + @Param('id') id: number, + @GetUser() user: session_userprofile, + ) { + if (id !== user.id) { + throw new UnauthorizedException(); + } + const newPlanner = await this.plannersService.postPlanner(planner, user); + return newPlanner; + } + + @Post(':plannerId/add-arbitrary-item') + async addArbitraryItem( + @Param('id') id: number, + + @Param('plannerId') plannerId: number, + @Body() item: IPlanner.AddArbitraryItemDto, + @GetUser() user: session_userprofile, + ) { + if (id !== user.id) throw new UnauthorizedException(); + + const newPlanner = await this.plannersService.addArbitraryItem( + plannerId, + item, + user, + ); + return newPlanner; + } + + @Post(':plannerId/remove-item') + async removePlanner( + @Body() removeItem: IPlanner.RemoveItemBodyDto, + @Param('plannerId') plannerId: number, + @GetUser() user: session_userprofile, + ): Promise { + return await this.plannersService.removePlannerItem( + plannerId, + removeItem, + user, + ); + } + + @Post(':plannerId/add-future-item') + async addFutureItem( + @Body() item: IPlanner.FuturePlannerItemDto, + @Param('id') userId: number, + @Param('plannerId') plannerId: number, + @GetUser() user: session_userprofile, + ) { + if (userId !== user.id) { + throw new UnauthorizedException(); + } + const futureItem = await this.plannersService.createFuturePlannerItem( + plannerId, + item.year, + item.semester, + item.course, + ); + return futureItem; + } + + @Post(':plannerId/reorder') + async reorderPlanner( + @Body() reorder: IPlanner.ReorderBodyDto, + @Param('plannerId') plannerId: number, + @GetUser() user: session_userprofile, + ): Promise { + return await this.plannersService.reorderPlanner( + plannerId, + reorder.arrange_order, + user, + ); + } + + @Post(':plannerId/update-item') + async updatePlannerItem( + @Param('id') userId: number, + @Param('plannerId') plannerId: number, + @GetUser() user: session_userprofile, + @Body() updateItemDto: IPlanner.UpdateItemBodyDto, + ) { + if (userId !== user.id) { + throw new UnauthorizedException(); + } + + const updatedItem = await this.plannersService.updatePlannerItem( + plannerId, + updateItemDto, + ); + return toJsonPlannerItem(updatedItem, updateItemDto.item_type); + } +} diff --git a/src/modules/planners/planners.module.ts b/src/modules/planners/planners.module.ts new file mode 100644 index 00000000..18623ddf --- /dev/null +++ b/src/modules/planners/planners.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { PrismaModule } from 'src/prisma/prisma.module'; +import { PlannersController } from './planners.controller'; +import { PlannersService } from './planners.service'; + +@Module({ + imports: [PrismaModule], + controllers: [PlannersController], + providers: [PlannersService], +}) +export class PlannersModule {} diff --git a/src/modules/planners/planners.service.ts b/src/modules/planners/planners.service.ts new file mode 100644 index 00000000..eef4ffdb --- /dev/null +++ b/src/modules/planners/planners.service.ts @@ -0,0 +1,370 @@ +import { + BadRequestException, + HttpException, + HttpStatus, + Injectable, + NotFoundException, + UnauthorizedException, +} from '@nestjs/common'; +import { session_userprofile } from '@prisma/client'; +import { IPlanner } from 'src/common/interfaces/IPlanner'; +import { + toJsonArbitraryItem, + toJsonFutureItem, +} from 'src/common/interfaces/serializer/planner.item.serializer'; +import { toJsonPlanner } from 'src/common/interfaces/serializer/planner.serializer'; +import { DepartmentRepository } from 'src/prisma/repositories/department.repository'; +import { LectureRepository } from 'src/prisma/repositories/lecture.repository'; +import { PlannerRepository } from 'src/prisma/repositories/planner.repository'; +import { EPlanners } from '../../common/entities/EPlanners'; +import { CourseRepository } from './../../prisma/repositories/course.repository'; +import { Transactional } from '@nestjs-cls/transactional'; +import { SemesterRepository } from '@src/prisma/repositories/semester.repository'; +import { UserRepository } from '@src/prisma/repositories/user.repository'; + +@Injectable() +export class PlannersService { + constructor( + private readonly PlannerRepository: PlannerRepository, + private readonly LectureRepository: LectureRepository, + private readonly DepartmentRepository: DepartmentRepository, + private readonly CourseRepository: CourseRepository, + private readonly SemesterRepository: SemesterRepository, + private readonly UserRepository: UserRepository, + ) {} + + public async getPlannerByUser( + query: IPlanner.QueryDto, + user: session_userprofile, + ) { + const queryResult = await this.PlannerRepository.getPlannerByUser( + query, + user, + ); + return queryResult.map(toJsonPlanner); + } + + async getRelatedPlanner(user: session_userprofile) { + return await this.PlannerRepository.getRelatedPlanner(user); + } + + @Transactional() + public async postPlanner( + body: IPlanner.CreateBodyDto, + user: session_userprofile, + ): Promise { + const relatedPlanner = await this.getRelatedPlanner(user); + const arrangeOrder = + relatedPlanner.length == 0 + ? 0 + : relatedPlanner[relatedPlanner.length - 1].arrange_order + 1; + const planner = await this.PlannerRepository.createPlanner( + body, + arrangeOrder, + user, + ); + + if (body.should_update_taken_semesters) { + const takenLectures = + await this.LectureRepository.findReviewWritableLectures( + user, + new Date(), + ); + const valid_takenLectures = takenLectures.filter((lecture) => { + const validStartYear = lecture.year >= body.start_year; + const validEndYear = lecture.year <= body.end_year; + return validStartYear && validEndYear; + }); + await this.PlannerRepository.createTakenPlannerItem( + planner.id, + valid_takenLectures.map((lecture) => { + return { lectureId: lecture.id, isExcluded: false }; + }), + ); + } + + const takenTargetItems = + await this.PlannerRepository.getTakenPlannerItemByIds( + body.taken_items_to_copy, + ); + await this.PlannerRepository.createTakenPlannerItem( + planner.id, + takenTargetItems?.map((item) => { + return { lectureId: item.lecture_id, isExcluded: item.is_excluded }; + }) || [], + ); + + const futureTargetItems = + await this.PlannerRepository.getFuturePlannerItemById( + body.future_items_to_copy, + ); + await this.PlannerRepository.createFuturePlannerItem( + planner.id, + futureTargetItems || [], + ); + + const targetItems = + await this.PlannerRepository.getArbitraryPlannerItemById( + body.arbitrary_items_to_copy, + ); + await this.PlannerRepository.createArbitraryPlannerItem( + planner.id, + targetItems || [], + ); + + const newPlanner = await this.PlannerRepository.getPlannerById( + user, + planner.id, + ); + if (!newPlanner) { + throw new NotFoundException(); + } + + return toJsonPlanner(newPlanner); + } + + @Transactional() + async addArbitraryItem( + plannerId: number, + body: IPlanner.AddArbitraryItemDto, + user: session_userprofile, + ) { + const planner = await this.PlannerRepository.getBasicPlannerById(plannerId); + if (!planner) throw new NotFoundException(); + if (planner.user_id !== user.id) throw new UnauthorizedException(); + + const department = await this.DepartmentRepository.getBasicDepartmentById( + body.department, + ); + + const data = { + planner_id: planner.id, + year: body.year, + semester: body.semester, + department_id: department?.id || null, + type: body.type, + type_en: body.type_en, + credit: body.credit, + credit_au: body.credit_au, + is_excluded: false, + }; + + const arbitraryItem = + await this.PlannerRepository.createArbitraryPlannerItem(planner.id, data); + return toJsonArbitraryItem(arbitraryItem); + } + + @Transactional() + public async removePlannerItem( + plannerId: number, + removeItem: IPlanner.RemoveItemBodyDto, + user: session_userprofile, + ): Promise { + switch (removeItem.item_type) { + case 'TAKEN': + throw new BadRequestException( + 'Planner item with type "taken" can\'t be deleted', + ); + case 'FUTURE': { + const futureItem = + await this.PlannerRepository.getFuturePlannerItemById([ + removeItem.item, + ]); + if (!futureItem || futureItem[0].planner_id !== plannerId) { + throw new NotFoundException(); + } + await this.PlannerRepository.deleteFuturePlannerItem(futureItem[0]); + break; + } + case 'ARBITRARY': { + const arbitraryItem = + await this.PlannerRepository.getArbitraryPlannerItemById([ + removeItem.item, + ]); + if (!arbitraryItem || arbitraryItem[0].planner_id !== plannerId) { + throw new NotFoundException(); + } + await this.PlannerRepository.deleteArbitraryPlannerItem( + arbitraryItem[0], + ); + break; + } + } + + const planner = await this.PlannerRepository.getPlannerById( + user, + plannerId, + ); + + if (!planner) { + throw new NotFoundException(); + } + + return toJsonPlanner(planner); + } + + @Transactional() + async createFuturePlannerItem( + plannerId: number, + year: number, + semester: number, + courseId: number, + ): Promise { + const planner = await this.PlannerRepository.checkPlannerExists(plannerId); + if (!planner) { + throw new HttpException("Planner Doesn't exist", HttpStatus.NOT_FOUND); + } + + const course = await this.CourseRepository.getCourseById(courseId); + if (!course) { + throw new HttpException( + "Wrong field 'course' in request data", + HttpStatus.BAD_REQUEST, + ); + } + const item: EPlanners.EItems.Future.Extended = + await this.PlannerRepository.createPlannerItem( + plannerId, + year, + semester, + courseId, + ); + return toJsonFutureItem(item); + } + + @Transactional() + public async reorderPlanner( + plannerId: number, + order: number, + user: session_userprofile, + ): Promise { + const planner = await this.PlannerRepository.getPlannerById( + user, + plannerId, + ); + + if (!planner) { + throw new NotFoundException(); + } + + const oldOrder = planner.arrange_order; + const relatedPlannerIds = ( + await this.PlannerRepository.getRelatedPlanner(user) + ).map((planner) => planner.id); + + if (oldOrder < order) { + await this.PlannerRepository.decrementOrders( + relatedPlannerIds, + oldOrder + 1, + order, + ); + } else if (oldOrder > order) { + await this.PlannerRepository.incrementOrders( + relatedPlannerIds, + order, + oldOrder - 1, + ); + } + + const updated = await this.PlannerRepository.updateOrder(plannerId, order); + + return toJsonPlanner(updated); + } + + @Transactional() + async updatePlannerItem( + plannerId: number, + updateItemDto: IPlanner.UpdateItemBodyDto, + ): Promise< + | EPlanners.EItems.Taken.Details + | EPlanners.EItems.Future.Extended + | EPlanners.EItems.Arbitrary.Extended + > { + const planner = await this.PlannerRepository.checkPlannerExists(plannerId); + if (!planner) { + throw new HttpException("Planner Doesn't exist", HttpStatus.NOT_FOUND); + } + const { item_type, item, ...updatedFields } = updateItemDto; + return await this.PlannerRepository.updatePlannerItem( + item_type, + item, + updatedFields, + ); + } + + @Transactional() + async updateTakenLectures( + user: session_userprofile, + plannerId: number, + start_year: number, + end_year: number, + ) { + const notWritableSemester = + await this.SemesterRepository.getNotWritableSemester(); + const takenLectures = await this.UserRepository.getTakenLecturesByYear( + user.id, + start_year, + end_year, + notWritableSemester, + ); + const existedTakenItems = + await this.PlannerRepository.getTakenPlannerItemByLectures( + plannerId, + takenLectures.map((takenLecture) => takenLecture.lecture_id), + ); + const needToAddTakenLectures = takenLectures.filter( + (takenLecture) => + !existedTakenItems.find( + (existedTakenItem) => + existedTakenItem.lecture_id === takenLecture.lecture_id, + ), + ); + await this.PlannerRepository.createTakenPlannerItem( + plannerId, + needToAddTakenLectures.map((lecture) => { + return { lectureId: lecture.lecture_id, isExcluded: false }; + }), + ); + await this.PlannerRepository.deleteFuturePlannerItemsWithWhere( + plannerId, + start_year, + end_year, + ); + await this.PlannerRepository.deleteArbitraryPlannerItemsWithWhere( + plannerId, + start_year, + end_year, + ); + await this.PlannerRepository.deleteTakenPlannerItemsWithWhere(plannerId, { + year: { + lte: start_year, + gte: end_year, + }, + }); + } + + @Transactional() + async updatePlanner( + plannerId: number, + plannerDto: IPlanner.UpdateBodyDto, + user: session_userprofile, + ) { + const planner = this.PlannerRepository.getPlannerById(user, plannerId); + if (!planner) { + throw new NotFoundException(); + } + const updateFields = { + start_year: plannerDto.start_year, + end_year: plannerDto.end_year, + general_track_id: plannerDto.general_track, + major_track_id: plannerDto.major_track, + additional_track_ids: plannerDto.additional_tracks ?? [], + }; + return await this.PlannerRepository.updatePlanner(plannerId, updateFields); + } + + @Transactional() + async deletePlanner(plannerId: number) { + await this.PlannerRepository.deletePlanner(plannerId); + } +} diff --git a/src/modules/rates/rates.controller.ts b/src/modules/rates/rates.controller.ts new file mode 100644 index 00000000..addaa2bb --- /dev/null +++ b/src/modules/rates/rates.controller.ts @@ -0,0 +1,19 @@ +import { Body, Controller, Post } from '@nestjs/common'; +import { session_userprofile } from '@prisma/client'; +import { GetUser } from 'src/common/decorators/get-user.decorator'; +import { IRate } from 'src/common/interfaces/IRate'; +import { toJsonRate } from 'src/common/interfaces/serializer/rate.serializer'; +import { RatesService } from './rates.service'; + +@Controller('api/rates') +export class RatesController { + constructor(private readonly ratesService: RatesService) {} + @Post() + async createRates( + @Body() body: IRate.CreateDto, + @GetUser() user: session_userprofile, + ): Promise { + const rate = await this.ratesService.createRate(body, user); + return toJsonRate(rate); + } +} diff --git a/src/modules/rates/rates.module.ts b/src/modules/rates/rates.module.ts new file mode 100644 index 00000000..3cbe1d92 --- /dev/null +++ b/src/modules/rates/rates.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { PrismaModule } from 'src/prisma/prisma.module'; +import { RateRepository } from 'src/prisma/repositories/rates.repository'; +import { RatesController } from './rates.controller'; +import { RatesService } from './rates.service'; + +@Module({ + imports: [PrismaModule], + controllers: [RatesController], + providers: [RatesService, RateRepository], +}) +export class RatesModule {} diff --git a/src/modules/rates/rates.service.ts b/src/modules/rates/rates.service.ts new file mode 100644 index 00000000..7cd8817d --- /dev/null +++ b/src/modules/rates/rates.service.ts @@ -0,0 +1,33 @@ +import { BadRequestException, Injectable } from '@nestjs/common'; +import { session_userprofile, support_rate } from '@prisma/client'; +import { IRate } from 'src/common/interfaces/IRate'; +import { RateRepository } from 'src/prisma/repositories/rates.repository'; +import settings from 'src/settings'; +import { Transactional } from '@nestjs-cls/transactional'; + +@Injectable() +export class RatesService { + constructor(private readonly rateRepository: RateRepository) {} + @Transactional() + async createRate( + body: IRate.CreateDto, + user: session_userprofile, + ): Promise { + const { score } = body; + const year = new Date().getFullYear(); + const version = settings().getVersion(); + + const rate = await this.rateRepository.getRate(user.id, year, version); + if (rate) { + throw new BadRequestException('You already rated for current year'); + } + + const newRate = await this.rateRepository.createRate( + user.id, + score, + year, + version, + ); + return newRate; + } +} diff --git a/src/modules/reviews/reviews.controller.ts b/src/modules/reviews/reviews.controller.ts new file mode 100644 index 00000000..e160ada1 --- /dev/null +++ b/src/modules/reviews/reviews.controller.ts @@ -0,0 +1,90 @@ +import { + Body, + Controller, + Get, + HttpException, + HttpStatus, + Param, + Patch, + Post, + Query, +} from '@nestjs/common'; +import { session_userprofile } from '@prisma/client'; +import { GetUser } from 'src/common/decorators/get-user.decorator'; +import { Public } from 'src/common/decorators/skip-auth.decorator'; +import { IReview } from 'src/common/interfaces/IReview'; +import { ReviewsService } from './reviews.service'; + +@Controller('api/reviews') +export class ReviewsController { + constructor(private readonly reviewsService: ReviewsService) {} + @Public() + @Get() + async getReviews( + @Query() reviewsParam: IReview.QueryDto, + @GetUser() user: session_userprofile, + ): Promise<(IReview.Basic & { userspecific_is_liked: boolean })[] | number> { + if (reviewsParam.response_type === 'count') { + const reviewsCount = await this.reviewsService.getReviewsCount( + reviewsParam.lecture_year, + reviewsParam.lecture_semester, + ); + return reviewsCount; + } + const result = await this.reviewsService.getReviews(reviewsParam, user); + return result; + } + + @Post() + async createReviews( + @Body() reviewsBody: IReview.CreateDto, + @GetUser() user: session_userprofile, + ): Promise { + if (user) { + const result = await this.reviewsService.createReviews(reviewsBody, user); + return result; + } else { + throw new HttpException("Can't find user", 401); + } + } + + @Public() + @Get(':reviewId') + async getReviewInstance( + @Param('reviewId') reviewId: number, + @GetUser() user: session_userprofile, + ): Promise { + return await this.reviewsService.getReviewById(reviewId, user); + } + + @Patch(':reviewId') + async updateReviewInstance( + @Body() reviewsBody: IReview.UpdateDto, + @Param('reviewId') reviewId: number, + @GetUser() user: session_userprofile, + ): Promise { + if (user) { + return await this.reviewsService.updateReviewById( + reviewId, + user, + reviewsBody, + ); + } else { + throw new HttpException("Can't find user", 401); + } + } + + @Post(':reviewId/like') + async likeReviewInstance( + @Param('reviewId') reviewId: number, + @GetUser() user: session_userprofile, + ) { + const reviewVote = await this.reviewsService.findReviewVote(reviewId, user); + if (reviewVote) { + throw new HttpException('Already Liked', HttpStatus.BAD_REQUEST); + } else { + await this.reviewsService.createReviewVote(reviewId, user); + return null; + } + } +} diff --git a/src/modules/reviews/reviews.module.ts b/src/modules/reviews/reviews.module.ts new file mode 100644 index 00000000..685a4507 --- /dev/null +++ b/src/modules/reviews/reviews.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { UserService } from '../user/user.service'; +import { PrismaModule } from './../../prisma/prisma.module'; +import { ReviewsController } from './reviews.controller'; +import { ReviewsService } from './reviews.service'; + +@Module({ + imports: [PrismaModule], + controllers: [ReviewsController], + providers: [ReviewsService, UserService], +}) +export class ReviewsModule {} diff --git a/src/modules/reviews/reviews.service.ts b/src/modules/reviews/reviews.service.ts new file mode 100644 index 00000000..031fc7fa --- /dev/null +++ b/src/modules/reviews/reviews.service.ts @@ -0,0 +1,173 @@ +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { session_userprofile } from '@prisma/client'; +import { IReview } from 'src/common/interfaces/IReview'; +import { toJsonReview } from 'src/common/interfaces/serializer/review.serializer'; +import { LectureRepository } from 'src/prisma/repositories/lecture.repository'; +import { ReviewsRepository } from 'src/prisma/repositories/review.repository'; +import { Transactional } from '@nestjs-cls/transactional'; + +@Injectable() +export class ReviewsService { + constructor( + private readonly reviewsRepository: ReviewsRepository, + private readonly lectureRepository: LectureRepository, + ) {} + + async getReviewById( + reviewId: number, + user: session_userprofile, + ): Promise { + const review = await this.reviewsRepository.getReviewById(reviewId); + if (review == undefined) throw new HttpException("Can't find review", 404); + const result = toJsonReview(review); + if (user) { + const isLiked: boolean = await this.reviewsRepository.isLiked( + review.id, + user.id, + ); + return Object.assign(result, { + userspecific_is_liked: isLiked, + }); + } else { + return Object.assign(result, { + userspecific_is_liked: false, + }); + } + } + async getReviews( + reviewsParam: IReview.QueryDto, + user: session_userprofile, + ): Promise<(IReview.Basic & { userspecific_is_liked: boolean })[]> { + const MAX_LIMIT = 50; + const DEFAULT_ORDER = ['-written_datetime', '-id']; + const reviews = await this.reviewsRepository.getReviews( + reviewsParam.order ?? DEFAULT_ORDER, + reviewsParam.offset ?? 0, + reviewsParam.limit ?? MAX_LIMIT, + reviewsParam.lecture_year, + reviewsParam.lecture_semester, + ); + return await Promise.all( + reviews.map(async (review) => { + const result = toJsonReview(review); + if (user) { + const isLiked: boolean = await this.reviewsRepository.isLiked( + review.id, + user.id, + ); + return Object.assign(result, { + userspecific_is_liked: isLiked, + }); + } else { + return Object.assign(result, { + userspecific_is_liked: false, + }); + } + }), + ); + } + + async getReviewsCount( + lectureYear?: number, + lectureSemester?: number, + ): Promise { + return await this.reviewsRepository.getReviewsCount( + lectureYear, + lectureSemester, + ); + } + + @Transactional() + async createReviews( + reviewsBody: IReview.CreateDto, + user: session_userprofile, + ): Promise { + const reviewWritableLectures = + await this.lectureRepository.findReviewWritableLectures(user, new Date()); + const reviewLecture = reviewWritableLectures.find((lecture) => { + return lecture.id == reviewsBody.lecture; + }); + if (reviewLecture == undefined) + throw new HttpException("Can't find lecture", 404); + const review = await this.reviewsRepository.upsertReview( + reviewLecture.course_id, + reviewLecture.id, + reviewsBody.content, + reviewsBody.grade, + reviewsBody.load, + reviewsBody.speech, + user.id, + ); + const result = toJsonReview(review); + if (user) { + const isLiked: boolean = await this.reviewsRepository.isLiked( + review.id, + user.id, + ); + return Object.assign(result, { + userspecific_is_liked: isLiked, + }); + } else { + return Object.assign(result, { + userspecific_is_liked: false, + }); + } + } + + @Transactional() + async updateReviewById( + reviewId: number, + user: session_userprofile, + reviewBody: IReview.UpdateDto, + ): Promise { + const review = await this.reviewsRepository.getReviewById(reviewId); + if (!review) throw new HttpException("Can't find review", 404); + if (review.writer_id !== user.id) + throw new HttpException("Can't find user", 401); + if (review.is_deleted) + throw new HttpException( + 'Target review deleted by admin', + HttpStatus.BAD_REQUEST, + ); + const updateReview = await this.reviewsRepository.updateReview( + review.id, + reviewBody.content, + reviewBody.grade, + reviewBody.load, + reviewBody.speech, + ); + const result = toJsonReview(updateReview); + if (user) { + const isLiked: boolean = await this.reviewsRepository.isLiked( + review.id, + user.id, + ); + return Object.assign(result, { + userspecific_is_liked: isLiked, + }); + } else { + return Object.assign(result, { + userspecific_is_liked: false, + }); + } + } + + async findReviewVote( + reviewId: number, + user: session_userprofile, + ): Promise { + const review = await this.reviewsRepository.getReviewById(reviewId); + if (review == undefined) throw new HttpException("Can't find review", 404); + const isLiked: boolean = await this.reviewsRepository.isLiked( + review.id, + user.id, + ); + return isLiked; + } + + @Transactional() + async createReviewVote(reviewId: number, user: session_userprofile) { + await this.reviewsRepository.upsertReviewVote(reviewId, user.id); + return null; + } +} diff --git a/src/modules/semesters/semesters.controller.ts b/src/modules/semesters/semesters.controller.ts new file mode 100644 index 00000000..4e3dae94 --- /dev/null +++ b/src/modules/semesters/semesters.controller.ts @@ -0,0 +1,17 @@ +import { Controller, Get, Query } from '@nestjs/common'; +import { ISemester } from 'src/common/interfaces/ISemester'; +import { toJsonSemester } from '../../common/interfaces/serializer/semester.serializer'; +import { SemestersService } from './semesters.service'; +import { Public } from '@src/common/decorators/skip-auth.decorator'; + +@Controller('api/semesters') +export class SemestersController { + constructor(private readonly semestersService: SemestersService) {} + + @Get() + @Public() + async getSemesters(@Query() query: ISemester.QueryDto) { + const semesters = await this.semestersService.getSemesters(query); + return semesters.map((semester) => toJsonSemester(semester)); + } +} diff --git a/src/modules/semesters/semesters.module.ts b/src/modules/semesters/semesters.module.ts new file mode 100644 index 00000000..8e0e29f7 --- /dev/null +++ b/src/modules/semesters/semesters.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { PrismaModule } from '../../prisma/prisma.module'; +import { SemestersController } from './semesters.controller'; +import { SemestersService } from './semesters.service'; + +@Module({ + imports: [PrismaModule], + controllers: [SemestersController], + providers: [SemestersService], + exports: [SemestersService], +}) +export class SemestersModule {} diff --git a/src/modules/semesters/semesters.service.ts b/src/modules/semesters/semesters.service.ts new file mode 100644 index 00000000..bddcdaf2 --- /dev/null +++ b/src/modules/semesters/semesters.service.ts @@ -0,0 +1,30 @@ +import { Injectable } from '@nestjs/common'; +import { subject_semester } from '@prisma/client'; +import { ISemester } from 'src/common/interfaces/ISemester'; +import { SemesterRepository } from 'src/prisma/repositories/semester.repository'; +import { orderFilter } from '../../common/utils/search.utils'; + +@Injectable() +export class SemestersService { + constructor(private readonly semesterRepository: SemesterRepository) {} + + async getSemesters(order: ISemester.QueryDto) { + const orderBy = orderFilter(order.order); + const semesters = await this.semesterRepository.getSemesters({ + orderBy: orderBy, + }); + return semesters; + } + + public getSemesterName( + semester: subject_semester, + language: string = 'kr', + ): string { + const seasons = language.includes('en') + ? ['spring', 'summer', 'fall', 'winter'] + : ['봄', '여름', '가을', '겨울']; + + const seasonName = seasons[semester.semester - 1]; + return `${semester.year} ${seasonName}`; + } +} diff --git a/src/modules/session/session.controller.ts b/src/modules/session/session.controller.ts new file mode 100644 index 00000000..4f5c8c73 --- /dev/null +++ b/src/modules/session/session.controller.ts @@ -0,0 +1,35 @@ +import { Body, Controller, Get, Post } from '@nestjs/common'; +import { session_userprofile } from '@prisma/client'; +import { GetUser } from 'src/common/decorators/get-user.decorator'; +import { ISession } from 'src/common/interfaces/ISession'; +import { toJsonDepartment } from 'src/common/interfaces/serializer/department.serializer'; +import { DepartmentsService } from '../departments/departments.service'; +import { SessionService } from './session.service'; + +@Controller('session') +export class SessionController { + constructor( + private readonly sessionService: SessionService, + private readonly departmentsService: DepartmentsService, + ) {} + + @Get('department-options') + async departmentOptions() { + const { undergraduate, recent, other } = + await this.departmentsService.getDepartmentOptions(); + return [ + undergraduate.map((e) => toJsonDepartment(e)), + recent.map((e) => toJsonDepartment(e)), + other.map((e) => toJsonDepartment(e)), + ]; + } + + @Post('favorite-departments') + async favoriteDepartments( + @Body() body: ISession.FavoriteDepartmentsDto, + @GetUser() user: session_userprofile, + ) { + const departmentIds = body.fav_department.map((id) => parseInt(id)); + await this.sessionService.changeFavoriteDepartments(user.id, departmentIds); + } +} diff --git a/src/modules/session/session.module.ts b/src/modules/session/session.module.ts new file mode 100644 index 00000000..441d7d47 --- /dev/null +++ b/src/modules/session/session.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { PrismaModule } from 'src/prisma/prisma.module'; +import { DepartmentsModule } from '../departments/departments.module'; +import { SessionController } from './session.controller'; +import { SessionService } from './session.service'; + +@Module({ + imports: [PrismaModule, DepartmentsModule], + controllers: [SessionController], + providers: [SessionService], +}) +export class SessionModule {} diff --git a/src/modules/session/session.service.ts b/src/modules/session/session.service.ts new file mode 100644 index 00000000..4c22a632 --- /dev/null +++ b/src/modules/session/session.service.ts @@ -0,0 +1,13 @@ +import { Injectable } from '@nestjs/common'; +import { UserRepository } from 'src/prisma/repositories/user.repository'; +import { Transactional } from '@nestjs-cls/transactional'; + +@Injectable() +export class SessionService { + constructor(private readonly userRepository: UserRepository) {} + + @Transactional() + async changeFavoriteDepartments(userId: number, departmentIds: number[]) { + return this.userRepository.changeFavoriteDepartments(userId, departmentIds); + } +} diff --git a/src/modules/share/share.controller.ts b/src/modules/share/share.controller.ts new file mode 100644 index 00000000..54af6f38 --- /dev/null +++ b/src/modules/share/share.controller.ts @@ -0,0 +1,40 @@ +import { Controller, Get, Query, Res } from '@nestjs/common'; +import { session_userprofile } from '@prisma/client'; +import { Response } from 'express'; +import { IShare } from 'src/common/interfaces'; +import { TimetableRepository } from 'src/prisma/repositories/timetable.repository'; +import { GetUser } from '../../common/decorators/get-user.decorator'; +import { ShareService } from './share.service'; + +@Controller('/api/share') +export class ShareController { + constructor( + private readonly shareService: ShareService, + private readonly timetableRepository: TimetableRepository, + ) {} + + @Get('timetable/image') + async getTimetableImage( + @Query() query: IShare.TimetableImageQueryDto, + @GetUser() user: session_userprofile, + @Res() res: Response, + ) { + const imageBuffer = await this.shareService.createTimetableImage( + query, + user, + ); + res.setHeader('Content-Type', 'image/png'); + res.send(imageBuffer); + } + + @Get('timetable/ical') + async getTimetableIcal( + @Query() query: IShare.TimetableIcalQueryDto, + @GetUser() user: session_userprofile, + @Res() res: Response, + ) { + const calendar = await this.shareService.createTimetableIcal(query, user); + res.setHeader('Content-Type', 'text/calendar'); + res.send(calendar.toString()); + } +} diff --git a/src/modules/share/share.module.ts b/src/modules/share/share.module.ts new file mode 100644 index 00000000..911cbe05 --- /dev/null +++ b/src/modules/share/share.module.ts @@ -0,0 +1,14 @@ +import { Module } from '@nestjs/common'; +import { ShareController } from './share.controller'; +import { ShareService } from './share.service'; +import { TimetablesModule } from '../timetables/timetables.module'; +import { PrismaModule } from 'src/prisma/prisma.module'; +import { SemestersModule } from '../semesters/semesters.module'; +import { LecturesModule } from '../lectures/lectures.module'; + +@Module({ + imports: [PrismaModule, TimetablesModule, SemestersModule, LecturesModule], + controllers: [ShareController], + providers: [ShareService], +}) +export class ShareModule {} diff --git a/src/modules/share/share.service.ts b/src/modules/share/share.service.ts new file mode 100644 index 00000000..a2765aa1 --- /dev/null +++ b/src/modules/share/share.service.ts @@ -0,0 +1,463 @@ +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { session_userprofile, subject_semester } from '@prisma/client'; +import { + CanvasRenderingContext2D, + createCanvas, + loadImage, + registerFont, +} from 'canvas'; +import ical, { + ICalAlarmType, + ICalCalendar, + ICalEventRepeatingFreq, +} from 'ical-generator'; +import moment from 'moment-timezone'; +import { join } from 'path'; +import { ELecture } from 'src/common/entities/ELecture'; +import { ILecture, IShare } from 'src/common/interfaces'; +import { SemesterRepository } from 'src/prisma/repositories/semester.repository'; +import { LecturesService } from '../lectures/lectures.service'; +import { SemestersService } from '../semesters/semesters.service'; +import { TimetablesService } from '../timetables/timetables.service'; +import settings from '@src/settings'; + +interface RoundedRectangleOptions { + ctx: CanvasRenderingContext2D; + x: number; + y: number; + width: number; + height: number; + radius: number; + color: string; +} + +interface TextOptions { + ctx: CanvasRenderingContext2D; + x: number; + y: number; + text: string; + font: string; + fontSize: number; + color: string; + align?: 'right' | 'left' | 'center'; +} + +interface DrawTileOptions { + ctx: CanvasRenderingContext2D; + x: number; + y: number; + width: number; + height: number; + title: string; + professor: string; + location: string; + font: string; + fontSize: number; +} + +interface DrawTimetableDatas { + lectures: ELecture.WithClasstime[]; + timetableType: string; + semesterName: string; + isEnglish: boolean; + semesterFontSize: number; + tileFontSize: number; +} + +@Injectable() +export class ShareService { + private readonly file_path = settings().getStaticConfig().file_path; + + constructor( + private readonly semesterRepository: SemesterRepository, + private readonly lecturesService: LecturesService, + private readonly semestersService: SemestersService, + private readonly timetablesService: TimetablesService, + ) { + registerFont(join(this.file_path, 'fonts/NotoSansKR-Regular.otf'), { + family: 'NotoSansKR', + }); + registerFont(join(this.file_path, 'fonts/NotoSansKR-Regular.otf'), { + family: 'NotoSansKR', + }); + } + + private drawRoundedRectangle(options: RoundedRectangleOptions) { + const { ctx, x, y, width, height, radius, color } = options; + ctx.fillStyle = color; + ctx.beginPath(); + ctx.moveTo(x + radius, y); + ctx.lineTo(x + width - radius, y); + ctx.quadraticCurveTo(x + width, y, x + width, y + radius); + ctx.lineTo(x + width, y + height - radius); + ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height); + ctx.lineTo(x + radius, y + height); + ctx.quadraticCurveTo(x, y + height, x, y + height - radius); + ctx.lineTo(x, y + radius); + ctx.quadraticCurveTo(x, y, x + radius, y); + ctx.closePath(); + ctx.fill(); + } + + private sliceTextToFitWidth( + text: string, + maxWidth: number, + font: string, + fontSize: number, + ): string[] { + const canvas = createCanvas(100, 100); + const ctx = canvas.getContext('2d'); + ctx.font = `${fontSize}px '${font}'`; + + let currentLine = ''; + const lines = []; + + for (const char of text) { + const testLine = currentLine + char; + const metrics = ctx.measureText(testLine); + const testWidth = metrics.width; + if (testWidth > maxWidth && currentLine !== '') { + lines.push(currentLine); + currentLine = char; + } else { + currentLine = testLine; + } + } + + if (currentLine) { + lines.push(currentLine); + } + + return lines; + } + + private drawText(options: TextOptions) { + const { ctx, x, y, text, font, fontSize, color, align = 'left' } = options; + ctx.fillStyle = color; + ctx.font = `${fontSize}px '${font}'`; + ctx.textAlign = align ? align : 'left'; + ctx.fillText(text, x, y); + } + + private drawTile(options: DrawTileOptions) { + const { + ctx, + x, + y, + width, + height, + title, + professor, + location, + font, + fontSize, + } = options; + const slicedTitle = this.sliceTextToFitWidth(title, width, font, fontSize); + const slicedProfessor = this.sliceTextToFitWidth( + professor, + width, + font, + fontSize, + ); + const slicedLocation = this.sliceTextToFitWidth( + location, + width, + font, + fontSize, + ); + + let textTotalHeight = 0; + const slices: string[] = [ + ...slicedTitle, + ...slicedLocation, + ...slicedProfessor, + ].slice(0, 3); + + textTotalHeight = slices.reduce((total, slice, index) => { + if (slice === '') return total + 2; + return total + fontSize; + }, 0); + + const topPad = (height - textTotalHeight) / 2; + let offsetY = topPad + fontSize - 7; + + slices.forEach((slice, index) => { + if (slice !== '') { + this.drawText({ + ctx, + x, + y: y + offsetY, + text: slice, + font, + fontSize, + color: + 'rgba(0, 0, 0, ' + (index < slicedTitle.length ? 0.8 : 0.5) + ')', + }); + offsetY += fontSize + 5; + } else { + offsetY += 2; + } + }); + } + + private async drawTimetable( + drawTimetableData: DrawTimetableDatas, + ): Promise { + const { + lectures, + timetableType, + semesterName, + isEnglish, + semesterFontSize, + tileFontSize, + } = drawTimetableData; + + const lectureIds = lectures.map((lecture) => lecture.id); + const lectureDetailsMap = + await this.lecturesService.getLectureDetailsForTimetable( + lectureIds, + isEnglish, + ); + + const TIMETABLE_CELL_COLORS = [ + '#F2CECE', + '#F4B3AE', + '#F2BCA0', + '#F0D3AB', + '#F1E1A9', + '#f4f2b3', + '#dbf4be', + '#beedd7', + '#b7e2de', + '#c9eaf4', + '#B4D3ED', + '#B9C5ED', + '#CCC6ED', + '#D8C1F0', + '#EBCAEF', + '#f4badb', + ]; + + const imageTemplatePath = join( + this.file_path, + `img/Image_template_${timetableType}.png`, + ); + const baseImage = await loadImage(imageTemplatePath); + const canvas = createCanvas(baseImage.width, baseImage.height); + const ctx = canvas.getContext('2d'); + ctx.drawImage(baseImage, 0, 0); + + this.drawText({ + ctx, + x: timetableType === '5days' ? 952 : 1302, + y: 78, + text: semesterName, + font: 'NotoSansKR', + fontSize: semesterFontSize, + color: '#CCCCCC', + align: 'right', + }); + + for (const lecture of lectures) { + const color = TIMETABLE_CELL_COLORS[lecture.course_id % 16]; + const lectureDetail = lectureDetailsMap.get(lecture.id); + + for (const classtime of lecture.subject_classtime) { + const { day, begin, end } = classtime; + const beginNumber = begin.getUTCHours() * 60 + begin.getUTCMinutes(); + const endNumber = end.getUTCHours() * 60 + end.getUTCMinutes(); + + const [x, y, width, height] = [ + 178 * day + 76, + (beginNumber * 4) / 3 - 486, + 178 - 7, + ((endNumber - beginNumber) * 4) / 3 - 7, + ]; + + this.drawRoundedRectangle({ + ctx, + x, + y, + width, + height, + radius: 4, + color, + }); + + this.drawTile({ + ctx, + x: x + 12, + y: y + 8, + width: width - 24, + height: height - 16, + title: isEnglish ? lecture.title_en : lecture.title, + professor: lectureDetail?.professorText || '', + location: lectureDetail?.classroomShortStr || '', + font: 'NotoSansKR', + fontSize: tileFontSize, + }); + } + } + + return canvas.toBuffer('image/png'); + } + + async createTimetableImage( + query: IShare.TimetableImageQueryDto, + user: session_userprofile, + ): Promise { + const lectures = await this.timetablesService.getTimetableEntries( + query.timetable, + query.year, + query.semester, + user, + ); + const timetableType = this.timetablesService.getTimetableType(lectures); + const semesterObject = await this.semesterRepository.findSemester( + query.year, + query.semester, + ); + + if (!semesterObject) { + throw new HttpException('Semester not found', HttpStatus.NOT_FOUND); + } + + const isEnglish = !!query.language && query.language.includes('en'); + const semesterName = await this.semestersService.getSemesterName( + semesterObject, + isEnglish ? 'en' : 'kr', + ); + + const drawTimetableData = { + lectures, + timetableType, + semesterName, + isEnglish, + semesterFontSize: 30, + tileFontSize: 24, + }; + + return this.drawTimetable(drawTimetableData); + } + + private async timetableIcal(timetableIcalData: { + name: string; + lectures: ILecture.Raw[]; + semesterObject: subject_semester; + isEnglish: boolean; + }): Promise { + const { name, lectures, semesterObject, isEnglish } = timetableIcalData; + + const calendar = ical({ + name: name, + prodId: '//SPARCS//OTL Plus', + timezone: 'Asia/Seoul', + }); + + const lectureIds = lectures.map((lecture) => lecture.id); + const lectureDetailsMap = + await this.lecturesService.getLectureDetailsForTimetable( + lectureIds, + isEnglish, + ); + + for (const lecture of lectures) { + for (const classtime of lecture.subject_classtime) { + const classroomShortStr = + lectureDetailsMap.get(lecture.id)?.classroomShortStr || 'Unknown'; + + const semesterBeginning = moment.tz( + semesterObject.beginning, + 'Asia/Seoul', + ); + const dayOfWeek = (classtime.day + 1) % 7; + const firstClassDate = semesterBeginning.clone().day(dayOfWeek); + + const eventStart = moment.tz( + { + year: firstClassDate.year(), + month: firstClassDate.month(), + day: firstClassDate.date(), + hour: classtime.begin.getUTCHours(), + minute: classtime.begin.getUTCMinutes(), + second: 0, + }, + 'Asia/Seoul', + ); + + const eventEnd = moment.tz( + { + year: firstClassDate.year(), + month: firstClassDate.month(), + day: firstClassDate.date(), + hour: classtime.end.getUTCHours(), + minute: classtime.end.getUTCMinutes(), + second: 0, + }, + 'Asia/Seoul', + ); + + const event = calendar.createEvent({ + start: eventStart.toDate(), + end: eventEnd.toDate(), + summary: isEnglish ? lecture.title_en : lecture.title, + location: classroomShortStr, + repeating: { + freq: ICalEventRepeatingFreq.WEEKLY, + until: moment(semesterObject.end).toDate(), + }, + timezone: 'Asia/Seoul', + }); + + event.alarms([ + { + type: ICalAlarmType.display, + trigger: 900, + }, + ]); + } + } + + return calendar; + } + + async createTimetableIcal( + query: IShare.TimetableIcalQueryDto, + user: session_userprofile, + ): Promise { + const lectures = await this.timetablesService.getTimetableEntries( + query.timetable, + query.year, + query.semester, + user, + ); + + if (!lectures || lectures.length === 0) { + throw new HttpException('Timetable not found', HttpStatus.NOT_FOUND); + } + + const semesterObject = await this.semesterRepository.findSemester( + query.year, + query.semester, + ); + + if (!semesterObject) { + throw new HttpException('Semester not found', HttpStatus.NOT_FOUND); + } + + const isEnglish = !!query.language && query.language.includes('en'); + const semesterName = await this.semestersService.getSemesterName( + semesterObject, + isEnglish ? 'en' : 'kr', + ); + + const timetableIcalData = { + name: `[OTL] ${semesterName}`, + lectures: lectures, + semesterObject: semesterObject, + isEnglish: isEnglish, + }; + + return this.timetableIcal(timetableIcalData); + } +} diff --git a/src/modules/status/status.controller.ts b/src/modules/status/status.controller.ts new file mode 100644 index 00000000..60363922 --- /dev/null +++ b/src/modules/status/status.controller.ts @@ -0,0 +1,11 @@ +import { Controller, Get } from '@nestjs/common'; +import { Public } from '../../common/decorators/skip-auth.decorator'; + +@Controller('api/status') +export class StatusController { + @Get() + @Public() + getStatus() { + return 'I am Healthy!'; + } +} diff --git a/src/modules/status/status.module.ts b/src/modules/status/status.module.ts new file mode 100644 index 00000000..5c116e3e --- /dev/null +++ b/src/modules/status/status.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { StatusController } from './status.controller'; +import { StatusService } from './status.service'; + +@Module({ + controllers: [StatusController], + providers: [StatusService], +}) +export class StatusModule {} diff --git a/src/modules/status/status.service.ts b/src/modules/status/status.service.ts new file mode 100644 index 00000000..aebcee14 --- /dev/null +++ b/src/modules/status/status.service.ts @@ -0,0 +1,4 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class StatusService {} diff --git a/src/modules/template/template.controller.ts b/src/modules/template/template.controller.ts new file mode 100644 index 00000000..a44f4104 --- /dev/null +++ b/src/modules/template/template.controller.ts @@ -0,0 +1,4 @@ +import { Controller } from '@nestjs/common'; + +@Controller('template') +export class TemplateController {} diff --git a/src/modules/template/template.module.ts b/src/modules/template/template.module.ts new file mode 100644 index 00000000..a292c765 --- /dev/null +++ b/src/modules/template/template.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { TemplateController } from './template.controller'; +import { TemplateService } from './template.service'; + +@Module({ + controllers: [TemplateController], + providers: [TemplateService], +}) +export class TemplateModule {} diff --git a/src/modules/template/template.service.ts b/src/modules/template/template.service.ts new file mode 100644 index 00000000..f5c2f667 --- /dev/null +++ b/src/modules/template/template.service.ts @@ -0,0 +1,4 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class TemplateService {} diff --git a/src/modules/timetables/timetables.controller.ts b/src/modules/timetables/timetables.controller.ts new file mode 100644 index 00000000..013c5192 --- /dev/null +++ b/src/modules/timetables/timetables.controller.ts @@ -0,0 +1,112 @@ +import { + Body, + Controller, + Delete, + Get, + Param, + Post, + Query, +} from '@nestjs/common'; +import { session_userprofile } from '@prisma/client'; +import { ITimetable } from 'src/common/interfaces'; +import { GetUser } from '../../common/decorators/get-user.decorator'; +import { toJsonTimetable } from '../../common/interfaces/serializer/timetable.serializer'; +import { LecturesService } from '../lectures/lectures.service'; +import { TimetablesService } from './timetables.service'; + +@Controller('/api/users/:userId/timetables') +export class TimetablesController { + constructor( + private readonly timetablesService: TimetablesService, + private readonly lectureService: LecturesService, + ) {} + + @Get() + async getTimetables( + @Param('userId') userId: number, + @Query() query: ITimetable.QueryDto, + @GetUser() user: session_userprofile, + ) { + const timeTableList = await this.timetablesService.getTimetables( + query, + user, + ); + return timeTableList.map((timeTable) => toJsonTimetable(timeTable)); + } + + @Get('/:timetableId') + async getTimeTable( + @Param('userId') userId: number, + @Param('timetableId') timetableId: number, + @GetUser() user: session_userprofile, + ) { + const timeTable = await this.timetablesService.getTimetable(timetableId); + return toJsonTimetable(timeTable); + } + + @Delete('/:timetableId') + async deleteTimetable( + @Param('userId') userId: number, + @Param('timetableId') timetableId: number, + @GetUser() user: session_userprofile, + ) { + await this.timetablesService.deleteTimetable(user, timetableId); + return null; + } + + @Post() + async createTimetable( + @Body() timeTableBody: ITimetable.CreateDto, + @GetUser() user: session_userprofile, + ) { + const timeTable = await this.timetablesService.createTimetable( + timeTableBody, + user, + ); + return toJsonTimetable(timeTable); + } + + @Post('/:timetableId/add-lecture') + async addLectureToTimetable( + @Param('timetableId') timetableId: number, + @Body() body: ITimetable.AddLectureDto, + ) { + const timeTable = await this.timetablesService.addLectureToTimetable( + timetableId, + body, + ); + return toJsonTimetable(timeTable); + } + + @Post('/:timetableId/remove-lecture') + async removeLectureFromTimetable( + @Param('timetableId') timetableId: number, + @Body() body: ITimetable.AddLectureDto, + ) { + const timeTable = await this.timetablesService.removeLectureFromTimetable( + timetableId, + body, + ); + return toJsonTimetable(timeTable); + } + + @Post('/:timetableId/reorder') + async reorderTimetable( + /** + * @todo use user by auth instead of userId by endpoint param + * userId should be removed from endpoint in the future + * since each user should only control their own timetable + */ + @Param('userId') userId: number, + @Param('timetableId') timetableId: number, + @Body() body: ITimetable.ReorderTimetableDto, + @GetUser() user: session_userprofile, + ) { + const timeTable = await this.timetablesService.reorderTimetable( + user, + timetableId, + body, + ); + return toJsonTimetable(timeTable); + } +} diff --git a/src/modules/timetables/timetables.module.ts b/src/modules/timetables/timetables.module.ts new file mode 100644 index 00000000..d888670b --- /dev/null +++ b/src/modules/timetables/timetables.module.ts @@ -0,0 +1,13 @@ +import { Module } from '@nestjs/common'; +import { PrismaModule } from '../../prisma/prisma.module'; +import { LecturesModule } from '../lectures/lectures.module'; +import { TimetablesController } from './timetables.controller'; +import { TimetablesService } from './timetables.service'; + +@Module({ + imports: [PrismaModule, LecturesModule], + controllers: [TimetablesController], + providers: [TimetablesService], + exports: [TimetablesService], +}) +export class TimetablesModule {} diff --git a/src/modules/timetables/timetables.service.ts b/src/modules/timetables/timetables.service.ts new file mode 100644 index 00000000..6b289af1 --- /dev/null +++ b/src/modules/timetables/timetables.service.ts @@ -0,0 +1,309 @@ +import { + BadRequestException, + HttpException, + HttpStatus, + Injectable, +} from '@nestjs/common'; +import { session_userprofile } from '@prisma/client'; +import { ELecture } from 'src/common/entities/ELecture'; +import { ITimetable } from 'src/common/interfaces'; +import { + orderFilter, + validateYearAndSemester, +} from '../../common/utils/search.utils'; +import { PrismaService } from '../../prisma/prisma.service'; +import { LectureRepository } from '../../prisma/repositories/lecture.repository'; +import { SemesterRepository } from '../../prisma/repositories/semester.repository'; +import { TimetableRepository } from '../../prisma/repositories/timetable.repository'; +import { Transactional } from '@nestjs-cls/transactional'; + +@Injectable() +export class TimetablesService { + constructor( + private readonly prismaService: PrismaService, + private readonly timetableRepository: TimetableRepository, + private readonly lectureRepository: LectureRepository, + private readonly semesterRepository: SemesterRepository, + ) {} + + async getTimetables(query: ITimetable.QueryDto, user: session_userprofile) { + const { year, semester, order, offset, limit } = query; + + const orderBy = orderFilter(order); + const paginationAndSorting = { + skip: offset, + take: limit, + orderBy: orderBy, + }; + + const timetables = await this.timetableRepository.getTimetables( + user, + year, + semester, + paginationAndSorting, + ); + return timetables; + } + + async getTimetable(timetableId: number) { + return await this.timetableRepository.getTimeTableById(timetableId); + } + + @Transactional() + async createTimetable( + timeTableBody: ITimetable.CreateDto, + user: session_userprofile, + ) { + const { year, semester } = timeTableBody; + if ( + !(await validateYearAndSemester(year, semester, this.semesterRepository)) + ) { + throw new BadRequestException( + "Wrong fields 'year' and 'semester' in request data", + ); + } + + let arrangeOrder = 0; + const relatedTimeTables = await this.timetableRepository.getTimetableBasics( + user, + year, + semester, + { orderBy: { arrange_order: 'asc' } }, + ); + if (relatedTimeTables.length > 0) { + arrangeOrder = + relatedTimeTables[relatedTimeTables.length - 1].arrange_order + 1; + } + + /* + 기존 파이썬 코드는 lectureId list가 오면 냅다 검색해서 timetable에 붙이고 return 해버렸으나, + timetable이 속한 semester, year와 lecture들의 semester, year를 비교해서 일치하는 것만 붙이는 것으로 변경 + + 또한 lectureId 중 데이터베이스에 존재하지 않는 것이 하나라도 있으면, 바로 에러를 내버리는 식으로 구현되었으나, + 가능한 것들은 모두 붙여서 저장하는 것으로 변경. + */ + const lectureIds = timeTableBody.lectures; + const lectures = await this.lectureRepository.getLectureByIds(lectureIds); + const filteredLectures = lectures.filter( + (lecture) => + lecture.semester === timeTableBody.semester && + lecture.year === timeTableBody.year, + ); + return await this.timetableRepository.createTimetable( + user, + year, + semester, + arrangeOrder, + filteredLectures, + ); + } + + @Transactional() + async addLectureToTimetable( + timeTableId: number, + body: ITimetable.AddLectureDto, + ) { + const lectureId = body.lecture; + const lecture = await this.lectureRepository.getLectureBasicById(lectureId); + const timetable = await this.timetableRepository.getTimeTableBasicById( + timeTableId, + ); + if (!lecture) { + throw new BadRequestException( + "Wrong field \\'lecture\\' in request data", + ); + } + if ( + !( + lecture.year == timetable.year && lecture.semester == timetable.semester + ) + ) { + throw new BadRequestException( + "Wrong field \\'lecture\\' in request data", + ); + } + await this.timetableRepository.addLectureToTimetable( + timeTableId, + lectureId, + ); + return await this.timetableRepository.getTimeTableById(timeTableId); + } + + @Transactional() + async removeLectureFromTimetable( + timeTableId: number, + body: ITimetable.AddLectureDto, + ) { + const lectureId = body.lecture; + const lecture = await this.lectureRepository.getLectureBasicById(lectureId); + const timetable = await this.timetableRepository.getTimeTableBasicById( + timeTableId, + ); + if (!lecture) { + throw new BadRequestException( + "Wrong field \\'lecture\\' in request data", + ); + } + if ( + !( + lecture.year == timetable.year && lecture.semester == timetable.semester + ) + ) { + throw new BadRequestException( + "Wrong field \\'lecture\\' in request data", + ); + } + await this.timetableRepository.removeLectureFromTimetable( + timeTableId, + lectureId, + ); + return await this.timetableRepository.getTimeTableById(timeTableId); + } + + @Transactional() + async deleteTimetable(user: session_userprofile, timetableId: number) { + return await this.prismaService.$transaction(async (tx) => { + const { semester, year, arrange_order } = + await this.timetableRepository.getTimeTableById(timetableId); + await this.timetableRepository.deleteById(timetableId); + const relatedTimeTables = await this.timetableRepository.getTimetables( + user, + year, + semester, + ); + const timeTablesToBeUpdated = relatedTimeTables + .filter((timeTable) => timeTable.arrange_order > arrange_order) + .map((timeTable) => { + return { + id: timeTable.id, + arrange_order: timeTable.arrange_order - 1, + }; + }); + await Promise.all( + timeTablesToBeUpdated.map(async (updateElem) => { + return this.timetableRepository.updateOrder( + updateElem.id, + updateElem.arrange_order, + ); + }), + ); + }); + } + + @Transactional() + async reorderTimetable( + user: session_userprofile, + timetableId: number, + body: ITimetable.ReorderTimetableDto, + ) { + return await this.prismaService.$transaction(async (tx) => { + const { arrange_order: targetArrangeOrder } = body; + const targetTimetable = await this.timetableRepository.getTimeTableById( + timetableId, + ); + if (targetTimetable.user_id !== user.id) { + throw new BadRequestException('User is not owner of timetable'); + } + if (targetArrangeOrder === targetTimetable.arrange_order) { + return targetTimetable; + } + + const relatedTimeTables = await this.timetableRepository.getTimetables( + user, + targetTimetable.year, + targetTimetable.semester, + ); + if ( + targetArrangeOrder < 0 || + targetArrangeOrder >= relatedTimeTables.length + ) { + throw new BadRequestException('Wrong field arrange_order in request'); + } + + let timeTablesToBeUpdated: { id: number; arrange_order: number }[] = []; + if (targetArrangeOrder < targetTimetable.arrange_order) { + timeTablesToBeUpdated = relatedTimeTables + .filter( + (timeTable) => + timeTable.arrange_order >= targetArrangeOrder && + timeTable.arrange_order < targetTimetable.arrange_order, + ) + .map((timeTable) => { + return { + id: timeTable.id, + arrange_order: timeTable.arrange_order + 1, + }; + }); + } else if (targetArrangeOrder > targetTimetable.arrange_order) { + timeTablesToBeUpdated = relatedTimeTables + .filter( + (timeTable) => + timeTable.arrange_order <= targetArrangeOrder && + timeTable.arrange_order > targetTimetable.arrange_order, + ) + .map((timeTable) => { + return { + id: timeTable.id, + arrange_order: timeTable.arrange_order - 1, + }; + }); + } + + await Promise.all( + timeTablesToBeUpdated.map(async (timetable) => { + return this.timetableRepository.updateOrder( + timetable.id, + timetable.arrange_order, + ); + }), + ); + const updatedTimeTable = await this.timetableRepository.updateOrder( + targetTimetable.id, + targetArrangeOrder, + ); + return updatedTimeTable; + }); + } + + public getTimetableType( + lectures: ELecture.WithClasstime[], + ): '5days' | '7days' { + return lectures.some((lecture) => + lecture.subject_classtime.some((classtime) => classtime.day >= 5), + ) + ? '7days' + : '5days'; + } + + public async getTimetableEntries( + timetableId: number, + year: number, + semester: number, + user: session_userprofile, + ): Promise { + if (!user) { + throw new HttpException( + 'User profile is required', + HttpStatus.BAD_REQUEST, + ); + } + + const MY_TIMETABLE_ID = -1; + + let timetableDetails; + if (timetableId === MY_TIMETABLE_ID) { + return await this.lectureRepository.getUserLecturesByYearSemester( + user.id, + year, + semester, + ); + } else { + timetableDetails = + await this.timetableRepository.getLecturesWithClassTimes(timetableId); + if (!timetableDetails) { + throw new HttpException('No such timetable', HttpStatus.NOT_FOUND); + } + } + return timetableDetails.map((detail) => detail.subject_lecture); + } +} diff --git a/src/modules/tracks/tracks.controller.ts b/src/modules/tracks/tracks.controller.ts new file mode 100644 index 00000000..3542acfa --- /dev/null +++ b/src/modules/tracks/tracks.controller.ts @@ -0,0 +1,14 @@ +import { Controller, Get } from '@nestjs/common'; +import { TracksService } from './tracks.service'; +import { Public } from '@src/common/decorators/skip-auth.decorator'; + +@Controller('/api/tracks') +export class TracksController { + constructor(private readonly tracksService: TracksService) {} + + @Get() + @Public() + async getTracks() { + return await this.tracksService.getAllTrack(); + } +} diff --git a/src/modules/tracks/tracks.module.ts b/src/modules/tracks/tracks.module.ts new file mode 100644 index 00000000..fc9883ae --- /dev/null +++ b/src/modules/tracks/tracks.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { PrismaModule } from 'src/prisma/prisma.module'; +import { TracksController } from './tracks.controller'; +import { TracksService } from './tracks.service'; + +@Module({ + imports: [PrismaModule], + controllers: [TracksController], + providers: [TracksService], +}) +export class TracksModule {} diff --git a/src/modules/tracks/tracks.service.ts b/src/modules/tracks/tracks.service.ts new file mode 100644 index 00000000..86e98b12 --- /dev/null +++ b/src/modules/tracks/tracks.service.ts @@ -0,0 +1,22 @@ +import { Injectable } from '@nestjs/common'; +import { + toJsonAdditionalTrack, + toJsonGeneralTrack, + toJsonMajorTrack, +} from 'src/common/interfaces/serializer/track.serializer'; +import { TracksRepository } from 'src/prisma/repositories/track.repository'; + +@Injectable() +export class TracksService { + constructor(private readonly TracksRepository: TracksRepository) {} + + public async getAllTrack() { + const { generalTracks, majorTracks, addtionalTracks } = + await this.TracksRepository.getAllTracks(); + return { + general: generalTracks.map(toJsonGeneralTrack), + major: majorTracks.map(toJsonMajorTrack), + additional: addtionalTracks.map(toJsonAdditionalTrack), + }; + } +} diff --git a/src/modules/user/user.controller.ts b/src/modules/user/user.controller.ts index e69de29b..31ec7d2e 100644 --- a/src/modules/user/user.controller.ts +++ b/src/modules/user/user.controller.ts @@ -0,0 +1,38 @@ +import { Controller, Get, HttpException, Param, Query } from '@nestjs/common'; +import { session_userprofile } from '@prisma/client'; +import { GetUser } from 'src/common/decorators/get-user.decorator'; +import { ICourse } from 'src/common/interfaces'; +import { IReview } from 'src/common/interfaces/IReview'; +import { IUser } from 'src/common/interfaces/IUser'; +import { UserService } from './user.service'; + +@Controller('api/users') +export class UserController { + constructor(private readonly userService: UserService) {} + + @Get(':user_id/taken-courses') + async getUserTakenCourses( + @Query() query: IUser.TakenCoursesQueryDto, + @Param('user_id') userId: number, + @GetUser() user: session_userprofile, + ): Promise { + if (userId === user.id) { + return await this.userService.getUserTakenCourses(query, user); + } else { + throw new HttpException("Can't find user", 401); + } + } + + @Get(':user_id/liked-reviews') + async getUserLikedReviews( + @Query() query: IUser.ReviewLikedQueryDto, + @Param('user_id') userId: number, + @GetUser() user: session_userprofile, + ): Promise<(IReview.Basic & { userspecific_is_liked: boolean })[]> { + if (userId === user.id) { + return await this.userService.getUserLikedReviews(user, userId, query); + } else { + throw new HttpException("Can't find user", 401); + } + } +} diff --git a/src/modules/user/user.module.ts b/src/modules/user/user.module.ts index e69de29b..2be35f8d 100644 --- a/src/modules/user/user.module.ts +++ b/src/modules/user/user.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { PrismaModule } from 'src/prisma/prisma.module'; +import { UserController } from './user.controller'; +import { UserService } from './user.service'; + +@Module({ + imports: [PrismaModule], + controllers: [UserController], + providers: [UserService], +}) +export class UserModule {} diff --git a/src/modules/user/user.service.ts b/src/modules/user/user.service.ts index cabf448d..95bd46f5 100644 --- a/src/modules/user/user.service.ts +++ b/src/modules/user/user.service.ts @@ -1,15 +1,158 @@ -import { UserRepository } from "../../prisma/repositories/user.repository"; -import { Injectable, NotFoundException } from "@nestjs/common"; +import { Injectable, NotFoundException } from '@nestjs/common'; +import { session_userprofile } from '@prisma/client'; +import { ICourse } from 'src/common/interfaces'; +import { IReview } from 'src/common/interfaces/IReview'; +import { IUser } from 'src/common/interfaces/IUser'; +import { + addIsRead, + toJsonCourseDetail, +} from 'src/common/interfaces/serializer/course.serializer'; +import { ResearchLecture } from '../../common/interfaces/constants/lecture'; +import { toJsonDepartment } from '../../common/interfaces/serializer/department.serializer'; +import { toJsonLectureDetail } from '../../common/interfaces/serializer/lecture.serializer'; +import { toJsonReview } from '../../common/interfaces/serializer/review.serializer'; +import { getRepresentativeLecture } from '../../common/utils/lecture.utils'; +import { DepartmentRepository } from '../../prisma/repositories/department.repository'; +import { LectureRepository } from '../../prisma/repositories/lecture.repository'; +import { ReviewsRepository } from '../../prisma/repositories/review.repository'; +import { UserRepository } from '../../prisma/repositories/user.repository'; +import { CourseRepository } from './../../prisma/repositories/course.repository'; @Injectable() export class UserService { - constructor(private readonly userRepository: UserRepository) { - } + constructor( + private readonly userRepository: UserRepository, + private readonly lectureRepository: LectureRepository, + private readonly departmentRepository: DepartmentRepository, + private readonly reviewRepository: ReviewsRepository, + private readonly courseRepository: CourseRepository, + ) {} - public async findBySid(sid: string){ - const user = this.userRepository.findBySid(sid); + public async findBySid(sid: string) { + const user = this.userRepository.findBySid(sid); if (!user) { throw new NotFoundException(`Can't find user with sid: ${sid}`); } } -} \ No newline at end of file + + public async getProfile(user: session_userprofile): Promise { + const [ + department, + favoriteDepartments, + majors, + minors, + specializedMajors, + reviewWritableLectures, + takenLectures, + writtenReviews, + ] = await Promise.all([ + this.departmentRepository.getDepartmentOfUser(user), + this.departmentRepository.getFavoriteDepartments(user), + this.departmentRepository.getMajors(user), + this.departmentRepository.getMinors(user), + this.departmentRepository.getSpecializedMajors(user), + this.lectureRepository.findReviewWritableLectures(user, new Date()), + this.lectureRepository.getTakenLectures(user), + this.reviewRepository.findReviewByUser(user), + ]); + const departments = [ + ...majors, + ...minors, + ...specializedMajors, + ...favoriteDepartments, + ]; + const researchLectures = Object.values(ResearchLecture); + const timeTableLectures = takenLectures.filter( + (lecture) => !researchLectures.includes(lecture.type_en), + ); + + return { + id: user.id, + email: user.email ?? '', + student_id: user.student_id, + firstName: user.first_name, + lastName: user.last_name, + department: department ? toJsonDepartment(department) : null, + majors: majors.map((major) => toJsonDepartment(major)), + departments: departments.map((department) => + toJsonDepartment(department), + ), + favorite_departments: favoriteDepartments.map((department) => + toJsonDepartment(department), + ), + review_writable_lectures: reviewWritableLectures.map((lecture) => + toJsonLectureDetail(lecture), + ), + my_timetable_lectures: timeTableLectures.map((lecture) => + toJsonLectureDetail(lecture), + ), + reviews: writtenReviews.map((review) => toJsonReview(review)), + }; + } + + async getUserTakenCourses( + query: IUser.TakenCoursesQueryDto, + user: session_userprofile, + ): Promise { + const DEFAULT_ORDER = ['old_code']; + const takenLectures = await this.lectureRepository.getTakenLectures(user); + const takenLecturesId = takenLectures.map((lecture) => lecture.id); + const courses = await this.courseRepository.getUserTakenCourses( + takenLecturesId, + query.order ?? DEFAULT_ORDER, + ); + return courses.map((course) => { + const representativeLecture = getRepresentativeLecture(course.lecture); + const professorRaw = course.subject_course_professors.map( + (x) => x.professor, + ); + const result = toJsonCourseDetail( + course, + representativeLecture, + professorRaw, + ); + const subjectCourseUser = course.subject_courseuser.filter( + (e) => e.user_profile_id === user.id && e.course_id === course.id, + )[0]; + if (!subjectCourseUser || !course.latest_written_datetime) + return addIsRead(result, false); + else + return addIsRead( + result, + course.latest_written_datetime > + subjectCourseUser?.latest_read_datetime, + ); + }); + } + + async getUserLikedReviews( + user: session_userprofile, + userId: number, + query: IUser.ReviewLikedQueryDto, + ): Promise<(IReview.Basic & { userspecific_is_liked: boolean })[]> { + const MAX_LIMIT = 300; + const DEFAULT_ORDER = ['-written_datetime', '-id']; + + const reviews = await this.reviewRepository.getLikedReviews( + userId, + query.order ?? DEFAULT_ORDER, + query.offset ?? 0, + query.limit ?? MAX_LIMIT, + ); + + const result = await Promise.all( + reviews.map(async (review) => { + const result = toJsonReview(review); + const isLiked: boolean = await this.reviewRepository.isLiked( + review.id, + user.id, + ); + return Object.assign(result, { + userspecific_is_liked: isLiked, + }); + }), + ); + + return result; + } +} diff --git a/src/modules/wishlist/wishlist.controller.ts b/src/modules/wishlist/wishlist.controller.ts new file mode 100644 index 00000000..8f3a5b14 --- /dev/null +++ b/src/modules/wishlist/wishlist.controller.ts @@ -0,0 +1,52 @@ +import { + Body, + Controller, + Get, + Param, + Post, + UnauthorizedException, +} from '@nestjs/common'; +import { session_userprofile } from '@prisma/client'; +import { GetUser } from 'src/common/decorators/get-user.decorator'; +import { IWishlist } from 'src/common/interfaces/IWishlist'; +import { toJsonWishlist } from 'src/common/interfaces/serializer/wishlist.serializer'; +import { WishlistService } from './wishlist.service'; + +@Controller('api/users/:userId/wishlist') +export class WishlistController { + constructor(private readonly wishlistService: WishlistService) {} + + @Get() + async getLectures( + @Param('userId') userId: number, + @GetUser() user: session_userprofile, + ) { + if (userId !== user.id) throw new UnauthorizedException(); // TODO: Better message + const wishlist = await this.wishlistService.getWishlistWithLectures( + user.id, + ); + return toJsonWishlist(wishlist); + } + + @Post('add-lecture') + async addLecture( + @Param('userId') userId: number, + @Body() body: IWishlist.AddLectureDto, + @GetUser() user: session_userprofile, + ) { + if (userId !== user.id) throw new UnauthorizedException(); // TODO: Better message + const wishlist = await this.wishlistService.addLecture(user.id, body); + return toJsonWishlist(wishlist); + } + + @Post('remove-lecture') + async removeLecture( + @Param('userId') userId: number, + @Body() body: IWishlist.RemoveLectureDto, + @GetUser() user: session_userprofile, + ) { + if (userId !== user.id) throw new UnauthorizedException(); // TODO: Better message + const wishlist = await this.wishlistService.removeLecture(user.id, body); + return toJsonWishlist(wishlist); + } +} diff --git a/src/modules/wishlist/wishlist.module.ts b/src/modules/wishlist/wishlist.module.ts new file mode 100644 index 00000000..0321b8a8 --- /dev/null +++ b/src/modules/wishlist/wishlist.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { PrismaModule } from 'src/prisma/prisma.module'; +import { WishlistController } from './wishlist.controller'; +import { WishlistService } from './wishlist.service'; + +@Module({ + imports: [PrismaModule], + controllers: [WishlistController], + providers: [WishlistService], +}) +export class WishlistModule {} diff --git a/src/modules/wishlist/wishlist.service.ts b/src/modules/wishlist/wishlist.service.ts new file mode 100644 index 00000000..7c9aae1d --- /dev/null +++ b/src/modules/wishlist/wishlist.service.ts @@ -0,0 +1,65 @@ +import { + BadRequestException, + Injectable, + NotFoundException, +} from '@nestjs/common'; +import { IWishlist } from 'src/common/interfaces/IWishlist'; +import { LectureRepository } from 'src/prisma/repositories/lecture.repository'; +import { WishlistRepository } from 'src/prisma/repositories/wishlist.repository'; +import { Transactional } from '@nestjs-cls/transactional'; + +@Injectable() +export class WishlistService { + constructor( + private readonly wishlistRepository: WishlistRepository, + private readonly lectureRepository: LectureRepository, + ) {} + + async getWishlistWithLectures(userId: number) { + return await this.wishlistRepository.getOrCreateWishlist(userId); + } + + @Transactional() + async addLecture(userId: number, body: IWishlist.AddLectureDto) { + const wishlist = await this.wishlistRepository.getOrCreateWishlist(userId); + + if ( + await this.wishlistRepository.getLectureInWishlist( + wishlist.id, + body.lecture, + ) + ) + throw new BadRequestException("Wrong field 'lecture' in request data"); + + const lecture = await this.lectureRepository.getLectureById(body.lecture); + if (!lecture) + throw new NotFoundException( + `Lecture with id ${body.lecture} does not exist`, + ); + + await this.wishlistRepository.addLecture(wishlist.id, lecture.id); + const updatedWishlist = + await this.wishlistRepository.getWishlistWithLectures(wishlist.id); + if (!updatedWishlist) throw new Error('Wishlist not found'); + return updatedWishlist; + } + + @Transactional() + async removeLecture(userId: number, body: IWishlist.RemoveLectureDto) { + const wishlist = await this.wishlistRepository.getOrCreateWishlist(userId); + + if ( + !(await this.wishlistRepository.getLectureInWishlist( + wishlist.id, + body.lecture, + )) + ) + throw new BadRequestException("Wrong field 'lecture' in request data"); + + await this.wishlistRepository.removeLecture(wishlist.id, body.lecture); + const updatedWishlist = + await this.wishlistRepository.getWishlistWithLectures(wishlist.id); + if (!updatedWishlist) throw new Error('Wishlist not found'); + return updatedWishlist; + } +} diff --git a/src/prisma/custom-prisma-client.ts b/src/prisma/custom-prisma-client.ts new file mode 100644 index 00000000..b33817a8 --- /dev/null +++ b/src/prisma/custom-prisma-client.ts @@ -0,0 +1,53 @@ +// import { Prisma, PrismaClient } from '@prisma/client'; + +import { Prisma } from '@prisma/client'; +import { mediator } from './middleware/mediator'; + +export const signalExtension = Prisma.defineExtension((client) => { + return client.$extends({ + name: 'signal', + query: { + $allModels: { + async $allOperations({ model, operation, args, query }) { + const signal = mediator(model); + if (signal) { + const preExecute = await signal.preExecute(operation, args); + if (!preExecute) { + throw new Error('Middleware Error'); + } + const result = await query(args); + const postExecute = await signal.postExecute( + operation, + args, + result, + ); + if (!postExecute) { + throw new Error('Middleware Error'); + } + return result; + } + return query(args); + }, + }, + }, + }); +}); +// export const customPrismaClient = ( +// prismaClient: PrismaClient, +// )=> { +// return prismaClient.$extends(signalExtension); +// }; + +// export class PrismaClientExtended extends PrismaClient { +// customPrismaClient!: CustomPrismaClient; + +// get client() { +// if(!this.customPrismaClient) { +// this.customPrismaClient = customPrismaClient(this); +// } + +// return this.customPrismaClient; +// } +// } + +// export type CustomPrismaClient = ReturnType; diff --git a/src/prisma/middleware/mediator.ts b/src/prisma/middleware/mediator.ts new file mode 100644 index 00000000..6dfc566a --- /dev/null +++ b/src/prisma/middleware/mediator.ts @@ -0,0 +1,36 @@ +import { IPrismaMiddleware } from './../../common/interfaces/IPrismaMiddleware'; +import { CourseMiddleware } from './prisma.course'; +import { DepartmentMiddleware } from './prisma.department'; +import { LectureMiddleware } from './prisma.lecture'; +import { LectureProfessorsMiddleware } from './prisma.lectureprofessors'; +import { ReviewMiddleware } from './prisma.reviews'; +import { ReviewVoteMiddleware } from './prisma.reviewvote'; +import { SemesterMiddleware } from './prisma.semester'; +import { TimetableMiddleware } from './prisma.timetable'; +import { TimetableLectureMiddleware } from './prisma.timetablelecture'; + +export const mediator = ( + model: string | undefined, +): IPrismaMiddleware.IPrismaMiddleware | null => { + if (model === 'review_review') { + return ReviewMiddleware.getInstance(); + } else if (model === 'review_reviewvote') { + return ReviewVoteMiddleware.getInstance(); + } else if (model === 'timetable_timetable') { + return TimetableMiddleware.getInstance(); + } else if (model === 'timetable_timetable_lectures') { + return TimetableLectureMiddleware.getInstance(); + } else if (model === 'subject_semester') { + return SemesterMiddleware.getInstance(); + } else if (model === 'subject_lecture_professors') { + return LectureProfessorsMiddleware.getInstance(); + } else if (model === 'subject_lecture') { + return LectureMiddleware.getInstance(); + } else if (model === 'subject_course') { + return CourseMiddleware.getInstance(); + } else if (model === 'subject_department') { + return DepartmentMiddleware.getInstance(); + } + return null; +}; +// todo : moduleRef 적용해보기 diff --git a/src/prisma/middleware/prisma.course.ts b/src/prisma/middleware/prisma.course.ts new file mode 100644 index 00000000..362b2323 --- /dev/null +++ b/src/prisma/middleware/prisma.course.ts @@ -0,0 +1,39 @@ +import { IPrismaMiddleware } from 'src/common/interfaces/IPrismaMiddleware'; +import { PrismaService } from '../prisma.service'; + +export class CourseMiddleware implements IPrismaMiddleware.IPrismaMiddleware { + private static instance: CourseMiddleware; + private prisma: PrismaService; + + constructor(prisma: PrismaService) { + this.prisma = prisma; + } + + async preExecute( + operations: IPrismaMiddleware.operationType, + args: any, + ): Promise { + return true; + } + + async postExecute( + operations: IPrismaMiddleware.operationType, + args: any, + result: any, + ): Promise { + if (operations === 'create') { + //todo: cache delete + } + return true; + } + + static initialize(prisma: PrismaService) { + if (!CourseMiddleware.instance) { + CourseMiddleware.instance = new CourseMiddleware(prisma); + } + } + + static getInstance(): CourseMiddleware { + return CourseMiddleware.instance; + } +} diff --git a/src/prisma/middleware/prisma.department.ts b/src/prisma/middleware/prisma.department.ts new file mode 100644 index 00000000..6ecd924a --- /dev/null +++ b/src/prisma/middleware/prisma.department.ts @@ -0,0 +1,41 @@ +import { IPrismaMiddleware } from 'src/common/interfaces/IPrismaMiddleware'; +import { PrismaService } from '../prisma.service'; + +export class DepartmentMiddleware + implements IPrismaMiddleware.IPrismaMiddleware +{ + private static instance: DepartmentMiddleware; + private prisma: PrismaService; + + constructor(prisma: PrismaService) { + this.prisma = prisma; + } + + async preExecute( + operations: IPrismaMiddleware.operationType, + args: any, + ): Promise { + return true; + } + + async postExecute( + operations: IPrismaMiddleware.operationType, + args: any, + result: any, + ): Promise { + if (operations === 'create') { + //todo: cache delete + } + return true; + } + + static initialize(prisma: PrismaService) { + if (!DepartmentMiddleware.instance) { + DepartmentMiddleware.instance = new DepartmentMiddleware(prisma); + } + } + + static getInstance(): DepartmentMiddleware { + return DepartmentMiddleware.instance; + } +} diff --git a/src/prisma/middleware/prisma.lecture.ts b/src/prisma/middleware/prisma.lecture.ts new file mode 100644 index 00000000..c62f5483 --- /dev/null +++ b/src/prisma/middleware/prisma.lecture.ts @@ -0,0 +1,171 @@ +import { Prisma, subject_lecture } from '@prisma/client'; +import { IPrismaMiddleware } from 'src/common/interfaces/IPrismaMiddleware'; +import { XOR } from '@src/common/types/types'; +import { PrismaService } from '../prisma.service'; + +export class LectureMiddleware implements IPrismaMiddleware.IPrismaMiddleware { + private static instance: LectureMiddleware; + private prisma: PrismaService; + + constructor(prisma: PrismaService) { + this.prisma = prisma; + } + + async preExecute( + operations: IPrismaMiddleware.operationType, + args: any, + ): Promise { + return true; + } + + async postExecute( + operations: IPrismaMiddleware.operationType, + args: any, + result: any, + ): Promise { + if ( + operations === 'create' || + operations === 'update' || + operations === 'upsert' + ) { + const t: XOR< + Prisma.subject_lectureUpdateInput, + Prisma.subject_lectureUncheckedUpdateInput + > = args?.data; + if ( + !t.common_title && + !t.class_title && + !t.common_title_en && + !t.class_title_en + ) { + if (this._checkClassTitleUpdateRequired(result)) { + await this.updateClassTitle(result); + } + } + + if (!(operations === 'create')) { + //todo: cache delete + } + } + return true; + } + + static initialize(prisma: PrismaService) { + if (!LectureMiddleware.instance) { + LectureMiddleware.instance = new LectureMiddleware(prisma); + } + } + + static getInstance(): LectureMiddleware { + return LectureMiddleware.instance; + } + + private _checkClassTitleUpdateRequired(lecture: subject_lecture) { + const isTitleEqual = + lecture.common_title && + lecture.class_title && + [ + lecture.common_title + lecture.class_title, + lecture.common_title, + ].includes(lecture.title); + const isTitleEnEqual = + lecture.common_title_en && + lecture.class_title_en && + [ + lecture.common_title_en + lecture.class_title_en, + lecture.common_title_en, + ].includes(lecture.title_en); + return !(isTitleEqual && isTitleEnEqual); + } + + private async updateClassTitle(lecture: subject_lecture) { + const lectures = await this.prisma.subject_lecture.findMany({ + where: { + course_id: lecture.course_id, + deleted: false, + year: lecture.year, + semester: lecture.semester, + }, + }); + await this._addTitleFormat(lectures); + await this._addTitleFormatEn(lectures); + } + + private async _addTitleFormat(lectures: subject_lecture[]) { + if (lectures.length === 1) { + const title = lectures[0].title; + const commonTitle = title.endsWith('>') + ? title.substring(0, title.indexOf('<')) + : title; + await this.update(lectures, commonTitle, 'title'); + } else { + const commonTitle = this.lcsFront( + lectures.map((lecture) => lecture.title), + ); + await this.update(lectures, commonTitle, 'title'); + } + } + + private async _addTitleFormatEn(lectures: subject_lecture[]) { + if (lectures.length === 1) { + const title = lectures[0].title_en; + const commonTitle = title.endsWith('>') + ? title.substring(0, title.indexOf('<')) + : title; + await this.update(lectures, commonTitle, 'title_en'); + } else { + const commonTitle = this.lcsFront( + lectures.map((lecture) => lecture.title_en), + ); + await this.update(lectures, commonTitle, 'title_en'); + } + } + + private async update( + lectures: subject_lecture[], + commonTitle: string, + updateType: 'title' | 'title_en', + ) { + await Promise.all( + lectures.map(async (lecture) => { + const titleField = + updateType === 'title' ? lecture.title : lecture.title_en; + const updateClassField = + updateType === 'title' ? 'class_title' : 'class_title_en'; + const updateCommonField = + updateType === 'title' ? 'common_title' : 'common_title_en'; + let classTitle: string; + if (titleField != commonTitle) { + classTitle = titleField.substring(commonTitle.length); + } else if (lecture.class_no.length > 0) { + classTitle = lecture.class_no; + } else { + classTitle = 'A'; + } + return await this.prisma.subject_lecture.update({ + where: { id: lecture.id }, + data: { + [updateCommonField]: commonTitle, + [updateClassField]: classTitle, + }, + }); + }), + ); + } + private lcsFront(lectureTitles: string[]): string { + if (lectureTitles.length === 0) { + return ''; + } + let result = ''; + for (let i = lectureTitles[0].length; i > 0; i--) { + const targetSubstring = lectureTitles[0].substring(0, i); + if (lectureTitles.every((t) => t.substring(0, i) === targetSubstring)) { + result = targetSubstring; + break; + } + } + // eslint-disable-next-line no-useless-escape + result = result.replace(/[<(\[{]+$/, ''); + return result; + } +} diff --git a/src/prisma/middleware/prisma.lectureprofessors.ts b/src/prisma/middleware/prisma.lectureprofessors.ts new file mode 100644 index 00000000..1c2fbc1e --- /dev/null +++ b/src/prisma/middleware/prisma.lectureprofessors.ts @@ -0,0 +1,167 @@ +import { review_review, subject_lecture } from '@prisma/client'; +import { IPrismaMiddleware } from 'src/common/interfaces/IPrismaMiddleware'; +import { IReview } from 'src/common/interfaces/IReview'; +import { PrismaService } from '../prisma.service'; + +export class LectureProfessorsMiddleware + implements IPrismaMiddleware.IPrismaMiddleware +{ + private static instance: LectureProfessorsMiddleware; + private prisma: PrismaService; + + constructor(prisma: PrismaService) { + this.prisma = prisma; + } + + async preExecute( + operations: IPrismaMiddleware.operationType, + args: any, + ): Promise { + return true; + } + + async postExecute( + operations: IPrismaMiddleware.operationType, + args: any, + result: any, + ): Promise { + if ( + operations === 'create' || + operations === 'delete' || + operations === 'deleteMany' + ) { + const lectureId = result.lecture_id; + const lecture = await this.prisma.subject_lecture.findUniqueOrThrow({ + where: { id: lectureId }, + }); + await this.lectureRecalcScore(lecture); + return true; + } + return true; + } + + static initialize(prisma: PrismaService) { + if (!LectureProfessorsMiddleware.instance) { + LectureProfessorsMiddleware.instance = new LectureProfessorsMiddleware( + prisma, + ); + } + } + + static getInstance(): LectureProfessorsMiddleware { + return LectureProfessorsMiddleware.instance; + } + + private async lectureRecalcScore(lecture: subject_lecture) { + const professors = await this.prisma.subject_professor.findMany({ + where: { + subject_lecture_professors: { + some: { lecture: { id: lecture.id } }, + }, + }, + }); + const professorsId = professors.map((result) => result.id); + const reviews = await this.prisma.review_review.findMany({ + where: { + lecture: { + AND: [ + { + course: { + id: lecture.course_id, + }, + }, + { + subject_lecture_professors: { + some: { + professor: { + id: { in: professorsId }, + }, + }, + }, + }, + ], + }, + }, + }); + const grades = await this.lectureCalcAverage(reviews); + await this.prisma.subject_lecture.update({ + where: { id: lecture.id }, + data: { + review_total_weight: grades.totalWeight, + grade_sum: grades.sums.gradeSum, + load_sum: grades.sums.loadSum, + speech_sum: grades.sums.speechSum, + grade: grades.avgs.grade, + load: grades.avgs.load, + speech: grades.avgs.speech, + }, + }); + } + + private async lectureCalcAverage( + reviews: review_review[], + ): Promise { + const nonzeroReviews = reviews.filter( + (review) => + review.grade !== 0 || review.load !== 0 || review.speech !== 0, + ); + const reducedNonzero = await Promise.all( + nonzeroReviews.map(async (review) => { + const weight = await this.lectureGetWeight(review); + return { + weight, + grade: review.grade, + speech: review.speech, + load: review.load, + }; + }), + ); + const reviewNum = reviews.length; + const totalWeight = reducedNonzero.reduce((acc, r) => acc + r.weight, 0); + const gradeSum = reducedNonzero.reduce( + (acc, r) => acc + r.weight * r.grade * 3, + 0, + ); + const loadSum = reducedNonzero.reduce( + (acc, r) => acc + r.weight * r.load * 3, + 0, + ); + const speechSum = reducedNonzero.reduce( + (acc, r) => acc + r.weight * r.speech * 3, + 0, + ); + + const grade = totalWeight !== 0 ? gradeSum / totalWeight : 0.0; + const load = totalWeight !== 0 ? loadSum / totalWeight : 0.0; + const speech = totalWeight !== 0 ? speechSum / totalWeight : 0.0; + + return { + reviewNum, + totalWeight, + sums: { + gradeSum, + loadSum, + speechSum, + }, + avgs: { + grade, + load, + speech, + }, + }; + } + + private async lectureGetWeight(review: review_review): Promise { + const baseYear = new Date().getFullYear(); + const lectureYear: number = ( + await this.prisma.subject_lecture.findUniqueOrThrow({ + where: { id: review.id }, + select: { + year: true, + }, + }) + ).year; + const yearDiff = baseYear > lectureYear ? baseYear - lectureYear : 0; + return (Math.sqrt(review.like) + 2) * 0.85 ** yearDiff; + } +} diff --git a/src/prisma/middleware/prisma.reviews.ts b/src/prisma/middleware/prisma.reviews.ts new file mode 100644 index 00000000..46525222 --- /dev/null +++ b/src/prisma/middleware/prisma.reviews.ts @@ -0,0 +1,278 @@ +import { + review_review, + subject_course, + subject_lecture, + subject_professor, +} from '@prisma/client'; +import { IPrismaMiddleware } from 'src/common/interfaces/IPrismaMiddleware'; +import { IReview } from 'src/common/interfaces/IReview'; +import { PrismaService } from '../prisma.service'; + +export class ReviewMiddleware implements IPrismaMiddleware.IPrismaMiddleware { + private static instance: ReviewMiddleware; + private prisma: PrismaService; + + constructor(prisma: PrismaService) { + this.prisma = prisma; + } + + async preExecute( + operations: IPrismaMiddleware.operationType, + args: any, + ): Promise { + return true; + } + + async postExecute( + operations: IPrismaMiddleware.operationType, + args: any, + result: any, + ): Promise { + if ( + operations === 'create' || + operations === 'update' || + operations === 'upsert' + ) { + await this.reviewSavedMiddleware(result, operations); + return true; + } else if (operations === 'delete') { + await this.reviewDeletedMiddleware(result); + return true; + } + return true; + } + + static initialize(prisma: PrismaService) { + if (!ReviewMiddleware.instance) { + ReviewMiddleware.instance = new ReviewMiddleware(prisma); + } + } + + static getInstance(): ReviewMiddleware { + return ReviewMiddleware.instance; + } + + private async lectureRecalcScore(lecture: subject_lecture) { + const professors = await this.prisma.subject_professor.findMany({ + where: { + subject_lecture_professors: { + some: { lecture: { id: lecture.id } }, + }, + }, + }); + const professorsId = professors.map((result) => result.id); + const reviews = await this.prisma.review_review.findMany({ + where: { + lecture: { + AND: [ + { + course: { + id: lecture.course_id, + }, + }, + { + subject_lecture_professors: { + some: { + professor: { + id: { in: professorsId }, + }, + }, + }, + }, + ], + }, + }, + }); + const grades = await this.lectureCalcAverage(reviews); + await this.prisma.subject_lecture.update({ + where: { id: lecture.id }, + data: { + review_total_weight: grades.totalWeight, + grade_sum: grades.sums.gradeSum, + load_sum: grades.sums.loadSum, + speech_sum: grades.sums.speechSum, + grade: grades.avgs.grade, + load: grades.avgs.load, + speech: grades.avgs.speech, + }, + }); + } + + private async lectureCalcAverage( + reviews: review_review[], + ): Promise { + const nonzeroReviews = reviews.filter( + (review) => + review.grade !== 0 || review.load !== 0 || review.speech !== 0, + ); + const reducedNonzero = await Promise.all( + nonzeroReviews.map(async (review) => { + const weight = await this.lectureGetWeight(review); + return { + weight, + grade: review.grade, + speech: review.speech, + load: review.load, + }; + }), + ); + const reviewNum = reviews.length; + const totalWeight = reducedNonzero.reduce((acc, r) => acc + r.weight, 0); + const gradeSum = reducedNonzero.reduce( + (acc, r) => acc + r.weight * r.grade * 3, + 0, + ); + const loadSum = reducedNonzero.reduce( + (acc, r) => acc + r.weight * r.load * 3, + 0, + ); + const speechSum = reducedNonzero.reduce( + (acc, r) => acc + r.weight * r.speech * 3, + 0, + ); + + const grade = totalWeight !== 0 ? gradeSum / totalWeight : 0.0; + const load = totalWeight !== 0 ? loadSum / totalWeight : 0.0; + const speech = totalWeight !== 0 ? speechSum / totalWeight : 0.0; + + return { + reviewNum, + totalWeight, + sums: { + gradeSum, + loadSum, + speechSum, + }, + avgs: { + grade, + load, + speech, + }, + }; + } + + private async lectureGetWeight(review: review_review): Promise { + const baseYear = new Date().getFullYear(); + const lectureYear: number = ( + await this.prisma.subject_lecture.findUniqueOrThrow({ + where: { id: review.lecture_id }, + select: { + year: true, + }, + }) + ).year; + const yearDiff = baseYear > lectureYear ? baseYear - lectureYear : 0; + return (Math.sqrt(review.like) + 2) * 0.85 ** yearDiff; + } + + private async courseRecalcScore(course: subject_course) { + const reviews = await this.prisma.review_review.findMany({ + where: { + lecture: { + course: { + id: course.id, + }, + }, + }, + }); + const grades = await this.lectureCalcAverage(reviews); + await this.prisma.subject_course.update({ + where: { id: course.id }, + data: { + review_total_weight: grades.totalWeight, + grade_sum: grades.sums.gradeSum, + load_sum: grades.sums.loadSum, + speech_sum: grades.sums.speechSum, + grade: grades.avgs.grade, + load: grades.avgs.load, + speech: grades.avgs.speech, + }, + }); + } + + private async professorRecalcScore(professor: subject_professor) { + const reviews = await this.prisma.review_review.findMany({ + where: { + lecture: { + subject_lecture_professors: { + some: { + professor: { + id: professor.id, + }, + }, + }, + }, + }, + }); + const grades = await this.lectureCalcAverage(reviews); + await this.prisma.subject_professor.update({ + where: { id: professor.id }, + data: { + review_total_weight: grades.totalWeight, + grade_sum: grades.sums.gradeSum, + load_sum: grades.sums.loadSum, + speech_sum: grades.sums.speechSum, + grade: grades.avgs.grade, + load: grades.avgs.load, + speech: grades.avgs.speech, + }, + }); + } + + private async recalcRelatedScore(review: review_review) { + const course = await this.prisma.subject_course.findUnique({ + where: { id: review.course_id }, + }); + + const professors = await this.prisma.subject_professor.findMany({ + where: { + subject_lecture_professors: { + some: { lecture: { id: review.lecture_id } }, + }, + }, + }); + + const professorsId = professors.map((result) => result.id); + + const lectures = await this.prisma.subject_lecture.findMany({ + where: { + course_id: review.course_id, + subject_lecture_professors: { + some: { professor: { id: { in: professorsId } } }, + }, + }, + }); + if (course) { + await this.courseRecalcScore(course); + } + await Promise.all( + lectures.map(async (lecture) => await this.lectureRecalcScore(lecture)), + ); + await Promise.all( + professors.map( + async (professor) => await this.professorRecalcScore(professor), + ), + ); + } + + private async reviewSavedMiddleware(result: any, action: string) { + await this.recalcRelatedScore(result); + if (action === 'create') { + const course = await result.course; + await this.prisma.subject_course.update({ + where: { + id: course.id, + }, + data: { + latest_written_datetime: result.written_datetime, + }, + }); + } else { + //todo: caches + } + } + + private async reviewDeletedMiddleware(result: any) { + await this.recalcRelatedScore(result); + } +} diff --git a/src/prisma/middleware/prisma.reviewvote.ts b/src/prisma/middleware/prisma.reviewvote.ts new file mode 100644 index 00000000..af6ab985 --- /dev/null +++ b/src/prisma/middleware/prisma.reviewvote.ts @@ -0,0 +1,71 @@ +import { review_reviewvote } from '@prisma/client'; +import { IPrismaMiddleware } from 'src/common/interfaces/IPrismaMiddleware'; +import { PrismaService } from '../prisma.service'; + +export class ReviewVoteMiddleware + implements IPrismaMiddleware.IPrismaMiddleware +{ + private static instance: ReviewVoteMiddleware; + private prisma: PrismaService; + + constructor( + prisma: PrismaService, //private readonly courseRepository: CourseRepository, //private readonly lectureRepository: LectureRepository, //private readonly professorRepositiry: ProfessorRepositiry, + ) { + this.prisma = prisma; + } + async preExecute( + operations: IPrismaMiddleware.operationType, + args: any, + ): Promise { + return true; + } + + async postExecute( + operatoins: IPrismaMiddleware.operationType, + args: any, + result: any, + ): Promise { + if ( + operatoins === 'create' || + operatoins === 'update' || + operatoins === 'upsert' + ) { + await this.reviewVoteSavedMiddleware(result); + return true; + } else if (operatoins === 'delete') { + await this.reviewVoteDeletedMiddleware(result); + return true; + } + return true; + } + + static initialize(prisma: PrismaService) { + if (!ReviewVoteMiddleware.instance) { + ReviewVoteMiddleware.instance = new ReviewVoteMiddleware(prisma); + } + } + static getInstance(): ReviewVoteMiddleware { + return ReviewVoteMiddleware.instance; + } + + private async reviewRecalcLike(reviewVote: review_reviewvote) { + await this.prisma.$transaction(async (tx) => { + await tx.review_review.update({ + where: { id: reviewVote.review_id }, + data: { + like: await tx.review_reviewvote.count({ + where: { review_id: reviewVote.review_id }, + }), + }, + }); + }); + } + + private async reviewVoteSavedMiddleware(result: any) { + await this.reviewRecalcLike(result); + } + + private async reviewVoteDeletedMiddleware(result: any) { + await this.reviewRecalcLike(result); + } +} diff --git a/src/prisma/middleware/prisma.semester.ts b/src/prisma/middleware/prisma.semester.ts new file mode 100644 index 00000000..ff96946f --- /dev/null +++ b/src/prisma/middleware/prisma.semester.ts @@ -0,0 +1,39 @@ +import { IPrismaMiddleware } from 'src/common/interfaces/IPrismaMiddleware'; +import { PrismaService } from '../prisma.service'; + +export class SemesterMiddleware implements IPrismaMiddleware.IPrismaMiddleware { + private static instance: SemesterMiddleware; + private prisma: PrismaService; + + constructor(prisma: PrismaService) { + this.prisma = prisma; + } + + async preExecute( + operations: IPrismaMiddleware.operationType, + args: any, + ): Promise { + return true; + } + + async postExecute( + operations: IPrismaMiddleware.operationType, + args: any, + result: any, + ): Promise { + if (operations === 'create') { + //todo: cache delete + } + return true; + } + + static initialize(prisma: PrismaService) { + if (!SemesterMiddleware.instance) { + SemesterMiddleware.instance = new SemesterMiddleware(prisma); + } + } + + static getInstance(): SemesterMiddleware { + return SemesterMiddleware.instance; + } +} diff --git a/src/prisma/middleware/prisma.timetable.ts b/src/prisma/middleware/prisma.timetable.ts new file mode 100644 index 00000000..5b215ac2 --- /dev/null +++ b/src/prisma/middleware/prisma.timetable.ts @@ -0,0 +1,82 @@ +import { IPrismaMiddleware } from 'src/common/interfaces/IPrismaMiddleware'; +import { PrismaService } from '../prisma.service'; +interface UserCount { + [lecture_id: number]: Set; +} +export class TimetableMiddleware + implements IPrismaMiddleware.IPrismaMiddleware +{ + private static instance: TimetableMiddleware; + private prisma: PrismaService; + + constructor(prisma: PrismaService) { + this.prisma = prisma; + } + + async preExecute( + operations: IPrismaMiddleware.operationType, + args: any, + ): Promise { + return true; + } + + async postExecute( + operations: IPrismaMiddleware.operationType, + args: any, + result: any, + ): Promise { + if (operations === 'delete') { + const timetableId = args.where.id; + const lectures = await this.prisma.timetable_timetable_lectures.findMany({ + where: { + timetable_id: timetableId, + }, + }); + const res = await this.countNumPeopleBatch(lectures); + if (!res) throw new Error('Could not decrease num_people'); + return true; + } + return true; + } + + static initialize(prisma: PrismaService) { + if (!TimetableMiddleware.instance) { + TimetableMiddleware.instance = new TimetableMiddleware(prisma); + } + } + + static getInstance(): TimetableMiddleware { + return TimetableMiddleware.instance; + } + + private async countNumPeopleBatch( + lectures: { id: number; timetable_id: number; lecture_id: number }[], + ) { + const lectureIds = lectures.map((lecture) => lecture.lecture_id); + Promise.all( + lectureIds.map(async (id) => { + await this.prisma.$transaction(async (prisma) => { + await prisma.subject_lecture.update({ + where: { id: id }, + data: { + num_people: + ( + await prisma.timetable_timetable.findMany({ + distinct: ['user_id'], + where: { + timetable_timetable_lectures: { + some: { + lecture_id: id, + }, + }, + }, + }) + )?.length ?? 0, + }, + }); + }); + }), + ); + return true; + } +} diff --git a/src/prisma/middleware/prisma.timetablelecture.ts b/src/prisma/middleware/prisma.timetablelecture.ts new file mode 100644 index 00000000..da8bd93b --- /dev/null +++ b/src/prisma/middleware/prisma.timetablelecture.ts @@ -0,0 +1,160 @@ +import { IPrismaMiddleware } from 'src/common/interfaces/IPrismaMiddleware'; +import { PrismaService } from '../prisma.service'; + +export class TimetableLectureMiddleware + implements IPrismaMiddleware.IPrismaMiddleware +{ + private static instance: TimetableLectureMiddleware; + private prisma: PrismaService; + + constructor(prisma: PrismaService) { + this.prisma = prisma; + } + + async preExecute( + operations: IPrismaMiddleware.operationType, + args: any, + ): Promise { + return true; + } + + async postExecute( + operations: IPrismaMiddleware.operationType, + args: any, + result: any, + ): Promise { + if (operations === 'create') { + const timetableId = args?.data?.timetable_id; + const lectureId = args?.data?.lecture_id; + const userId: number | undefined = ( + await this.prisma.timetable_timetable.findUnique({ + where: { id: timetableId }, + select: { user_id: true }, + }) + )?.user_id; + if (userId !== undefined) { + const res = await this.countNumPeople(lectureId); + if (res) return true; + throw new Error('Could not decrease num_people'); + } + throw new Error("can't find user"); + } else if (operations === 'createMany') { + const timetableId = args?.data?.timetable_id; + const lectures = args?.data; // nested createMany 에 대해서는 작동 안함. + const userId: number | undefined = ( + await this.prisma.timetable_timetable.findUnique({ + where: { id: timetableId }, + select: { user_id: true }, + }) + )?.user_id; + if (userId !== undefined) { + const res = await this.countNumPeopleBatch(lectures); + if (!res) throw new Error('Could not increase num_people'); + return true; + } + throw new Error("can't find user"); + } else if (operations === 'delete') { + const timetableId = args?.where?.timetable_id_lecture_id?.timetable_id; // todo : args에 where이 들거가나? + const lectureId = args?.where?.timetable_id_lecture_id?.lecture_id; + const userId: number | undefined = ( + await this.prisma.timetable_timetable.findUnique({ + where: { id: timetableId }, + select: { user_id: true }, + }) + )?.user_id; + if (userId !== undefined) { + const res = await this.countNumPeople(lectureId); + if (res) return true; + throw new Error('Could not decrease num_people'); + } + throw new Error("can't find user"); + } else if (operations === 'deleteMany') { + const timetableId = args?.where?.timetable_id; + const lectures = await this.prisma.timetable_timetable_lectures.findMany({ + where: { + timetable_id: timetableId, + }, + }); + const userId: number | undefined = ( + await this.prisma.timetable_timetable.findUnique({ + where: { id: timetableId }, + select: { user_id: true }, + }) + )?.user_id; + if (userId !== undefined) { + const res = await this.countNumPeopleBatch(lectures); + if (!res) throw new Error('Could not decrease num_people'); + return true; + } + throw new Error("can't find user"); + } + return true; + } + + static initialize(prisma: PrismaService) { + if (!TimetableLectureMiddleware.instance) { + TimetableLectureMiddleware.instance = new TimetableLectureMiddleware( + prisma, + ); + } + } + + static getInstance(): TimetableLectureMiddleware { + return TimetableLectureMiddleware.instance; + } + + private async countNumPeople(lectureId: number) { + await this.prisma.$transaction(async (prisma) => { + await prisma.subject_lecture.update({ + where: { id: lectureId }, + data: { + num_people: + ( + await prisma.timetable_timetable.findMany({ + distinct: ['user_id'], + where: { + timetable_timetable_lectures: { + some: { + lecture_id: lectureId, + }, + }, + }, + }) + )?.length ?? 0, + }, + }); + }); + return true; + } + + private async countNumPeopleBatch( + lectures: { id: number; timetable_id: number; lecture_id: number }[], + ) { + const lectureIds = lectures.map((lecture) => lecture.lecture_id); + Promise.all( + lectureIds.map(async (id) => { + await this.prisma.$transaction(async (prisma) => { + await prisma.subject_lecture.update({ + where: { id: id }, + data: { + num_people: + ( + await prisma.timetable_timetable.findMany({ + distinct: ['user_id'], + where: { + timetable_timetable_lectures: { + some: { + lecture_id: id, + }, + }, + }, + }) + )?.length ?? 0, + }, + }); + }); + }), + ); + return true; + } +} diff --git a/src/prisma/migrations/0_init/migration.sql b/src/prisma/migrations/0_init/migration.sql index 2486e3f0..9cec7b57 100644 --- a/src/prisma/migrations/0_init/migration.sql +++ b/src/prisma/migrations/0_init/migration.sql @@ -3,8 +3,8 @@ CREATE TABLE `auth_group` ( `id` INTEGER NOT NULL AUTO_INCREMENT, `name` VARCHAR(150) NOT NULL, - UNIQUE INDEX `name`(`name` ASC), - PRIMARY KEY (`id` ASC) + UNIQUE INDEX `name`(`name`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -13,9 +13,9 @@ CREATE TABLE `auth_group_permissions` ( `group_id` INTEGER NOT NULL, `permission_id` INTEGER NOT NULL, - INDEX `auth_group__permission_id_1f49ccbbdc69d2fc_fk_auth_permission_id`(`permission_id` ASC), - UNIQUE INDEX `group_id`(`group_id` ASC, `permission_id` ASC), - PRIMARY KEY (`id` ASC) + INDEX `auth_group__permission_id_1f49ccbbdc69d2fc_fk_auth_permission_id`(`permission_id`), + UNIQUE INDEX `group_id`(`group_id`, `permission_id`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -25,8 +25,8 @@ CREATE TABLE `auth_permission` ( `content_type_id` INTEGER NOT NULL, `codename` VARCHAR(100) NOT NULL, - UNIQUE INDEX `content_type_id`(`content_type_id` ASC, `codename` ASC), - PRIMARY KEY (`id` ASC) + UNIQUE INDEX `content_type_id`(`content_type_id`, `codename`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -43,8 +43,8 @@ CREATE TABLE `auth_user` ( `is_active` BOOLEAN NOT NULL, `date_joined` DATETIME(0) NOT NULL, - UNIQUE INDEX `username`(`username` ASC), - PRIMARY KEY (`id` ASC) + UNIQUE INDEX `username`(`username`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -53,9 +53,9 @@ CREATE TABLE `auth_user_groups` ( `user_id` INTEGER NOT NULL, `group_id` INTEGER NOT NULL, - INDEX `auth_user_groups_group_id_33ac548dcf5f8e37_fk_auth_group_id`(`group_id` ASC), - UNIQUE INDEX `user_id`(`user_id` ASC, `group_id` ASC), - PRIMARY KEY (`id` ASC) + INDEX `auth_user_groups_group_id_33ac548dcf5f8e37_fk_auth_group_id`(`group_id`), + UNIQUE INDEX `user_id`(`user_id`, `group_id`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -64,9 +64,9 @@ CREATE TABLE `auth_user_user_permissions` ( `user_id` INTEGER NOT NULL, `permission_id` INTEGER NOT NULL, - INDEX `auth_user_u_permission_id_384b62483d7071f0_fk_auth_permission_id`(`permission_id` ASC), - UNIQUE INDEX `user_id`(`user_id` ASC, `permission_id` ASC), - PRIMARY KEY (`id` ASC) + INDEX `auth_user_u_permission_id_384b62483d7071f0_fk_auth_permission_id`(`permission_id`), + UNIQUE INDEX `user_id`(`user_id`, `permission_id`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -80,9 +80,9 @@ CREATE TABLE `django_admin_log` ( `content_type_id` INTEGER NULL, `user_id` INTEGER NOT NULL, - INDEX `djang_content_type_id_697914295151027a_fk_django_content_type_id`(`content_type_id` ASC), - INDEX `django_admin_log_user_id_52fdd58701c5f563_fk_auth_user_id`(`user_id` ASC), - PRIMARY KEY (`id` ASC) + INDEX `djang_content_type_id_697914295151027a_fk_django_content_type_id`(`content_type_id`), + INDEX `django_admin_log_user_id_52fdd58701c5f563_fk_auth_user_id`(`user_id`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -91,8 +91,8 @@ CREATE TABLE `django_content_type` ( `app_label` VARCHAR(100) NOT NULL, `model` VARCHAR(100) NOT NULL, - UNIQUE INDEX `django_content_type_app_label_45f3b1d93ec8c61c_uniq`(`app_label` ASC, `model` ASC), - PRIMARY KEY (`id` ASC) + UNIQUE INDEX `django_content_type_app_label_45f3b1d93ec8c61c_uniq`(`app_label`, `model`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -102,7 +102,7 @@ CREATE TABLE `django_migrations` ( `name` VARCHAR(255) NOT NULL, `applied` DATETIME(0) NOT NULL, - PRIMARY KEY (`id` ASC) + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -111,70 +111,8 @@ CREATE TABLE `django_session` ( `session_data` LONGTEXT NOT NULL, `expire_date` DATETIME(0) NOT NULL, - INDEX `django_session_de54fa62`(`expire_date` ASC), - PRIMARY KEY (`session_key` ASC) -) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - --- CreateTable -CREATE TABLE `graduation_additionaltrack` ( - `id` INTEGER NOT NULL AUTO_INCREMENT, - `start_year` INTEGER NOT NULL, - `end_year` INTEGER NOT NULL, - `type` VARCHAR(32) NOT NULL, - `major_required` INTEGER NOT NULL, - `major_elective` INTEGER NOT NULL, - `department_id` INTEGER NULL, - - INDEX `graduation_additiona_department_id_788c5289_fk_subject_d`(`department_id` ASC), - UNIQUE INDEX `graduation_additionaltra_end_year_type_department_9d873c1b_uniq`(`end_year` ASC, `type` ASC, `department_id` ASC), - UNIQUE INDEX `graduation_additionaltra_start_year_type_departme_763552eb_uniq`(`start_year` ASC, `type` ASC, `department_id` ASC), - INDEX `graduation_additionaltrack_end_year_6af1030b`(`end_year` ASC), - INDEX `graduation_additionaltrack_start_year_7a87318d`(`start_year` ASC), - INDEX `graduation_additionaltrack_type_0fa38fc5`(`type` ASC), - PRIMARY KEY (`id` ASC) -) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - --- CreateTable -CREATE TABLE `graduation_generaltrack` ( - `id` INTEGER NOT NULL AUTO_INCREMENT, - `start_year` INTEGER NOT NULL, - `end_year` INTEGER NOT NULL, - `is_foreign` BOOLEAN NOT NULL, - `total_credit` INTEGER NOT NULL, - `total_au` INTEGER NOT NULL, - `basic_required` INTEGER NOT NULL, - `basic_elective` INTEGER NOT NULL, - `thesis_study` INTEGER NOT NULL, - `thesis_study_doublemajor` INTEGER NOT NULL, - `general_required_credit` INTEGER NOT NULL, - `general_required_au` INTEGER NOT NULL, - `humanities` INTEGER NOT NULL, - `humanities_doublemajor` INTEGER NOT NULL, - - INDEX `graduation_generaltrack_end_year_3bba699e`(`end_year` ASC), - UNIQUE INDEX `graduation_generaltrack_end_year_is_foreign_1f062f8b_uniq`(`end_year` ASC, `is_foreign` ASC), - INDEX `graduation_generaltrack_is_foreign_d38919a2`(`is_foreign` ASC), - INDEX `graduation_generaltrack_start_year_00aee782`(`start_year` ASC), - UNIQUE INDEX `graduation_generaltrack_start_year_is_foreign_c1eb425f_uniq`(`start_year` ASC, `is_foreign` ASC), - PRIMARY KEY (`id` ASC) -) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - --- CreateTable -CREATE TABLE `graduation_majortrack` ( - `id` INTEGER NOT NULL AUTO_INCREMENT, - `start_year` INTEGER NOT NULL, - `end_year` INTEGER NOT NULL, - `basic_elective_doublemajor` INTEGER NOT NULL, - `major_required` INTEGER NOT NULL, - `major_elective` INTEGER NOT NULL, - `department_id` INTEGER NOT NULL, - - INDEX `graduation_majortrac_department_id_81bfc8fa_fk_subject_d`(`department_id` ASC), - INDEX `graduation_majortrack_end_year_57017559`(`end_year` ASC), - UNIQUE INDEX `graduation_majortrack_end_year_department_id_b3ef1bc8_uniq`(`end_year` ASC, `department_id` ASC), - INDEX `graduation_majortrack_start_year_6281dc28`(`start_year` ASC), - UNIQUE INDEX `graduation_majortrack_start_year_department_id_59122c6d_uniq`(`start_year` ASC, `department_id` ASC), - PRIMARY KEY (`id` ASC) + INDEX `django_session_de54fa62`(`expire_date`), + PRIMARY KEY (`session_key`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -184,8 +122,8 @@ CREATE TABLE `main_famoushumanityreviewdailyfeed` ( `priority` DOUBLE NOT NULL, `visible` BOOLEAN NOT NULL, - UNIQUE INDEX `main_famoushumanityreviewdailyfeed_date_0fbb607a_uniq`(`date` ASC), - PRIMARY KEY (`id` ASC) + UNIQUE INDEX `main_famoushumanityreviewdailyfeed_date_0fbb607a_uniq`(`date`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -194,9 +132,9 @@ CREATE TABLE `main_famoushumanityreviewdailyfeed_reviews` ( `famoushumanityreviewdailyfeed_id` INTEGER NOT NULL, `review_id` INTEGER NOT NULL, - UNIQUE INDEX `main_famoushumani_famoushumanityreviewdailyfeed_id_97def4df_uniq`(`famoushumanityreviewdailyfeed_id` ASC, `review_id` ASC), - INDEX `main_famoushumanityreview_review_id_f305d8aa_fk_review_review_id`(`review_id` ASC), - PRIMARY KEY (`id` ASC) + INDEX `main_famoushumanityreview_review_id_f305d8aa_fk_review_review_id`(`review_id`), + UNIQUE INDEX `main_famoushumani_famoushumanityreviewdailyfeed_id_97def4df_uniq`(`famoushumanityreviewdailyfeed_id`, `review_id`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -207,9 +145,9 @@ CREATE TABLE `main_famousmajorreviewdailyfeed` ( `department_id` INTEGER NOT NULL, `visible` BOOLEAN NOT NULL, - INDEX `main_famousmajorrevi_department_id_a0a5a3a5_fk_subject_d`(`department_id` ASC), - UNIQUE INDEX `main_famousreviewdailyfeed_date_94cf00dd_uniq`(`date` ASC, `department_id` ASC), - PRIMARY KEY (`id` ASC) + INDEX `main_famousmajorrevi_department_id_a0a5a3a5_fk_subject_d`(`department_id`), + UNIQUE INDEX `main_famousreviewdailyfeed_date_94cf00dd_uniq`(`date`, `department_id`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -218,9 +156,9 @@ CREATE TABLE `main_famousmajorreviewdailyfeed_reviews` ( `famousmajorreviewdailyfeed_id` INTEGER NOT NULL, `review_id` INTEGER NOT NULL, - INDEX `main_famousmajorreviewdai_review_id_c0d3bbec_fk_review_review_id`(`review_id` ASC), - UNIQUE INDEX `main_famousreviewdailyfee_famousreviewdailyfeed_id_12d71d0b_uniq`(`famousmajorreviewdailyfeed_id` ASC, `review_id` ASC), - PRIMARY KEY (`id` ASC) + INDEX `main_famousmajorreviewdai_review_id_c0d3bbec_fk_review_review_id`(`review_id`), + UNIQUE INDEX `main_famousreviewdailyfee_famousreviewdailyfeed_id_12d71d0b_uniq`(`famousmajorreviewdailyfeed_id`, `review_id`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -231,9 +169,9 @@ CREATE TABLE `main_rankedreviewdailyfeed` ( `visible` BOOLEAN NOT NULL, `semester_id` INTEGER NULL, - INDEX `main_rankedreviewdai_semester_id_f71e3a66_fk_subject_s`(`semester_id` ASC), - UNIQUE INDEX `main_rankedreviewdailyfeed_date_635bca2a_uniq`(`date` ASC), - PRIMARY KEY (`id` ASC) + UNIQUE INDEX `main_rankedreviewdailyfeed_date_635bca2a_uniq`(`date`), + INDEX `main_rankedreviewdai_semester_id_f71e3a66_fk_subject_s`(`semester_id`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -244,9 +182,9 @@ CREATE TABLE `main_ratedailyuserfeed` ( `visible` BOOLEAN NOT NULL, `user_id` INTEGER NOT NULL, - INDEX `main_ratedailyuserfe_user_id_31a534d5_fk_session_u`(`user_id` ASC), - UNIQUE INDEX `main_ratedailyuserfeed_date_user_id_4142794f_uniq`(`date` ASC, `user_id` ASC), - PRIMARY KEY (`id` ASC) + INDEX `main_ratedailyuserfe_user_id_31a534d5_fk_session_u`(`user_id`), + UNIQUE INDEX `main_ratedailyuserfeed_date_user_id_4142794f_uniq`(`date`, `user_id`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -258,10 +196,10 @@ CREATE TABLE `main_relatedcoursedailyuserfeed` ( `user_id` INTEGER NOT NULL, `visible` BOOLEAN NOT NULL, - INDEX `main_relatedcourseda_course_id_129fc5e2_fk_subject_c`(`course_id` ASC), - INDEX `main_relatedcoursedai_user_id_a1be2390_fk_session_userprofile_id`(`user_id` ASC), - UNIQUE INDEX `main_relatedcoursedailyuserfeed_date_6043d8bb_uniq`(`date` ASC, `user_id` ASC), - PRIMARY KEY (`id` ASC) + INDEX `main_relatedcourseda_course_id_129fc5e2_fk_subject_c`(`course_id`), + INDEX `main_relatedcoursedai_user_id_a1be2390_fk_session_userprofile_id`(`user_id`), + UNIQUE INDEX `main_relatedcoursedailyuserfeed_date_6043d8bb_uniq`(`date`, `user_id`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -273,102 +211,24 @@ CREATE TABLE `main_reviewwritedailyuserfeed` ( `user_id` INTEGER NOT NULL, `visible` BOOLEAN NOT NULL, - INDEX `main_reviewwritedail_lecture_id_75ed0f87_fk_subject_l`(`lecture_id` ASC), - INDEX `main_reviewwritedaily_user_id_9ffd0881_fk_session_userprofile_id`(`user_id` ASC), - UNIQUE INDEX `main_reviewwritedailyuserfeed_date_1e7bc6d7_uniq`(`date` ASC, `user_id` ASC), - PRIMARY KEY (`id` ASC) -) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - --- CreateTable -CREATE TABLE `planner_arbitraryplanneritem` ( - `id` INTEGER NOT NULL AUTO_INCREMENT, - `is_excluded` BOOLEAN NOT NULL, - `year` INTEGER NOT NULL, - `semester` INTEGER NOT NULL, - `type` VARCHAR(12) NOT NULL, - `type_en` VARCHAR(36) NOT NULL, - `credit` INTEGER NOT NULL, - `credit_au` INTEGER NOT NULL, - `department_id` INTEGER NULL, - `planner_id` INTEGER NOT NULL, - - INDEX `planner_arbitrarypla_department_id_0dc7ce25_fk_subject_d`(`department_id` ASC), - INDEX `planner_arbitrarypla_planner_id_d6069d2c_fk_planner_p`(`planner_id` ASC), - INDEX `planner_arbitraryplanneritem_semester_7508baa5`(`semester` ASC), - INDEX `planner_arbitraryplanneritem_year_5a0c7252`(`year` ASC), - PRIMARY KEY (`id` ASC) -) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - --- CreateTable -CREATE TABLE `planner_futureplanneritem` ( - `id` INTEGER NOT NULL AUTO_INCREMENT, - `is_excluded` BOOLEAN NOT NULL, - `year` INTEGER NOT NULL, - `semester` INTEGER NOT NULL, - `course_id` INTEGER NOT NULL, - `planner_id` INTEGER NOT NULL, - - INDEX `planner_futureplanne_course_id_b1a06444_fk_subject_c`(`course_id` ASC), - INDEX `planner_futureplanne_planner_id_dfd70193_fk_planner_p`(`planner_id` ASC), - INDEX `planner_futureplanneritem_semester_cda6512e`(`semester` ASC), - INDEX `planner_futureplanneritem_year_5e3a2d4e`(`year` ASC), - PRIMARY KEY (`id` ASC) -) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - --- CreateTable -CREATE TABLE `planner_planner` ( - `id` INTEGER NOT NULL AUTO_INCREMENT, - `start_year` INTEGER NOT NULL, - `end_year` INTEGER NOT NULL, - `arrange_order` SMALLINT NOT NULL, - `general_track_id` INTEGER NOT NULL, - `major_track_id` INTEGER NOT NULL, - `user_id` INTEGER NOT NULL, - - INDEX `planner_planner_arrange_order_e50a3044`(`arrange_order` ASC), - INDEX `planner_planner_end_year_e5fab7f3`(`end_year` ASC), - INDEX `planner_planner_general_track_id_6d607973_fk_graduatio`(`general_track_id` ASC), - INDEX `planner_planner_major_track_id_9f7204bd_fk_graduatio`(`major_track_id` ASC), - INDEX `planner_planner_start_year_463173f3`(`start_year` ASC), - INDEX `planner_planner_user_id_17740247_fk_session_userprofile_id`(`user_id` ASC), - PRIMARY KEY (`id` ASC) -) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - --- CreateTable -CREATE TABLE `planner_planner_additional_tracks` ( - `id` INTEGER NOT NULL AUTO_INCREMENT, - `planner_id` INTEGER NOT NULL, - `additionaltrack_id` INTEGER NOT NULL, - - INDEX `planner_planner_addi_additionaltrack_id_c46b8c4e_fk_graduatio`(`additionaltrack_id` ASC), - UNIQUE INDEX `planner_planner_addition_planner_id_additionaltra_2298c5cd_uniq`(`planner_id` ASC, `additionaltrack_id` ASC), - PRIMARY KEY (`id` ASC) -) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - --- CreateTable -CREATE TABLE `planner_takenplanneritem` ( - `id` INTEGER NOT NULL AUTO_INCREMENT, - `is_excluded` BOOLEAN NOT NULL, - `lecture_id` INTEGER NOT NULL, - `planner_id` INTEGER NOT NULL, - - INDEX `planner_takenplanner_lecture_id_9b2d30d8_fk_subject_l`(`lecture_id` ASC), - UNIQUE INDEX `planner_takenplanneritem_planner_id_lecture_id_4b39b432_uniq`(`planner_id` ASC, `lecture_id` ASC), - PRIMARY KEY (`id` ASC) + INDEX `main_reviewwritedail_lecture_id_75ed0f87_fk_subject_l`(`lecture_id`), + INDEX `main_reviewwritedaily_user_id_9ffd0881_fk_session_userprofile_id`(`user_id`), + UNIQUE INDEX `main_reviewwritedailyuserfeed_date_1e7bc6d7_uniq`(`date`, `user_id`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable CREATE TABLE `review_humanitybestreview` ( `review_id` INTEGER NOT NULL, - PRIMARY KEY (`review_id` ASC) + PRIMARY KEY (`review_id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable CREATE TABLE `review_majorbestreview` ( `review_id` INTEGER NOT NULL, - PRIMARY KEY (`review_id` ASC) + PRIMARY KEY (`review_id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -377,19 +237,21 @@ CREATE TABLE `review_review` ( `course_id` INTEGER NOT NULL, `lecture_id` INTEGER NOT NULL, `content` MEDIUMTEXT NOT NULL, - `grade` SMALLINT NOT NULL, - `load` SMALLINT NOT NULL, - `speech` SMALLINT NOT NULL, + `grade` SMALLINT NOT NULL DEFAULT 0, + `load` SMALLINT NOT NULL DEFAULT 0, + `speech` SMALLINT NOT NULL DEFAULT 0, `writer_id` INTEGER NULL, `writer_label` VARCHAR(200) NOT NULL, `updated_datetime` DATETIME(0) NOT NULL, - `like` INTEGER NOT NULL, - `is_deleted` INTEGER NOT NULL, + `like` INTEGER NOT NULL DEFAULT 0, + `is_deleted` INTEGER NOT NULL DEFAULT 0, `written_datetime` DATETIME(0) NULL, - INDEX `review_comment_e5e30a4a`(`written_datetime` ASC), - UNIQUE INDEX `review_comment_writer_id_af700a5d_uniq`(`writer_id` ASC, `lecture_id` ASC), - PRIMARY KEY (`id` ASC) + INDEX `review_comment_e5e30a4a`(`written_datetime`), + INDEX `review_review_course_id_fkey`(`course_id`), + INDEX `review_review_lecture_id_fkey`(`lecture_id`), + UNIQUE INDEX `review_comment_writer_id_af700a5d_uniq`(`writer_id`, `lecture_id`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -399,24 +261,30 @@ CREATE TABLE `review_reviewvote` ( `userprofile_id` INTEGER NULL, `created_datetime` DATETIME(6) NULL, - UNIQUE INDEX `review_commentvote_comment_id_e4594aea_uniq`(`review_id` ASC, `userprofile_id` ASC), - INDEX `review_reviewvote_created_datetime_450f85e2`(`created_datetime` ASC), - PRIMARY KEY (`id` ASC) + INDEX `review_reviewvote_created_datetime_450f85e2`(`created_datetime`), + INDEX `review_reviewvote_userprofile_id_fkey`(`userprofile_id`), + UNIQUE INDEX `review_commentvote_comment_id_e4594aea_uniq`(`review_id`, `userprofile_id`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable CREATE TABLE `session_userprofile` ( `id` INTEGER NOT NULL AUTO_INCREMENT, - `user_id` INTEGER NOT NULL, + `user_id` INTEGER NULL, `student_id` VARCHAR(10) NOT NULL, `sid` VARCHAR(30) NOT NULL, - `language` VARCHAR(15) NOT NULL, + `language` VARCHAR(15) NULL, `portal_check` INTEGER NULL DEFAULT 0, `department_id` INTEGER NULL, `email` VARCHAR(255) NULL, + `date_joined` DATETIME(0) NOT NULL, + `first_name` VARCHAR(30) NOT NULL, + `last_name` VARCHAR(150) NOT NULL, + `refresh_token` VARCHAR(255) NULL, - UNIQUE INDEX `session_userprofile_user_id_09dd6af1_uniq`(`user_id` ASC), - PRIMARY KEY (`id` ASC) + UNIQUE INDEX `session_userprofile_user_id_09dd6af1_uniq`(`user_id`), + INDEX `session_userprofile_department_id_fkey`(`department_id`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -425,8 +293,9 @@ CREATE TABLE `session_userprofile_favorite_departments` ( `userprofile_id` INTEGER NOT NULL, `department_id` INTEGER NOT NULL, - UNIQUE INDEX `userprofile_id`(`userprofile_id` ASC, `department_id` ASC), - PRIMARY KEY (`id` ASC) + INDEX `session_userprofile_favorite_departments_department_id_fkey`(`department_id`), + UNIQUE INDEX `userprofile_id`(`userprofile_id`, `department_id`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -435,9 +304,9 @@ CREATE TABLE `session_userprofile_majors` ( `userprofile_id` INTEGER NOT NULL, `department_id` INTEGER NOT NULL, - INDEX `session_userprof_department_id_db568678_fk_subject_department_id`(`department_id` ASC), - UNIQUE INDEX `session_userprofile_majors_userprofile_id_12b76c49_uniq`(`userprofile_id` ASC, `department_id` ASC), - PRIMARY KEY (`id` ASC) + INDEX `session_userprof_department_id_db568678_fk_subject_department_id`(`department_id`), + UNIQUE INDEX `session_userprofile_majors_userprofile_id_12b76c49_uniq`(`userprofile_id`, `department_id`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -446,9 +315,9 @@ CREATE TABLE `session_userprofile_minors` ( `userprofile_id` INTEGER NOT NULL, `department_id` INTEGER NOT NULL, - INDEX `session_userprof_department_id_7a7ea3ed_fk_subject_department_id`(`department_id` ASC), - UNIQUE INDEX `session_userprofile_minors_userprofile_id_d01e3e38_uniq`(`userprofile_id` ASC, `department_id` ASC), - PRIMARY KEY (`id` ASC) + INDEX `session_userprof_department_id_7a7ea3ed_fk_subject_department_id`(`department_id`), + UNIQUE INDEX `session_userprofile_minors_userprofile_id_d01e3e38_uniq`(`userprofile_id`, `department_id`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -457,9 +326,9 @@ CREATE TABLE `session_userprofile_specialized_major` ( `userprofile_id` INTEGER NOT NULL, `department_id` INTEGER NOT NULL, - INDEX `session_userprof_department_id_919e11be_fk_subject_department_id`(`department_id` ASC), - UNIQUE INDEX `session_userprofile_specialized_maj_userprofile_id_3951a553_uniq`(`userprofile_id` ASC, `department_id` ASC), - PRIMARY KEY (`id` ASC) + INDEX `session_userprof_department_id_919e11be_fk_subject_department_id`(`department_id`), + UNIQUE INDEX `session_userprofile_specialized_maj_userprofile_id_3951a553_uniq`(`userprofile_id`, `department_id`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -468,8 +337,9 @@ CREATE TABLE `session_userprofile_taken_lectures` ( `userprofile_id` INTEGER NOT NULL, `lecture_id` INTEGER NOT NULL, - UNIQUE INDEX `userprofile_id`(`userprofile_id` ASC, `lecture_id` ASC), - PRIMARY KEY (`id` ASC) + INDEX `session_userprofile_taken_lectures_lecture_id_fkey`(`lecture_id`), + UNIQUE INDEX `userprofile_id`(`userprofile_id`, `lecture_id`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -486,8 +356,8 @@ CREATE TABLE `subject_classtime` ( `unit_time` SMALLINT NULL, `lecture_id` INTEGER NULL, - INDEX `subject_classtime_72a11f01`(`lecture_id` ASC), - PRIMARY KEY (`id` ASC) + INDEX `subject_classtime_72a11f01`(`lecture_id`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -508,8 +378,15 @@ CREATE TABLE `subject_course` ( `load` DOUBLE NOT NULL, `speech` DOUBLE NOT NULL, `latest_written_datetime` DATETIME(0) NULL, + `title_no_space` VARCHAR(100) NOT NULL, + `title_en_no_space` VARCHAR(200) NOT NULL, - PRIMARY KEY (`id` ASC) + INDEX `subject_course_department_id_fkey`(`department_id`), + INDEX `subject_course_title_en_no_space_index`(`title_en_no_space`), + INDEX `subject_course_title_index`(`title`), + INDEX `subject_course_title_no_space_index`(`title_no_space`), + INDEX `subject_course_title_en_index`(`title_en`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -518,8 +395,9 @@ CREATE TABLE `subject_course_professors` ( `course_id` INTEGER NOT NULL, `professor_id` INTEGER NOT NULL, - UNIQUE INDEX `course_id`(`course_id` ASC, `professor_id` ASC), - PRIMARY KEY (`id` ASC) + INDEX `subject_course_professors_professor_id_fkey`(`professor_id`), + UNIQUE INDEX `course_id`(`course_id`, `professor_id`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -528,9 +406,9 @@ CREATE TABLE `subject_course_related_courses_posterior` ( `from_course_id` INTEGER NOT NULL, `to_course_id` INTEGER NOT NULL, - INDEX `subject_course_relat_to_course_id_5fbd4d28_fk_subject_c`(`to_course_id` ASC), - UNIQUE INDEX `subject_course_related_c_from_course_id_to_course_eaec2f22_uniq`(`from_course_id` ASC, `to_course_id` ASC), - PRIMARY KEY (`id` ASC) + INDEX `subject_course_relat_to_course_id_5fbd4d28_fk_subject_c`(`to_course_id`), + UNIQUE INDEX `subject_course_related_c_from_course_id_to_course_eaec2f22_uniq`(`from_course_id`, `to_course_id`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -539,9 +417,9 @@ CREATE TABLE `subject_course_related_courses_prior` ( `from_course_id` INTEGER NOT NULL, `to_course_id` INTEGER NOT NULL, - INDEX `subject_course_relat_to_course_id_52f44705_fk_subject_c`(`to_course_id` ASC), - UNIQUE INDEX `subject_course_related_c_from_course_id_to_course_74e1ae5f_uniq`(`from_course_id` ASC, `to_course_id` ASC), - PRIMARY KEY (`id` ASC) + INDEX `subject_course_relat_to_course_id_52f44705_fk_subject_c`(`to_course_id`), + UNIQUE INDEX `subject_course_related_c_from_course_id_to_course_74e1ae5f_uniq`(`from_course_id`, `to_course_id`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -551,9 +429,9 @@ CREATE TABLE `subject_courseuser` ( `course_id` INTEGER NOT NULL, `user_profile_id` INTEGER NOT NULL, - UNIQUE INDEX `subject_courseuser_course_id_a26ac0b3_uniq`(`course_id` ASC, `user_profile_id` ASC), - INDEX `subject_courseuser_user_profile_id_4d15ef1b_fk_session_u`(`user_profile_id` ASC), - PRIMARY KEY (`id` ASC) + INDEX `subject_courseuser_user_profile_id_4d15ef1b_fk_session_u`(`user_profile_id`), + UNIQUE INDEX `subject_courseuser_course_id_a26ac0b3_uniq`(`course_id`, `user_profile_id`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -565,7 +443,7 @@ CREATE TABLE `subject_department` ( `name_en` VARCHAR(60) NULL, `visible` BOOLEAN NOT NULL, - PRIMARY KEY (`id` ASC) + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -576,8 +454,8 @@ CREATE TABLE `subject_examtime` ( `end` TIME(0) NOT NULL, `lecture_id` INTEGER NOT NULL, - INDEX `subject_examtime_72a11f01`(`lecture_id` ASC), - PRIMARY KEY (`id` ASC) + INDEX `subject_examtime_72a11f01`(`lecture_id`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -614,10 +492,18 @@ CREATE TABLE `subject_lecture` ( `class_title_en` VARCHAR(100) NULL, `common_title` VARCHAR(100) NULL, `common_title_en` VARCHAR(100) NULL, + `title_no_space` VARCHAR(100) NOT NULL, + `title_en_no_space` VARCHAR(200) NOT NULL, - INDEX `subject_lecture_deleted_bedc6156_uniq`(`deleted` ASC), - INDEX `subject_lecture_type_en_45ee2d3a_uniq`(`type_en` ASC), - PRIMARY KEY (`id` ASC) + INDEX `subject_lecture_deleted_bedc6156_uniq`(`deleted`), + INDEX `subject_lecture_type_en_45ee2d3a_uniq`(`type_en`), + INDEX `subject_lecture_course_id_fkey`(`course_id`), + INDEX `subject_lecture_department_id_fkey`(`department_id`), + INDEX `subject_lecture_title_en_no_space_index`(`title_en_no_space`), + INDEX `subject_lecture_title_no_space_index`(`title_no_space`), + INDEX `subject_lecture_title_en_index`(`title_en`), + INDEX `subject_lecture_title_index`(`title`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -626,8 +512,9 @@ CREATE TABLE `subject_lecture_professors` ( `lecture_id` INTEGER NOT NULL, `professor_id` INTEGER NOT NULL, - UNIQUE INDEX `lecture_id`(`lecture_id` ASC, `professor_id` ASC), - PRIMARY KEY (`id` ASC) + INDEX `subject_lecture_professors_professor_id_fkey`(`professor_id`), + UNIQUE INDEX `lecture_id`(`lecture_id`, `professor_id`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -645,17 +532,7 @@ CREATE TABLE `subject_professor` ( `load` DOUBLE NOT NULL, `speech` DOUBLE NOT NULL, - PRIMARY KEY (`id` ASC) -) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - --- CreateTable -CREATE TABLE `subject_professor_course_list` ( - `id` INTEGER NOT NULL AUTO_INCREMENT, - `professor_id` INTEGER NOT NULL, - `course_id` INTEGER NOT NULL, - - UNIQUE INDEX `professor_id`(`professor_id` ASC, `course_id` ASC), - PRIMARY KEY (`id` ASC) + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -673,10 +550,10 @@ CREATE TABLE `subject_semester` ( `gradePosting` DATETIME(0) NULL, `courseDesciptionSubmission` DATETIME(0) NULL, - INDEX `subject_semester_1b3810e0`(`semester` ASC), - INDEX `subject_semester_84cdc76c`(`year` ASC), - UNIQUE INDEX `subject_semester_year_680c861f_uniq`(`year` ASC, `semester` ASC), - PRIMARY KEY (`id` ASC) + INDEX `subject_semester_1b3810e0`(`semester`), + INDEX `subject_semester_84cdc76c`(`year`), + UNIQUE INDEX `subject_semester_year_680c861f_uniq`(`year`, `semester`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -687,7 +564,7 @@ CREATE TABLE `support_notice` ( `title` VARCHAR(100) NOT NULL, `content` LONGTEXT NOT NULL, - PRIMARY KEY (`id` ASC) + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -695,13 +572,13 @@ CREATE TABLE `support_rate` ( `id` INTEGER NOT NULL AUTO_INCREMENT, `score` SMALLINT NOT NULL, `year` SMALLINT NOT NULL, - `created_datetime` DATETIME(0) NULL, + `created_datetime` DATETIME(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0), `user_id` INTEGER NOT NULL, `version` VARCHAR(20) NOT NULL, - INDEX `support_rate_created_datetime_d38a29eb`(`created_datetime` ASC), - UNIQUE INDEX `support_rate_user_id_year_a62fc7f7_uniq`(`user_id` ASC, `year` ASC), - PRIMARY KEY (`id` ASC) + INDEX `support_rate_created_datetime_d38a29eb`(`created_datetime`), + UNIQUE INDEX `support_rate_user_id_year_a62fc7f7_uniq`(`user_id`, `year`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -712,7 +589,7 @@ CREATE TABLE `timetable_oldtimetable` ( `semester` SMALLINT NULL, `table_no` SMALLINT NULL, - PRIMARY KEY (`id` ASC) + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -721,9 +598,9 @@ CREATE TABLE `timetable_oldtimetable_lectures` ( `oldtimetable_id` INTEGER NOT NULL, `lecture_id` INTEGER NOT NULL, - INDEX `timetable_oldtimetable_lecture_id_b19d5300_fk_subject_lecture_id`(`lecture_id` ASC), - UNIQUE INDEX `timetable_oldtimetable_lecture_oldtimetable_id_27bf3d09_uniq`(`oldtimetable_id` ASC, `lecture_id` ASC), - PRIMARY KEY (`id` ASC) + INDEX `timetable_oldtimetable_lecture_id_b19d5300_fk_subject_lecture_id`(`lecture_id`), + UNIQUE INDEX `timetable_oldtimetable_lecture_oldtimetable_id_27bf3d09_uniq`(`oldtimetable_id`, `lecture_id`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -734,11 +611,11 @@ CREATE TABLE `timetable_timetable` ( `user_id` INTEGER NOT NULL, `arrange_order` SMALLINT NOT NULL, - INDEX `timetable_timetable_arrange_order_84c8935c`(`arrange_order` ASC), - INDEX `timetable_timetable_semester_d8ce5d37_uniq`(`semester` ASC), - INDEX `timetable_timetable_user_id_0d214170_fk_session_userprofile_id`(`user_id` ASC), - INDEX `timetable_timetable_year_907cf59a_uniq`(`year` ASC), - PRIMARY KEY (`id` ASC) + INDEX `timetable_timetable_arrange_order_84c8935c`(`arrange_order`), + INDEX `timetable_timetable_semester_d8ce5d37_uniq`(`semester`), + INDEX `timetable_timetable_user_id_0d214170_fk_session_userprofile_id`(`user_id`), + INDEX `timetable_timetable_year_907cf59a_uniq`(`year`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -747,9 +624,9 @@ CREATE TABLE `timetable_timetable_lectures` ( `timetable_id` INTEGER NOT NULL, `lecture_id` INTEGER NOT NULL, - INDEX `timetable_timetable_le_lecture_id_79aa5f2e_fk_subject_lecture_id`(`lecture_id` ASC), - UNIQUE INDEX `timetable_timetable_lecture_timetable_id_57195f56_uniq`(`timetable_id` ASC, `lecture_id` ASC), - PRIMARY KEY (`id` ASC) + INDEX `timetable_timetable_le_lecture_id_79aa5f2e_fk_subject_lecture_id`(`lecture_id`), + UNIQUE INDEX `timetable_timetable_lecture_timetable_id_57195f56_uniq`(`timetable_id`, `lecture_id`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -757,8 +634,8 @@ CREATE TABLE `timetable_wishlist` ( `id` INTEGER NOT NULL AUTO_INCREMENT, `user_id` INTEGER NOT NULL, - UNIQUE INDEX `user_id`(`user_id` ASC), - PRIMARY KEY (`id` ASC) + UNIQUE INDEX `user_id`(`user_id`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- CreateTable @@ -767,9 +644,159 @@ CREATE TABLE `timetable_wishlist_lectures` ( `wishlist_id` INTEGER NOT NULL, `lecture_id` INTEGER NOT NULL, - INDEX `timetable_wishlist_lec_lecture_id_1ab5d523_fk_subject_lecture_id`(`lecture_id` ASC), - UNIQUE INDEX `timetable_wishlist_lectures_wishlist_id_e4c47efe_uniq`(`wishlist_id` ASC, `lecture_id` ASC), - PRIMARY KEY (`id` ASC) + INDEX `timetable_wishlist_lec_lecture_id_1ab5d523_fk_subject_lecture_id`(`lecture_id`), + UNIQUE INDEX `timetable_wishlist_lectures_wishlist_id_e4c47efe_uniq`(`wishlist_id`, `lecture_id`), + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- CreateTable +CREATE TABLE `graduation_additionaltrack` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `start_year` INTEGER NOT NULL, + `end_year` INTEGER NOT NULL, + `type` VARCHAR(32) NOT NULL, + `major_required` INTEGER NOT NULL, + `major_elective` INTEGER NOT NULL, + `department_id` INTEGER NULL, + + INDEX `graduation_additiona_department_id_788c5289_fk_subject_d`(`department_id`), + INDEX `graduation_additionaltrack_end_year_6af1030b`(`end_year`), + INDEX `graduation_additionaltrack_start_year_7a87318d`(`start_year`), + INDEX `graduation_additionaltrack_type_0fa38fc5`(`type`), + UNIQUE INDEX `graduation_additionaltra_end_year_type_department_9d873c1b_uniq`(`end_year`, `type`, `department_id`), + UNIQUE INDEX `graduation_additionaltra_start_year_type_departme_763552eb_uniq`(`start_year`, `type`, `department_id`), + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- CreateTable +CREATE TABLE `graduation_generaltrack` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `start_year` INTEGER NOT NULL, + `end_year` INTEGER NOT NULL, + `is_foreign` BOOLEAN NOT NULL, + `total_credit` INTEGER NOT NULL, + `total_au` INTEGER NOT NULL, + `basic_required` INTEGER NOT NULL, + `basic_elective` INTEGER NOT NULL, + `thesis_study` INTEGER NOT NULL, + `thesis_study_doublemajor` INTEGER NOT NULL, + `general_required_credit` INTEGER NOT NULL, + `general_required_au` INTEGER NOT NULL, + `humanities` INTEGER NOT NULL, + `humanities_doublemajor` INTEGER NOT NULL, + + INDEX `graduation_generaltrack_end_year_3bba699e`(`end_year`), + INDEX `graduation_generaltrack_is_foreign_d38919a2`(`is_foreign`), + INDEX `graduation_generaltrack_start_year_00aee782`(`start_year`), + UNIQUE INDEX `graduation_generaltrack_end_year_is_foreign_1f062f8b_uniq`(`end_year`, `is_foreign`), + UNIQUE INDEX `graduation_generaltrack_start_year_is_foreign_c1eb425f_uniq`(`start_year`, `is_foreign`), + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- CreateTable +CREATE TABLE `graduation_majortrack` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `start_year` INTEGER NOT NULL, + `end_year` INTEGER NOT NULL, + `basic_elective_doublemajor` INTEGER NOT NULL, + `major_required` INTEGER NOT NULL, + `major_elective` INTEGER NOT NULL, + `department_id` INTEGER NOT NULL, + + INDEX `graduation_majortrac_department_id_81bfc8fa_fk_subject_d`(`department_id`), + INDEX `graduation_majortrack_end_year_57017559`(`end_year`), + INDEX `graduation_majortrack_start_year_6281dc28`(`start_year`), + UNIQUE INDEX `graduation_majortrack_end_year_department_id_b3ef1bc8_uniq`(`end_year`, `department_id`), + UNIQUE INDEX `graduation_majortrack_start_year_department_id_59122c6d_uniq`(`start_year`, `department_id`), + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- CreateTable +CREATE TABLE `planner_arbitraryplanneritem` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `is_excluded` BOOLEAN NOT NULL, + `year` INTEGER NOT NULL, + `semester` INTEGER NOT NULL, + `type` VARCHAR(12) NOT NULL, + `type_en` VARCHAR(36) NOT NULL, + `credit` INTEGER NOT NULL, + `credit_au` INTEGER NOT NULL, + `department_id` INTEGER NULL, + `planner_id` INTEGER NOT NULL, + + INDEX `planner_arbitrarypla_department_id_0dc7ce25_fk_subject_d`(`department_id`), + INDEX `planner_arbitrarypla_planner_id_d6069d2c_fk_planner_p`(`planner_id`), + INDEX `planner_arbitraryplanneritem_semester_7508baa5`(`semester`), + INDEX `planner_arbitraryplanneritem_year_5a0c7252`(`year`), + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- CreateTable +CREATE TABLE `planner_futureplanneritem` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `is_excluded` BOOLEAN NOT NULL, + `year` INTEGER NOT NULL, + `semester` INTEGER NOT NULL, + `course_id` INTEGER NOT NULL, + `planner_id` INTEGER NOT NULL, + + INDEX `planner_futureplanne_course_id_b1a06444_fk_subject_c`(`course_id`), + INDEX `planner_futureplanne_planner_id_dfd70193_fk_planner_p`(`planner_id`), + INDEX `planner_futureplanneritem_semester_cda6512e`(`semester`), + INDEX `planner_futureplanneritem_year_5e3a2d4e`(`year`), + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- CreateTable +CREATE TABLE `planner_planner` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `start_year` INTEGER NOT NULL, + `end_year` INTEGER NOT NULL, + `arrange_order` SMALLINT NOT NULL, + `general_track_id` INTEGER NOT NULL, + `major_track_id` INTEGER NOT NULL, + `user_id` INTEGER NOT NULL, + + INDEX `planner_planner_arrange_order_e50a3044`(`arrange_order`), + INDEX `planner_planner_end_year_e5fab7f3`(`end_year`), + INDEX `planner_planner_general_track_id_6d607973_fk_graduatio`(`general_track_id`), + INDEX `planner_planner_major_track_id_9f7204bd_fk_graduatio`(`major_track_id`), + INDEX `planner_planner_start_year_463173f3`(`start_year`), + INDEX `planner_planner_user_id_17740247_fk_session_userprofile_id`(`user_id`), + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- CreateTable +CREATE TABLE `planner_planner_additional_tracks` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `planner_id` INTEGER NOT NULL, + `additionaltrack_id` INTEGER NOT NULL, + + INDEX `planner_planner_addi_additionaltrack_id_c46b8c4e_fk_graduatio`(`additionaltrack_id`), + UNIQUE INDEX `planner_planner_addition_planner_id_additionaltra_2298c5cd_uniq`(`planner_id`, `additionaltrack_id`), + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- CreateTable +CREATE TABLE `planner_takenplanneritem` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `is_excluded` BOOLEAN NOT NULL, + `lecture_id` INTEGER NOT NULL, + `planner_id` INTEGER NOT NULL, + + INDEX `planner_takenplanner_lecture_id_9b2d30d8_fk_subject_l`(`lecture_id`), + UNIQUE INDEX `planner_takenplanneritem_planner_id_lecture_id_4b39b432_uniq`(`planner_id`, `lecture_id`), + PRIMARY KEY (`id`) +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- CreateTable +CREATE TABLE `subject_professor_course_list` ( + `id` INTEGER NOT NULL AUTO_INCREMENT, + `professor_id` INTEGER NOT NULL, + `course_id` INTEGER NOT NULL, + + UNIQUE INDEX `professor_id`(`professor_id`, `course_id`), + PRIMARY KEY (`id`) ) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- AddForeignKey @@ -799,12 +826,6 @@ ALTER TABLE `django_admin_log` ADD CONSTRAINT `djang_content_type_id_69791429515 -- AddForeignKey ALTER TABLE `django_admin_log` ADD CONSTRAINT `django_admin_log_user_id_52fdd58701c5f563_fk_auth_user_id` FOREIGN KEY (`user_id`) REFERENCES `auth_user`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; --- AddForeignKey -ALTER TABLE `graduation_additionaltrack` ADD CONSTRAINT `graduation_additiona_department_id_788c5289_fk_subject_d` FOREIGN KEY (`department_id`) REFERENCES `subject_department`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; - --- AddForeignKey -ALTER TABLE `graduation_majortrack` ADD CONSTRAINT `graduation_majortrac_department_id_81bfc8fa_fk_subject_d` FOREIGN KEY (`department_id`) REFERENCES `subject_department`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; - -- AddForeignKey ALTER TABLE `main_famoushumanityreviewdailyfeed_reviews` ADD CONSTRAINT `e567529fdfd543a96610b342fea2bb84` FOREIGN KEY (`famoushumanityreviewdailyfeed_id`) REFERENCES `main_famoushumanityreviewdailyfeed`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; @@ -839,37 +860,28 @@ ALTER TABLE `main_reviewwritedailyuserfeed` ADD CONSTRAINT `main_reviewwritedail ALTER TABLE `main_reviewwritedailyuserfeed` ADD CONSTRAINT `main_reviewwritedaily_user_id_9ffd0881_fk_session_userprofile_id` FOREIGN KEY (`user_id`) REFERENCES `session_userprofile`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; -- AddForeignKey -ALTER TABLE `planner_arbitraryplanneritem` ADD CONSTRAINT `planner_arbitrarypla_department_id_0dc7ce25_fk_subject_d` FOREIGN KEY (`department_id`) REFERENCES `subject_department`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; +ALTER TABLE `review_review` ADD CONSTRAINT `review_review_course_id_fkey` FOREIGN KEY (`course_id`) REFERENCES `subject_course`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; -- AddForeignKey -ALTER TABLE `planner_arbitraryplanneritem` ADD CONSTRAINT `planner_arbitrarypla_planner_id_d6069d2c_fk_planner_p` FOREIGN KEY (`planner_id`) REFERENCES `planner_planner`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; +ALTER TABLE `review_review` ADD CONSTRAINT `review_review_lecture_id_fkey` FOREIGN KEY (`lecture_id`) REFERENCES `subject_lecture`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE; -- AddForeignKey -ALTER TABLE `planner_futureplanneritem` ADD CONSTRAINT `planner_futureplanne_course_id_b1a06444_fk_subject_c` FOREIGN KEY (`course_id`) REFERENCES `subject_course`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; +ALTER TABLE `review_review` ADD CONSTRAINT `review_review_writer_id_fkey` FOREIGN KEY (`writer_id`) REFERENCES `session_userprofile`(`id`) ON DELETE SET NULL ON UPDATE CASCADE; -- AddForeignKey -ALTER TABLE `planner_futureplanneritem` ADD CONSTRAINT `planner_futureplanne_planner_id_dfd70193_fk_planner_p` FOREIGN KEY (`planner_id`) REFERENCES `planner_planner`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; +ALTER TABLE `review_reviewvote` ADD CONSTRAINT `review_reviewvote_review_id_fkey` FOREIGN KEY (`review_id`) REFERENCES `review_review`(`id`) ON DELETE CASCADE ON UPDATE CASCADE; -- AddForeignKey -ALTER TABLE `planner_planner` ADD CONSTRAINT `planner_planner_general_track_id_6d607973_fk_graduatio` FOREIGN KEY (`general_track_id`) REFERENCES `graduation_generaltrack`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; +ALTER TABLE `review_reviewvote` ADD CONSTRAINT `review_reviewvote_userprofile_id_fkey` FOREIGN KEY (`userprofile_id`) REFERENCES `session_userprofile`(`id`) ON DELETE SET NULL ON UPDATE CASCADE; -- AddForeignKey -ALTER TABLE `planner_planner` ADD CONSTRAINT `planner_planner_major_track_id_9f7204bd_fk_graduatio` FOREIGN KEY (`major_track_id`) REFERENCES `graduation_majortrack`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; +ALTER TABLE `session_userprofile` ADD CONSTRAINT `session_userprofile_department_id_fkey` FOREIGN KEY (`department_id`) REFERENCES `subject_department`(`id`) ON DELETE SET NULL ON UPDATE RESTRICT; -- AddForeignKey -ALTER TABLE `planner_planner` ADD CONSTRAINT `planner_planner_user_id_17740247_fk_session_userprofile_id` FOREIGN KEY (`user_id`) REFERENCES `session_userprofile`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; - --- AddForeignKey -ALTER TABLE `planner_planner_additional_tracks` ADD CONSTRAINT `planner_planner_addi_additionaltrack_id_c46b8c4e_fk_graduatio` FOREIGN KEY (`additionaltrack_id`) REFERENCES `graduation_additionaltrack`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; +ALTER TABLE `session_userprofile_favorite_departments` ADD CONSTRAINT `session_userprofile_favorite_departments_department_id_fkey` FOREIGN KEY (`department_id`) REFERENCES `subject_department`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; -- AddForeignKey -ALTER TABLE `planner_planner_additional_tracks` ADD CONSTRAINT `planner_planner_addi_planner_id_e439a309_fk_planner_p` FOREIGN KEY (`planner_id`) REFERENCES `planner_planner`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; - --- AddForeignKey -ALTER TABLE `planner_takenplanneritem` ADD CONSTRAINT `planner_takenplanner_lecture_id_9b2d30d8_fk_subject_l` FOREIGN KEY (`lecture_id`) REFERENCES `subject_lecture`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; - --- AddForeignKey -ALTER TABLE `planner_takenplanneritem` ADD CONSTRAINT `planner_takenplanner_planner_id_b725ff83_fk_planner_p` FOREIGN KEY (`planner_id`) REFERENCES `planner_planner`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; +ALTER TABLE `session_userprofile_favorite_departments` ADD CONSTRAINT `session_userprofile_favorite_departments_userprofile_id_fkey` FOREIGN KEY (`userprofile_id`) REFERENCES `session_userprofile`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; -- AddForeignKey ALTER TABLE `session_userprofile_majors` ADD CONSTRAINT `session_userpr_userprofile_id_20f3742a_fk_session_userprofile_id` FOREIGN KEY (`userprofile_id`) REFERENCES `session_userprofile`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; @@ -889,9 +901,24 @@ ALTER TABLE `session_userprofile_specialized_major` ADD CONSTRAINT `session_user -- AddForeignKey ALTER TABLE `session_userprofile_specialized_major` ADD CONSTRAINT `session_userprof_department_id_919e11be_fk_subject_department_id` FOREIGN KEY (`department_id`) REFERENCES `subject_department`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; +-- AddForeignKey +ALTER TABLE `session_userprofile_taken_lectures` ADD CONSTRAINT `session_userprofile_taken_lectures_lecture_id_fkey` FOREIGN KEY (`lecture_id`) REFERENCES `subject_lecture`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; + +-- AddForeignKey +ALTER TABLE `session_userprofile_taken_lectures` ADD CONSTRAINT `session_userprofile_taken_lectures_userprofile_id_fkey` FOREIGN KEY (`userprofile_id`) REFERENCES `session_userprofile`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; + -- AddForeignKey ALTER TABLE `subject_classtime` ADD CONSTRAINT `subject_classtime_lecture_id_bf773e65_fk_subject_lecture_id` FOREIGN KEY (`lecture_id`) REFERENCES `subject_lecture`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; +-- AddForeignKey +ALTER TABLE `subject_course` ADD CONSTRAINT `subject_course_department_id_fkey` FOREIGN KEY (`department_id`) REFERENCES `subject_department`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; + +-- AddForeignKey +ALTER TABLE `subject_course_professors` ADD CONSTRAINT `subject_course_professors_course_id_fkey` FOREIGN KEY (`course_id`) REFERENCES `subject_course`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; + +-- AddForeignKey +ALTER TABLE `subject_course_professors` ADD CONSTRAINT `subject_course_professors_professor_id_fkey` FOREIGN KEY (`professor_id`) REFERENCES `subject_professor`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; + -- AddForeignKey ALTER TABLE `subject_course_related_courses_posterior` ADD CONSTRAINT `subject_course_relat_from_course_id_f520f461_fk_subject_c` FOREIGN KEY (`from_course_id`) REFERENCES `subject_course`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; @@ -913,6 +940,18 @@ ALTER TABLE `subject_courseuser` ADD CONSTRAINT `subject_courseuser_user_profile -- AddForeignKey ALTER TABLE `subject_examtime` ADD CONSTRAINT `subject_examtime_lecture_id_a35fa20c_fk_subject_lecture_id` FOREIGN KEY (`lecture_id`) REFERENCES `subject_lecture`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; +-- AddForeignKey +ALTER TABLE `subject_lecture` ADD CONSTRAINT `subject_lecture_course_id_fkey` FOREIGN KEY (`course_id`) REFERENCES `subject_course`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; + +-- AddForeignKey +ALTER TABLE `subject_lecture` ADD CONSTRAINT `subject_lecture_department_id_fkey` FOREIGN KEY (`department_id`) REFERENCES `subject_department`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; + +-- AddForeignKey +ALTER TABLE `subject_lecture_professors` ADD CONSTRAINT `subject_lecture_professors_lecture_id_fkey` FOREIGN KEY (`lecture_id`) REFERENCES `subject_lecture`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; + +-- AddForeignKey +ALTER TABLE `subject_lecture_professors` ADD CONSTRAINT `subject_lecture_professors_professor_id_fkey` FOREIGN KEY (`professor_id`) REFERENCES `subject_professor`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; + -- AddForeignKey ALTER TABLE `support_rate` ADD CONSTRAINT `support_rate_user_id_6d69ec9d_fk_session_userprofile_id` FOREIGN KEY (`user_id`) REFERENCES `session_userprofile`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; @@ -940,3 +979,42 @@ ALTER TABLE `timetable_wishlist_lectures` ADD CONSTRAINT `timetable_wishlist_lec -- AddForeignKey ALTER TABLE `timetable_wishlist_lectures` ADD CONSTRAINT `timetable_wishlist_wishlist_id_efc7ae12_fk_timetable_wishlist_id` FOREIGN KEY (`wishlist_id`) REFERENCES `timetable_wishlist`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; +-- AddForeignKey +ALTER TABLE `graduation_additionaltrack` ADD CONSTRAINT `graduation_additiona_department_id_788c5289_fk_subject_d` FOREIGN KEY (`department_id`) REFERENCES `subject_department`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; + +-- AddForeignKey +ALTER TABLE `graduation_majortrack` ADD CONSTRAINT `graduation_majortrac_department_id_81bfc8fa_fk_subject_d` FOREIGN KEY (`department_id`) REFERENCES `subject_department`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; + +-- AddForeignKey +ALTER TABLE `planner_arbitraryplanneritem` ADD CONSTRAINT `planner_arbitrarypla_department_id_0dc7ce25_fk_subject_d` FOREIGN KEY (`department_id`) REFERENCES `subject_department`(`id`) ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE `planner_arbitraryplanneritem` ADD CONSTRAINT `planner_arbitrarypla_planner_id_d6069d2c_fk_planner_p` FOREIGN KEY (`planner_id`) REFERENCES `planner_planner`(`id`) ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE `planner_futureplanneritem` ADD CONSTRAINT `planner_futureplanne_course_id_b1a06444_fk_subject_c` FOREIGN KEY (`course_id`) REFERENCES `subject_course`(`id`) ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE `planner_futureplanneritem` ADD CONSTRAINT `planner_futureplanne_planner_id_dfd70193_fk_planner_p` FOREIGN KEY (`planner_id`) REFERENCES `planner_planner`(`id`) ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE `planner_planner` ADD CONSTRAINT `planner_planner_general_track_id_6d607973_fk_graduatio` FOREIGN KEY (`general_track_id`) REFERENCES `graduation_generaltrack`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; + +-- AddForeignKey +ALTER TABLE `planner_planner` ADD CONSTRAINT `planner_planner_major_track_id_9f7204bd_fk_graduatio` FOREIGN KEY (`major_track_id`) REFERENCES `graduation_majortrack`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; + +-- AddForeignKey +ALTER TABLE `planner_planner` ADD CONSTRAINT `planner_planner_user_id_17740247_fk_session_userprofile_id` FOREIGN KEY (`user_id`) REFERENCES `session_userprofile`(`id`) ON DELETE RESTRICT ON UPDATE RESTRICT; + +-- AddForeignKey +ALTER TABLE `planner_planner_additional_tracks` ADD CONSTRAINT `planner_planner_addi_additionaltrack_id_c46b8c4e_fk_graduatio` FOREIGN KEY (`additionaltrack_id`) REFERENCES `graduation_additionaltrack`(`id`) ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE `planner_planner_additional_tracks` ADD CONSTRAINT `planner_planner_addi_planner_id_e439a309_fk_planner_p` FOREIGN KEY (`planner_id`) REFERENCES `planner_planner`(`id`) ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE `planner_takenplanneritem` ADD CONSTRAINT `planner_takenplanner_lecture_id_9b2d30d8_fk_subject_l` FOREIGN KEY (`lecture_id`) REFERENCES `subject_lecture`(`id`) ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE `planner_takenplanneritem` ADD CONSTRAINT `planner_takenplanner_planner_id_b725ff83_fk_planner_p` FOREIGN KEY (`planner_id`) REFERENCES `planner_planner`(`id`) ON DELETE CASCADE ON UPDATE NO ACTION; + diff --git a/src/prisma/migrations/20230511150955_reconstruct_session_userprofile/mergeAuthUserAndSessionUser.ts b/src/prisma/migrations/20230511150955_reconstruct_session_userprofile/mergeAuthUserAndSessionUser.ts deleted file mode 100644 index e681ffd6..00000000 --- a/src/prisma/migrations/20230511150955_reconstruct_session_userprofile/mergeAuthUserAndSessionUser.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { PrismaService } from '../../prisma.service'; -import settings from '../../../settings'; -import { normalizeArray } from '../../../common/utils/method.utils'; - -async function main() { - const ormConfig = settings().ormconfig(); - console.log(ormConfig); - const prisma = new PrismaService(); - try { - await prisma.$connect(); - - const auth_users = await prisma.auth_user.findMany(); - const session_users = await prisma.session_userprofile.findMany(); - const auth_users_map = normalizeArray( - auth_users, - (auth_users) => auth_users.id, - ); - - const BATCH_SIZE = 1000; - for (let i = 0; i < Math.ceil(session_users.length / BATCH_SIZE); i++) { - const slicedSessionUsers = session_users.slice( - i * BATCH_SIZE, - (i + 1) * BATCH_SIZE, - ); - const bulkPromises = slicedSessionUsers.map(async (session_user) => { - const matched_auth_user = auth_users_map[session_user.user_id]; - if (matched_auth_user) { - const updateData = { - first_name: matched_auth_user.first_name, - last_name: matched_auth_user.last_name, - email: matched_auth_user.email, - date_joined: matched_auth_user.date_joined, - }; - session_user.first_name = matched_auth_user.first_name; - session_user.last_name = matched_auth_user.last_name; - session_user.email = matched_auth_user.email; - session_user.date_joined = matched_auth_user.date_joined; - // console.log(session_user) - - const result = prisma.session_userprofile.update({ - where: { - id: session_user.id, - }, - data: updateData, - }); - return result; - } - }); - await Promise.all(bulkPromises); - } - } catch (e) { - console.error(e); - } finally { - await prisma.$disconnect(); - } -} - -main() - .then(() => { - console.log('done'); - }) - .catch((e) => { - console.error(e); - }) - .finally(() => { - process.exit(0); - }); diff --git a/src/prisma/migrations/20230511150955_reconstruct_session_userprofile/migration.sql b/src/prisma/migrations/20230511150955_reconstruct_session_userprofile/migration.sql deleted file mode 100644 index 04878567..00000000 --- a/src/prisma/migrations/20230511150955_reconstruct_session_userprofile/migration.sql +++ /dev/null @@ -1,16 +0,0 @@ -/* - Warnings: - - - You are about to drop the column `language` on the `session_userprofile` table. All the data in the column will be lost. - - You are about to drop the column `portal_check` on the `session_userprofile` table. All the data in the column will be lost. - - Added the required column `date_joined` to the `session_userprofile` table without a default value. This is not possible if the table is not empty. - - Added the required column `first_name` to the `session_userprofile` table without a default value. This is not possible if the table is not empty. - - Added the required column `last_name` to the `session_userprofile` table without a default value. This is not possible if the table is not empty. - -*/ --- AlterTable -ALTER TABLE `session_userprofile` DROP COLUMN `language`, - DROP COLUMN `portal_check`, - ADD COLUMN `date_joined` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - ADD COLUMN `first_name` VARCHAR(30) NOT NULL, - ADD COLUMN `last_name` VARCHAR(150) NOT NULL; diff --git a/src/prisma/migrations/20230511231222_drop_user_id_in_session_userprofile/migration.sql b/src/prisma/migrations/20230511231222_drop_user_id_in_session_userprofile/migration.sql deleted file mode 100644 index fc7323e4..00000000 --- a/src/prisma/migrations/20230511231222_drop_user_id_in_session_userprofile/migration.sql +++ /dev/null @@ -1,12 +0,0 @@ -/* - Warnings: - - - You are about to drop the column `user_id` on the `session_userprofile` table. All the data in the column will be lost. - -*/ --- DropIndex -DROP INDEX `session_userprofile_user_id_09dd6af1_uniq` ON `session_userprofile`; - --- AlterTable -ALTER TABLE `session_userprofile` DROP COLUMN `user_id`, - ALTER COLUMN `date_joined` DROP DEFAULT; diff --git a/src/prisma/migrations/20230621131500_add_refresh_token_to_session_user_profile/addRefreshToken.ts b/src/prisma/migrations/20230621131500_add_refresh_token_to_session_user_profile/addRefreshToken.ts deleted file mode 100644 index fc749c67..00000000 --- a/src/prisma/migrations/20230621131500_add_refresh_token_to_session_user_profile/addRefreshToken.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { PrismaService } from "../../prisma.service"; -import settings from "../../../settings"; -import { JwtService } from "@nestjs/jwt"; -import { UserRepository } from "../../repositories/user.repository"; -import { AuthService } from "../../../modules/auth/auth.service"; -import * as bcrypt from "bcrypt"; - -async function main() { - const ormConfig = settings().ormconfig(); - console.log(ormConfig); - const prisma = new PrismaService(); - const userRepository = new UserRepository(prisma); - const authService = new AuthService(userRepository, new JwtService({})); - try { - await prisma.$connect(); - await prisma.$transaction(async (tx)=> { - const session_users = await tx.session_userprofile.findMany(); - - const BATCH_SIZE = 1000; - console.log(session_users.length); - for (let i = 0; i < Math.ceil(session_users.length / BATCH_SIZE); i++) { - const slicedSessionUsers = session_users.slice( - i * BATCH_SIZE, - (i + 1) * BATCH_SIZE - ); - const bulkPromises = slicedSessionUsers.map(async (session_user) => { - const { refreshToken, ...refreshTokenOptions } = authService.getCookieWithRefreshToken(session_user.sid) - const salt = await bcrypt.genSalt(Number(process.env.saltRounds)); - const encryptedRefreshToken = await bcrypt.hash(refreshToken, salt); - session_user.refresh_token = encryptedRefreshToken - const result = tx.session_userprofile.update({ - where: { - id: session_user.id - }, - data: { - refresh_token: encryptedRefreshToken - } - }); - return result; - }); - await Promise.all(bulkPromises); - console.log(`batch ${i} done`); - } - },{ - maxWait: 5000, - timeout: 300000 - }) - } catch (e) { - console.error(e); - } finally { - await prisma.$disconnect(); - } -} - -main() - .then(() => { - console.log("done"); - }) - .catch((e) => { - console.error(e); - }) - .finally(() => { - process.exit(0); - }); diff --git a/src/prisma/migrations/20230621131500_add_refresh_token_to_session_user_profile/migration.sql b/src/prisma/migrations/20230621131500_add_refresh_token_to_session_user_profile/migration.sql deleted file mode 100644 index 4ec1d499..00000000 --- a/src/prisma/migrations/20230621131500_add_refresh_token_to_session_user_profile/migration.sql +++ /dev/null @@ -1,2 +0,0 @@ --- AlterTable -ALTER TABLE `session_userprofile` ADD COLUMN `refresh_token` VARCHAR(255) NULL; diff --git a/src/prisma/prisma.module.ts b/src/prisma/prisma.module.ts index ec0ce329..43a7c2df 100644 --- a/src/prisma/prisma.module.ts +++ b/src/prisma/prisma.module.ts @@ -1,8 +1,79 @@ -import { Module } from '@nestjs/common'; +import { Module, OnModuleInit } from '@nestjs/common'; +import { CourseMiddleware } from './middleware/prisma.course'; +import { DepartmentMiddleware } from './middleware/prisma.department'; +import { LectureMiddleware } from './middleware/prisma.lecture'; +import { LectureProfessorsMiddleware } from './middleware/prisma.lectureprofessors'; +import { ReviewMiddleware } from './middleware/prisma.reviews'; +import { ReviewVoteMiddleware } from './middleware/prisma.reviewvote'; +import { SemesterMiddleware } from './middleware/prisma.semester'; +import { TimetableMiddleware } from './middleware/prisma.timetable'; +import { TimetableLectureMiddleware } from './middleware/prisma.timetablelecture'; import { PrismaService } from './prisma.service'; +import { CourseRepository } from './repositories/course.repository'; +import { DepartmentRepository } from './repositories/department.repository'; +import { LectureRepository } from './repositories/lecture.repository'; +import { NoticesRepository } from './repositories/notices.repository'; +import { PlannerRepository } from './repositories/planner.repository'; +import { ReviewsRepository } from './repositories/review.repository'; +import { SemesterRepository } from './repositories/semester.repository'; +import { TimetableRepository } from './repositories/timetable.repository'; +import { TracksRepository } from './repositories/track.repository'; +import { UserRepository } from './repositories/user.repository'; +import { WishlistRepository } from './repositories/wishlist.repository'; +import { TranManager } from './transactionManager'; + +// const extendPrismaClient = { +// provide: PrismaService, +// useFactory: () => { +// const prisma = new PrismaService() +// console.log("prisma instance created"); +// return prisma; +// }, +// }; @Module({ - providers: [PrismaService], - exports: [PrismaService], + providers: [ + PrismaService, + UserRepository, + LectureRepository, + ReviewsRepository, + DepartmentRepository, + CourseRepository, + SemesterRepository, + TimetableRepository, + WishlistRepository, + PlannerRepository, + TracksRepository, + NoticesRepository, + ReviewMiddleware, + TranManager, + ], + exports: [ + PrismaService, + UserRepository, + LectureRepository, + ReviewsRepository, + DepartmentRepository, + CourseRepository, + SemesterRepository, + TimetableRepository, + WishlistRepository, + PlannerRepository, + TracksRepository, + NoticesRepository, + ], }) -export class PrismaModule {} +export class PrismaModule implements OnModuleInit { + constructor(private readonly prisma: PrismaService) {} + onModuleInit() { + CourseMiddleware.initialize(this.prisma); + DepartmentMiddleware.initialize(this.prisma); + LectureMiddleware.initialize(this.prisma); + LectureProfessorsMiddleware.initialize(this.prisma); + ReviewMiddleware.initialize(this.prisma); + ReviewVoteMiddleware.initialize(this.prisma); + SemesterMiddleware.initialize(this.prisma); + TimetableMiddleware.initialize(this.prisma); + TimetableLectureMiddleware.initialize(this.prisma); + } +} diff --git a/src/prisma/prisma.service.ts b/src/prisma/prisma.service.ts index 0d534565..7b778208 100644 --- a/src/prisma/prisma.service.ts +++ b/src/prisma/prisma.service.ts @@ -1,6 +1,7 @@ -import { INestApplication, Injectable, OnModuleInit } from "@nestjs/common"; +import { INestApplication, Injectable, OnModuleInit } from '@nestjs/common'; import { PrismaClient } from '@prisma/client'; -import settings from "../settings"; +import settings from '../settings'; +import { signalExtension } from './custom-prisma-client'; @Injectable() export class PrismaService extends PrismaClient implements OnModuleInit { @@ -11,11 +12,12 @@ export class PrismaService extends PrismaClient implements OnModuleInit { async onModuleInit() { await this.$connect(); - } - - async enableShutdownHooks(app: INestApplication) { - this.$on('beforeExit', async () => { - await app.close(); + // @ts-ignore + this.$on('query', async (e) => { + // @ts-ignore + console.log(`Query: ${e.query} ${e.params}`); }); + const extendedClient = this.$extends(signalExtension); + Object.assign(this, extendedClient); } } diff --git a/src/prisma/repositories/course.repository.ts b/src/prisma/repositories/course.repository.ts new file mode 100644 index 00000000..ab7736f8 --- /dev/null +++ b/src/prisma/repositories/course.repository.ts @@ -0,0 +1,484 @@ +import { Injectable } from '@nestjs/common'; +import { Prisma } from '@prisma/client'; +import { ECourse } from 'src/common/entities/ECourse'; +import { ELecture } from 'src/common/entities/ELecture'; +import { EReview } from 'src/common/entities/EReview'; +import { ICourse } from 'src/common/interfaces'; +import { + applyOffset, + applyOrder, + orderFilter, +} from 'src/common/utils/search.utils'; +import { PrismaService } from '../prisma.service'; +import LectureQueryDto = ICourse.LectureQueryDto; + +@Injectable() +export class CourseRepository { + constructor(private readonly prisma: PrismaService) {} + + private TYPE_ACRONYMS = { + GR: 'General Required', + MGC: 'Mandatory General Courses', + BE: 'Basic Elective', + BR: 'Basic Required', + EG: 'Elective(Graduate)', + HSE: 'Humanities & Social Elective', + OE: 'Other Elective', + ME: 'Major Elective', + MR: 'Major Required', + }; + private MAJOR_ACRONYMS = [ + 'CE', + 'MSB', + 'ME', + 'PH', + 'BiS', + 'IE', + 'ID', + 'BS', + 'CBE', + 'MAS', + 'MS', + 'NQE', + 'HSS', + 'EE', + 'CS', + 'AE', + 'CH', + 'TS', + 'BTM', + 'BCS', + 'SS', + ]; + + public async getCourseById(id: number): Promise { + return await this.prisma.subject_course.findUnique({ + include: ECourse.Details.include, + where: { + id: id, + }, + }); + } + + public async getLecturesByCourseId( + query: LectureQueryDto, + id: number, + ): Promise { + const order = query.order ? query.order : ['year', 'semester', 'class_no']; + const course = await this.prisma.subject_course.findUnique({ + include: { + lecture: { + include: { + subject_department: true, + subject_lecture_professors: { include: { professor: true } }, + subject_classtime: true, + subject_examtime: true, + }, + orderBy: orderFilter(query.order), + }, + }, + where: { + id: id, + }, + }); + const filteredLecture = course + ? course.lecture.filter((lecture) => !lecture.deleted) + : []; + return filteredLecture; + } + + public async getReviewsByCourseId( + query: ICourse.ReviewQueryDto, + id: number, + ): Promise { + const review = await this.prisma.review_review.findMany({ + where: { course_id: id }, + include: EReview.Details.include, + take: query.limit, + skip: query.offset, + orderBy: orderFilter(query.order), + }); + return review; + } + + public async getCourses(query: any): Promise { + const DEFAULT_LIMIT = 150; + const DEFAULT_ORDER = ['old_code']; + + const { + department, + type, + level, + group, + keyword, + term, + order, + offset, + limit, + } = query; + const departmentFilter = this.departmentFilter(department); + const typeFilter = this.typeFilter(type); + const groupFilter = this.groupFilter(group); + const keywordFilter = this.keywordFilter(keyword); + const term_filter = this.termFilter(term); + const filterList: object[] = [ + departmentFilter, + typeFilter, + groupFilter, + keywordFilter, + term_filter, + ].filter((filter): filter is object => filter !== null); + + const queryResult = await this.prisma.subject_course.findMany({ + include: ECourse.Details.include, + where: { + AND: filterList, + }, + take: limit ?? DEFAULT_LIMIT, + }); + const levelFilteredResult = this.levelFilter( + queryResult, + level, + ); + + // Apply Ordering and Offset + const orderedResult = applyOrder( + levelFilteredResult, + order ?? DEFAULT_ORDER, + ); + return applyOffset(orderedResult, offset ?? 0); + } + + public departmentFilter(department_names?: string[]): object | null { + if (!department_names) { + return null; + } + if (department_names.includes('ALL')) { + return null; + } else if (department_names.includes('ETC')) { + return { + subject_department: { + code: { + notIn: this.MAJOR_ACRONYMS.filter((x) => + department_names.includes(x), + ), + }, + }, + }; + } else { + return { + subject_department: { + code: { + in: this.MAJOR_ACRONYMS.filter((x) => department_names.includes(x)), + }, + }, + }; + } + } + + public typeFilter(types?: string[]): object | null { + if (!types) { + return null; + } + + if (types.includes('ALL')) { + return null; + } else if (types.includes('ETC')) { + const unselected_types = Object.keys(this.TYPE_ACRONYMS) + .filter((type) => !(type in types)) + .map( + (type) => this.TYPE_ACRONYMS[type as keyof typeof this.TYPE_ACRONYMS], + ); + return { + type_en: { + in: unselected_types, + }, + }; + } else { + return { + type_en: { + in: types.map( + (type) => + this.TYPE_ACRONYMS[type as keyof typeof this.TYPE_ACRONYMS], + ), + }, + }; + } + } + + public termFilter(term?: string[]): object | null { + if (!term) { + return null; + } + + if (term.includes('ALL')) { + return null; + } else { + const current_year = new Date().getFullYear(); + const term_number = term.map((x) => parseInt(x))[0] ?? 0; + + const termFilter: Prisma.subject_courseWhereInput = { + lecture: { + some: { + year: { + gte: current_year - term_number, + }, + }, + }, + }; + return termFilter; + } + } + + public keywordFilter(keyword?: string, isCourse = true): object | null { + if (!keyword) { + return null; + } + + const keyword_trimed = keyword.trim(); + const keyword_space_removed = keyword_trimed.replace(/\s/g, ''); + const title_filter = { + title_no_space: { + contains: keyword_space_removed, + }, + }; + const en_title_filter = { + title_en_no_space: { + contains: keyword_space_removed, + }, + }; + const department_name_filter = { + subject_department: { + name: keyword_trimed, + }, + }; + const department_name_en_filter = { + subject_department: { + name_en: keyword_trimed, + }, + }; + const professors_professor_name_filter = isCourse + ? { + subject_course_professors: { + some: { + professor: { + professor_name: { + contains: keyword_trimed, + }, + }, + }, + }, + } + : { + subject_lecture_professors: { + some: { + professor: { + professor_name: { + contains: keyword_trimed, + }, + }, + }, + }, + }; + const professors_professor_name_en_filter = isCourse + ? { + subject_course_professors: { + some: { + professor: { + professor_name_en: { + contains: keyword_trimed, + }, + }, + }, + }, + } + : { + subject_lecture_professors: { + some: { + professor: { + professor_name_en: { + contains: keyword_trimed, + }, + }, + }, + }, + }; + + const old_code_filter = { + old_code: { + contains: keyword_space_removed, + }, + }; + return { + OR: [ + title_filter, + en_title_filter, + old_code_filter, + department_name_filter, + department_name_en_filter, + professors_professor_name_filter, + professors_professor_name_en_filter, + ], + }; + } + + public groupFilter(group?: string[]): object | null { + if (!group) { + return null; + } + + const filters = []; + const includeHumanity = group.includes('Humanity'); + + if (group.includes('Basic')) { + group.splice(group.indexOf('Basic'), 1); + filters.push({ type_en: { in: ['Basic Required', 'Basic Elective'] } }); + } + if (group.includes('Humanity')) { + group.splice(group.indexOf('Humanity'), 1); + filters.push({ type_en: { startsWith: 'Humanities & Social Elective' } }); + } + if (group.length) { + filters.push({ + type_en: { + in: ['Major Required', 'Major Elective', 'Elective(Graduate)'], + }, + subject_department: { code: { in: group } }, + }); + } + + return { + OR: filters, + }; + } + + public levelFilter( + queryResult: T[], + levels?: string[], + ): T[] { + if (!levels) { + return queryResult; + } + + const levelFilters = levels.map((level) => level[0]); + if (levels.includes('ALL')) { + return queryResult; + } else if (levels.includes('ETC')) { + return queryResult.filter((item) => { + const level = item.old_code.replace(/[^0-9]/g, '')[0]; + return !levelFilters.includes(level); + }); + } else { + return queryResult.filter((item) => { + const level = item.old_code.replace(/[^0-9]/g, '')[0]; + return levelFilters.includes(level); + }); + } + } + + async getUserTakenCourses( + takenLecturesId: number[], + order: string[], + ): Promise { + const orderFilter: { [key: string]: string }[] = []; + order.forEach((orderList) => { + const orderDict: { [key: string]: string } = {}; + let order = 'asc'; + const orderBy = orderList.split('-'); + if (orderBy[0] == '') { + order = 'desc'; + } + orderDict[orderBy[orderBy.length - 1]] = order; + orderFilter.push(orderDict); + }); + return this.prisma.subject_course.findMany({ + where: { + lecture: { + some: { + id: { + in: takenLecturesId, + }, + }, + }, + }, + orderBy: orderFilter, + include: { + subject_department: true, + subject_course_professors: { include: { professor: true } }, + lecture: true, + subject_courseuser: true, + }, + }); + } + + async getCourseAutocomplete({ + keyword, + }: ICourse.AutocompleteQueryDto): Promise { + const candidate = await this.prisma.subject_course.findFirst({ + where: { + OR: [ + { subject_department: { name: { startsWith: keyword } } }, + { subject_department: { name_en: { startsWith: keyword } } }, + { title: { startsWith: keyword } }, + { title_en: { startsWith: keyword } }, + { + subject_course_professors: { + some: { professor: { professor_name: { startsWith: keyword } } }, + }, + }, + { + subject_course_professors: { + some: { + professor: { professor_name_en: { startsWith: keyword } }, + }, + }, + }, + ], + }, + include: ECourse.Extended.include, + orderBy: { old_code: 'asc' }, + }); + return candidate; + } + + async readCourse(userId: number, courseId: number) { + const now = new Date(); + return await this.prisma.subject_courseuser.upsert({ + create: { + latest_read_datetime: now, + user_profile_id: userId, + course_id: courseId, + }, + update: { + latest_read_datetime: now, + }, + where: { + course_id_user_profile_id: { + course_id: courseId, + user_profile_id: userId, + }, + }, + }); + } + + public async isUserSpecificRead(courseId: number, userId: number) { + const courseUser = await this.prisma.subject_courseuser.findFirst({ + select: { + subject_course: { select: { latest_written_datetime: true } }, + latest_read_datetime: true, + }, + where: { + course_id: courseId, + user_profile_id: userId, + }, + }); + + if (!courseUser || !courseUser.subject_course.latest_written_datetime) + return false; + + return ( + courseUser.subject_course.latest_written_datetime > + courseUser.latest_read_datetime + ); + } +} diff --git a/src/prisma/repositories/department.repository.ts b/src/prisma/repositories/department.repository.ts new file mode 100644 index 00000000..76e4701e --- /dev/null +++ b/src/prisma/repositories/department.repository.ts @@ -0,0 +1,131 @@ +import { Injectable } from '@nestjs/common'; +import { session_userprofile, subject_department } from '@prisma/client'; +import { PrismaService } from '../prisma.service'; + +@Injectable() +export class DepartmentRepository { + constructor(private readonly prisma: PrismaService) {} + + async getBasicDepartmentById(id: number): Promise { + return this.prisma.subject_department.findUnique({ + where: { id }, + }); + } + + async getDepartmentOfUser( + user: session_userprofile, + ): Promise { + const departmentId = user.department_id; + if (!departmentId) { + return null; + } + + return this.prisma.subject_department.findUnique({ + where: { id: departmentId }, + }); + } + + async getFavoriteDepartments( + user: session_userprofile, + ): Promise { + const favoriteDepartments = ( + await this.prisma.session_userprofile_favorite_departments.findMany({ + where: { userprofile_id: user.id }, + include: { + department: true, + }, + }) + ).map((favoriteDepartment) => favoriteDepartment.department); + return favoriteDepartments; + } + + async getMajors(user: session_userprofile): Promise { + const majors = ( + await this.prisma.session_userprofile_majors.findMany({ + where: { userprofile_id: user.id }, + include: { + subject_department: true, + }, + }) + ).map((major) => major.subject_department); + return majors; + } + + async getMinors(user: session_userprofile): Promise { + const minors = ( + await this.prisma.session_userprofile_minors.findMany({ + where: { userprofile_id: user.id }, + include: { + subject_department: true, + }, + }) + ).map((minor) => minor.subject_department); + return minors; + } + + async getSpecializedMajors( + user: session_userprofile, + ): Promise { + const specializedMajors = ( + await this.prisma.session_userprofile_specialized_major.findMany({ + where: { userprofile_id: user.id }, + include: { + subject_department: true, + }, + }) + ).map((specializedMajor) => specializedMajor.subject_department); + return specializedMajors; + } + + async getAllDepartmentOptions( + excludedDepartmentCodes: string[], + ): Promise { + return this.prisma.subject_department.findMany({ + where: { + visible: true, + code: { notIn: excludedDepartmentCodes }, + }, + orderBy: { name: 'asc' }, + }); + } + + async getDepartmentCodesOfRecentLectures( + yearThreshold: number, + ): Promise { + const res = await this.prisma.subject_department.findMany({ + where: { + subject_lecture: { + some: { + year: { gte: yearThreshold }, + }, + }, + }, + select: { + code: true, + }, + }); + return res.map((e) => e.code); + } + + async getRelatedDepartments( + user: session_userprofile, + ): Promise { + const departments: subject_department[] = ( + await Promise.all([ + this.getDepartmentOfUser(user), + this.getMajors(user), + this.getMinors(user), + this.getSpecializedMajors(user), + this.getFavoriteDepartments(user), + ]) + ).flatMap( + ( + deps: subject_department | subject_department[] | null, + ): subject_department | subject_department[] => deps ?? [], + ); + const uniqueDepartments: subject_department[] = departments.filter( + (dep, index, deps) => deps.findIndex((d) => d.id === dep.id) === index, + ); + return uniqueDepartments; + } +} diff --git a/src/prisma/repositories/feeds.repository.ts b/src/prisma/repositories/feeds.repository.ts new file mode 100644 index 00000000..74cd6306 --- /dev/null +++ b/src/prisma/repositories/feeds.repository.ts @@ -0,0 +1,207 @@ +import { Injectable } from '@nestjs/common'; +import { + main_ratedailyuserfeed, + review_humanitybestreview, + review_majorbestreview, + subject_department, +} from '@prisma/client'; +import { EFeed } from 'src/common/entities/EFeed'; +import { + FeedRateMinDays, + FeedVisibleRate, +} from 'src/common/interfaces/constants/feed'; +import { PrismaService } from '../prisma.service'; + +@Injectable() +export class FeedsRepository { + constructor(private readonly prisma: PrismaService) {} + + public async getFamousHumanityReview(date: Date) { + return await this.prisma.main_famoushumanityreviewdailyfeed.findFirst({ + where: { + date, + }, + include: EFeed.FamousHumanityReviewDetails.include, + }); + } + + public async createFamousHumanityReview( + date: Date, + humanityBestReviews: review_humanitybestreview, + ) { + return await this.prisma.main_famoushumanityreviewdailyfeed.create({ + include: EFeed.FamousHumanityReviewDetails.include, + data: { + date, + priority: Math.random(), + main_famoushumanityreviewdailyfeed_reviews: { + createMany: { + data: humanityBestReviews, + }, + }, + visible: Math.random() < FeedVisibleRate.FamousHumanityReview, + }, + }); + } + + public async getRankedReview(date: Date) { + return await this.prisma.main_rankedreviewdailyfeed.findFirst({ + where: { + date, + }, + }); + } + + public async createRankedReview(date: Date) { + return await this.prisma.main_rankedreviewdailyfeed.create({ + data: { + date, + priority: Math.random(), + visible: Math.random() < FeedVisibleRate.RankedReview, + }, + }); + } + + public async getFamousMajorReview( + date: Date, + department: subject_department, + ) { + return await this.prisma.main_famousmajorreviewdailyfeed.findFirst({ + include: EFeed.FamousMajorReviewDetails.include, + where: { + date, + subject_department: department, + }, + }); + } + + public async createFamousMajorReview( + date: Date, + department: subject_department, + majorBestReviews: review_majorbestreview[], + departmentNum: number = 1, + ) { + return await this.prisma.main_famousmajorreviewdailyfeed.create({ + include: EFeed.FamousMajorReviewDetails.include, + data: { + date, + priority: Math.random(), + visible: + Math.random() < + FeedVisibleRate.FamousMajorReview / departmentNum ** 0.7, + department_id: department.id, + main_famousmajorreviewdailyfeed_reviews: { + createMany: { data: majorBestReviews }, + }, + }, + }); + } + + public async getReviewWrite(date: Date, userId: number) { + return await this.prisma.main_reviewwritedailyuserfeed.findFirst({ + include: EFeed.ReviewWriteDetails.include, + where: { + date, + user_id: userId, + }, + }); + } + + public async createReviewWrite( + date: Date, + userId: number, + takenLectureId: number, + ) { + return await this.prisma.main_reviewwritedailyuserfeed.create({ + include: EFeed.ReviewWriteDetails.include, + data: { + date, + priority: Math.random(), + visible: Math.random() < FeedVisibleRate.ReviewWrite, + user_id: userId, + lecture_id: takenLectureId, + }, + }); + } + + public async getRelatedCourse(date: Date, userId: number) { + return await this.prisma.main_relatedcoursedailyuserfeed.findFirst({ + include: EFeed.RelatedCourseDetails.include, + where: { + date, + user_id: userId, + }, + }); + } + + public async createRelatedCourse( + date: Date, + userId: number, + takenLectureCourseId: number, + ) { + return await this.prisma.main_relatedcoursedailyuserfeed.create({ + include: EFeed.RelatedCourseDetails.include, + data: { + date, + priority: Math.random(), + visible: Math.random() < FeedVisibleRate.RelatedCourse, + course_id: takenLectureCourseId, + user_id: userId, + }, + }); + } + + public async getOrCreateRate( + date: Date, + userId: number, + ): Promise { + let feed = await this.prisma.main_ratedailyuserfeed.findFirst({ + where: { + date, + user_id: userId, + }, + }); + + if (!feed) { + if ( + await this.prisma.support_rate.findFirst({ + where: { + user_id: userId, + year: date.getFullYear(), + }, + }) + ) { + return null; + } + + const beforeDate = new Date(date); + beforeDate.setDate(date.getDate() - FeedRateMinDays); + const afterDate = new Date(date); + afterDate.setDate(date.getDate() + FeedRateMinDays); + const weekFeeds = await this.prisma.main_ratedailyuserfeed.findMany({ + where: { + date: { + gt: beforeDate, + lt: afterDate, + }, + visible: true, + user_id: userId, + }, + }); + if (weekFeeds.length > 0) { + return null; + } + + feed = await this.prisma.main_ratedailyuserfeed.create({ + data: { + date, + priority: Math.random(), + visible: Math.random() < FeedVisibleRate.Rate, + user_id: userId, + }, + }); + } + + return feed; + } +} diff --git a/src/prisma/repositories/lecture.repository.ts b/src/prisma/repositories/lecture.repository.ts new file mode 100644 index 00000000..628be66e --- /dev/null +++ b/src/prisma/repositories/lecture.repository.ts @@ -0,0 +1,351 @@ +import { Injectable } from '@nestjs/common'; +import { Prisma, session_userprofile } from '@prisma/client'; +import { ELecture } from 'src/common/entities/ELecture'; +import { ILecture } from 'src/common/interfaces/ILecture'; +import { applyOffset, applyOrder } from 'src/common/utils/search.utils'; +import { groupBy } from '../../common/utils/method.utils'; +import { PrismaService } from '../prisma.service'; +import { CourseRepository } from './course.repository'; +import { FilterType } from '@src/common/types/types'; +import SubjectClasstimeFilter = FilterType.SubjectClasstimeFilter; +import Details = ELecture.Details; + +@Injectable() +export class LectureRepository { + constructor( + private readonly prisma: PrismaService, + private readonly courseRepository: CourseRepository, + ) {} + + async getLectureById(id: number): Promise { + return await this.prisma.subject_lecture.findUniqueOrThrow({ + include: ELecture.Details.include, + where: { + id: id, + }, + }); + } + + async getLectureBasicById(id: number): Promise { + return await this.prisma.subject_lecture.findUniqueOrThrow({ + where: { + id: id, + }, + }); + } + + async getLectureByIds(ids: number[]): Promise { + return await this.prisma.subject_lecture.findMany({ + include: ELecture.Details.include, + where: { + id: { + in: ids, + }, + }, + }); + } + + async filterByRequest(query: ILecture.QueryDto): Promise { + const DEFAULT_LIMIT = 300; + const DEFAULT_ORDER = ['year', 'semester', 'old_code', 'class_no']; + const researchTypes = [ + 'Individual Study', + 'Thesis Study(Undergraduate)', + 'Thesis Research(MA/phD)', + ]; + + const semesterFilter = this.semesterFilter(query?.year, query?.semester); + const timeFilter = this.timeFilter(query?.day, query?.begin, query?.end); + const departmentFilter = this.courseRepository.departmentFilter( + query?.department, + ); + const typeFilter = this.courseRepository.typeFilter(query?.type); + const groupFilter = this.courseRepository.groupFilter(query?.group); + const keywordFilter = this.courseRepository.keywordFilter( + query?.keyword, + false, + ); + const defaultFilter = { + AND: [ + { + deleted: false, + }, + { + type_en: { + notIn: researchTypes, + }, + }, + ], + }; + + const filters: object[] = [ + semesterFilter, + timeFilter, + departmentFilter, + typeFilter, + groupFilter, + keywordFilter, + defaultFilter, + ].filter((filter): filter is object => filter !== null); + const queryResult = await this.prisma.subject_lecture.findMany({ + include: { + subject_department: true, + subject_lecture_professors: { include: { professor: true } }, + subject_classtime: true, + subject_examtime: true, + }, + where: { + AND: filters, + }, + take: query.limit ?? DEFAULT_LIMIT, + }); + const levelFilteredResult = + this.courseRepository.levelFilter( + queryResult, + query?.level, + ); + + const orderedQuery = applyOrder( + levelFilteredResult, + (query.order ?? DEFAULT_ORDER) as (keyof ELecture.Details)[], + ); + return applyOffset(orderedQuery, query.offset ?? 0); + } + + async findReviewWritableLectures( + user: session_userprofile, + date?: Date, + ): Promise { + type Semester = { semester: number; year: number }; + const currDate = date ?? new Date(); + const notWritableSemesters = await this.prisma.subject_semester.findMany({ + where: { + OR: [ + { + courseAddDropPeriodEnd: { + gte: currDate, + }, + }, + { + beginning: { + gte: currDate, + }, + }, + ], + }, + }); + const notWritableYearAndSemester = groupBy( + notWritableSemesters.map((semester) => { + return { + semester: semester.semester, + year: semester.year, + }; + }), + (subject_semester) => subject_semester.year, + ); + + const notWritableYearAndSemesterMap: Record< + string, + Record + > = {}; + for (const [key, value] of Object.entries(notWritableYearAndSemester)) { + if (value) { + notWritableYearAndSemesterMap[key] = groupBy( + value, + (s) => s.year, + ); + } + } + + const takenLectures = await this.getTakenLectures(user); + const reviewWritableLectures = takenLectures.filter((lecture) => { + return notWritableYearAndSemesterMap[lecture.year] ?? [lecture.semester] + ? true + : false; + }); + + // const lectures = await this.prisma.subject_lecture.findMany({ + // where: { + // AND: notWritableYearAndSemester + // } + // }) + + return reviewWritableLectures; + } + + getResearchLectureQuery(): Prisma.subject_lectureWhereInput { + return { + type_en: { + in: [ + 'Individual Study', + 'Thesis Study(Undergraduate)', + 'Thesis Research(MA/phD)', + ], + }, + }; + } + + async getTakenLectures( + user: session_userprofile, + ): Promise { + const lectures = ( + await this.prisma.session_userprofile_taken_lectures.findMany({ + where: { + userprofile_id: user.id, + }, + include: { + lecture: { + include: Details.include, + }, + }, + }) + ).map((takenLecture) => takenLecture.lecture); + + return lectures; + } + + public semesterFilter(year?: number, semester?: number): object | null { + if (!year && !semester) { + return null; + } else if (!year) { + return { + semester: { + in: semester, + }, + }; + } else if (!semester) { + return { + years: { + equals: semester, + }, + }; + } else { + return { + AND: [ + { + year: { + equals: year, + }, + }, + { + semester: { + equals: semester, + }, + }, + ], + }; + } + } + + public timeFilter(day?: number, begin?: number, end?: number): object | null { + const datetimeBegin = + begin !== undefined && begin !== null + ? this.datetimeConverter(begin) + : undefined; + const datetimeEnd = + end !== undefined && end !== null + ? this.datetimeConverter(end) + : undefined; + + const result: any = {}; + + if (day !== undefined && day !== null) { + result.day = day; + } + if (datetimeBegin) { + result.begin = { gte: datetimeBegin }; + } + if (datetimeEnd) { + result.end = { lte: datetimeEnd }; + } + + return Object.keys(result).length > 0 + ? { subject_classtime: { some: result } } + : null; + } + + public datetimeConverter(time: number): Date { + const hour = Math.floor(time / 2) + 8; + const minute = (time % 2) * 30; + + // 1970-01-01 날짜로 고정된 Date 객체 생성 + const date = new Date('1970-01-01T00:00:00.000Z'); + date.setUTCHours(hour, minute, 0, 0); // UTC 시간을 설정 + + return date; + } + + async getLectureAutocomplete({ + year, + semester, + keyword, + }: ILecture.AutocompleteQueryDto): Promise { + const candidate = await this.prisma.subject_lecture.findFirst({ + where: { + year, + semester, + OR: [ + { subject_department: { name: { startsWith: keyword } } }, + { subject_department: { name_en: { startsWith: keyword } } }, + { title: { startsWith: keyword } }, + { title_en: { startsWith: keyword } }, + { + subject_lecture_professors: { + some: { professor: { professor_name: { startsWith: keyword } } }, + }, + }, + { + subject_lecture_professors: { + some: { + professor: { professor_name_en: { startsWith: keyword } }, + }, + }, + }, + ], + }, + include: ELecture.Extended.include, + }); + return candidate; + } + + async getUserLecturesByYearSemester( + userId: number, + year: number, + semester: number, + ): Promise { + const lectures = + await this.prisma.session_userprofile_taken_lectures.findMany({ + where: { + userprofile_id: userId, + lecture: { + year: year, + semester: semester, + deleted: false, + }, + }, + include: { + lecture: ELecture.UserTaken, + }, + }); + + return lectures.map((result) => result.lecture); + } + + async getLectureDetailsForTimetable(lectureIds: number[]) { + return await this.prisma.subject_lecture.findMany({ + where: { + id: { + in: lectureIds, + }, + }, + include: { + subject_lecture_professors: { + include: { + professor: true, + }, + }, + subject_classtime: true, // Include classtime details if needed + }, + }); + } +} diff --git a/src/prisma/repositories/notices.repository.ts b/src/prisma/repositories/notices.repository.ts new file mode 100644 index 00000000..5a49b9e8 --- /dev/null +++ b/src/prisma/repositories/notices.repository.ts @@ -0,0 +1,13 @@ +import { Injectable } from '@nestjs/common'; +import { PrismaService } from '../prisma.service'; + +@Injectable() +export class NoticesRepository { + constructor(private readonly prisma: PrismaService) {} + + public async getNotices(date: Date) { + return await this.prisma.support_notice.findMany({ + where: { start_time: { lte: date }, end_time: { gte: date } }, + }); + } +} diff --git a/src/prisma/repositories/planner.repository.ts b/src/prisma/repositories/planner.repository.ts new file mode 100644 index 00000000..8d37dd1e --- /dev/null +++ b/src/prisma/repositories/planner.repository.ts @@ -0,0 +1,589 @@ +import { BadRequestException, Injectable } from '@nestjs/common'; +import { Prisma, session_userprofile } from '@prisma/client'; +import { IPlanner } from 'src/common/interfaces/IPlanner'; +import { orderFilter } from 'src/common/utils/search.utils'; +import { EPlanners } from '../../common/entities/EPlanners'; +import { PlannerItemType } from '../../common/interfaces/constants/planner'; +import { PrismaService } from '../prisma.service'; +import CreateInput = EPlanners.EItems.Arbitrary.CreateInput; +import { Transactional } from '@nestjs-cls/transactional'; + +@Injectable() +export class PlannerRepository { + constructor(private readonly prisma: PrismaService) {} + + public async getPlannerByUser( + query: IPlanner.QueryDto, + user: session_userprofile, + ): Promise { + return await this.prisma.planner_planner.findMany({ + ...EPlanners.Details, + where: { + user_id: user.id, + }, + orderBy: orderFilter(query.order), + skip: query.offset, + take: query.limit, + }); + } + + public async getBasicPlannerById( + id: number, + ): Promise { + return await this.prisma.planner_planner.findUnique({ + where: { + id: id, + }, + }); + } + + public async createPlanner( + body: IPlanner.CreateBodyDto, + arrange_order: number, + user: session_userprofile, + ): Promise { + return await this.prisma.planner_planner.create({ + ...EPlanners.Details, + data: { + session_userprofile: { + connect: { + id: user.id, + }, + }, + graduation_generaltrack: { + connect: { + id: body.general_track, + }, + }, + graduation_majortrack: { + connect: { + id: body.major_track, + }, + }, + planner_planner_additional_tracks: { + create: body?.additional_tracks?.map((t) => { + return { + graduation_additionaltrack: { + connect: { + id: t, + }, + }, + }; + }), + }, + start_year: body.start_year, + end_year: body.end_year, + arrange_order: arrange_order, + }, + }); + } + + public async getPlannerById( + user: session_userprofile, + id: number, + ): Promise { + return this.prisma.planner_planner.findFirst({ + ...EPlanners.Details, + where: { + user_id: user.id, + id: id, + }, + }); + } + + public async updateOrder( + plannerId: number, + order: number, + ): Promise { + return await this.prisma.planner_planner.update({ + ...EPlanners.Details, + where: { + id: plannerId, + }, + data: { + arrange_order: order, + }, + }); + } + + public async incrementOrders( + plannerIds: number[], + from: number, + to: number, + ): Promise { + await this.prisma.planner_planner.updateMany({ + where: { + id: { + in: plannerIds, + }, + arrange_order: { + gte: from, + lte: to, + }, + }, + data: { + arrange_order: { + increment: 1, + }, + }, + }); + } + + public async decrementOrders( + plannerIds: number[], + from: number, + to: number, + ): Promise { + await this.prisma.planner_planner.updateMany({ + where: { + id: { + in: plannerIds, + }, + arrange_order: { + gte: from, + lte: to, + }, + }, + data: { + arrange_order: { + decrement: 1, + }, + }, + }); + } + + public async getRelatedPlanner( + user: session_userprofile, + ): Promise { + return await this.prisma.planner_planner.findMany({ + where: { + user_id: user.id, + }, + orderBy: { + arrange_order: 'asc', + }, + }); + } + + public async getTakenPlannerItemByIds( + plannerItemIds: number[], + ): Promise { + const plannerItems = await this.prisma.planner_takenplanneritem.findMany({ + include: EPlanners.EItems.Taken.Details.include, + where: { + id: { + in: plannerItemIds, + }, + }, + }); + return plannerItems.length > 0 ? plannerItems : null; + } + + public async createTakenPlannerItem( + plannerId: number, + lectures: { + lectureId: number; + isExcluded: boolean; + }[], + ): Promise { + const datas = lectures.map((lecture) => { + return { + planner_id: plannerId, + is_excluded: lecture.isExcluded, + lecture_id: lecture.lectureId, + }; + }); + await this.prisma.planner_takenplanneritem.createMany({ + data: datas, + }); + return this.prisma.planner_takenplanneritem.findMany({ + where: { + planner_id: plannerId, + lecture_id: { + in: lectures.map((lecture) => lecture.lectureId), + }, + }, + }); + } + public async getFuturePlannerItemById( + futureItemIds: number[], + ): Promise { + const futurePlannerItems = + await this.prisma.planner_futureplanneritem.findMany({ + include: EPlanners.EItems.Future.Extended.include, + where: { + id: { + in: futureItemIds, + }, + }, + }); + return futurePlannerItems.length > 0 ? futurePlannerItems : null; + } + + @Transactional() + public async createFuturePlannerItem( + plannerId: number, + targetItems: EPlanners.EItems.Future.Extended[], + ) { + const createDatas = targetItems.map((target_item) => { + return { + planner_id: plannerId, + course_id: target_item.course_id, + is_excluded: target_item.is_excluded, + year: target_item.year, + semester: target_item.semester, + }; + }); + return await this.prisma.planner_futureplanneritem.createMany({ + data: createDatas, + }); + } + + @Transactional() + public async deleteFuturePlannerItem( + target_item: EPlanners.EItems.Future.Extended, + ) { + return await this.prisma.planner_futureplanneritem.delete({ + where: { + id: target_item.id, + }, + }); + } + + public async getArbitraryPlannerItemById( + arbitraryItemIds: number[], + ): Promise { + const arbitraryItems = + await this.prisma.planner_arbitraryplanneritem.findMany({ + include: EPlanners.EItems.Arbitrary.Extended.include, + where: { + id: { + in: arbitraryItemIds, + }, + }, + }); + return arbitraryItems.length > 0 ? arbitraryItems : null; + } + + public async createArbitraryPlannerItem( + plannerId: number, + target_items: CreateInput[], + ): Promise; + + public async createArbitraryPlannerItem( + plannerId: number, + target_items: CreateInput, + ): Promise; + + @Transactional() + public async createArbitraryPlannerItem< + T extends CreateInput | CreateInput[], + >(plannerId: number, target_items: T): Promise { + if (Array.isArray(target_items)) { + const createDatas = target_items.map((targetItem: CreateInput) => ({ + planner_id: plannerId, + department_id: targetItem.department_id, + is_excluded: targetItem.is_excluded, + year: targetItem.year, + semester: targetItem.semester, + type: targetItem.type, + type_en: targetItem.type_en, + credit: targetItem.credit, + credit_au: targetItem.credit_au, + })); + + return await this.prisma.planner_arbitraryplanneritem.createMany({ + data: createDatas, + }); + } else { + const targetItem = target_items as CreateInput; + return await this.prisma.planner_arbitraryplanneritem.create({ + include: EPlanners.EItems.Arbitrary.Extended.include, + data: { + planner_id: plannerId, + department_id: targetItem.department_id, + is_excluded: targetItem.is_excluded, + year: targetItem.year, + semester: targetItem.semester, + type: targetItem.type, + type_en: targetItem.type_en, + credit: targetItem.credit, + credit_au: targetItem.credit_au, + }, + }); + } + } + + public async deleteArbitraryPlannerItem( + target_item: EPlanners.EItems.Arbitrary.Extended, + ) { + return await this.prisma.planner_arbitraryplanneritem.delete({ + where: { + id: target_item.id, + }, + }); + } + + public async checkPlannerExists(plannerId: number): Promise { + const planner = await this.prisma.planner_planner.findFirst({ + where: { + id: plannerId, + }, + }); + return planner ? true : false; + } + + public async createPlannerItem( + plannerId: number, + year: number, + semester: number, + courseId: number, + ): Promise { + return await this.prisma.planner_futureplanneritem.create({ + ...EPlanners.EItems.Future.Extended, + data: { + year: year, + semester: semester, + planner_planner: { + connect: { + id: plannerId, + }, + }, + subject_course: { + connect: { + id: courseId, + }, + }, + is_excluded: false, + }, + }); + } + + @Transactional() + async updatePlannerItem( + item_type: string, + item: number, + updatedFields: Pick, + ): Promise< + | EPlanners.EItems.Taken.Details + | EPlanners.EItems.Future.Extended + | EPlanners.EItems.Arbitrary.Extended + > { + if (item_type === PlannerItemType.Taken) { + return this.prisma.planner_takenplanneritem.update({ + where: { + id: item, + }, + data: { + is_excluded: updatedFields.is_excluded, + }, + include: EPlanners.EItems.Taken.Details.include, + }); + } else if (item_type === PlannerItemType.Future) { + return this.prisma.planner_futureplanneritem.update({ + where: { + id: item, + }, + data: { + is_excluded: updatedFields.is_excluded, + semester: updatedFields.semester, + }, + include: EPlanners.EItems.Future.Extended.include, + }); + } else if (item_type === PlannerItemType.Arbitrary) { + return this.prisma.planner_arbitraryplanneritem.update({ + where: { + id: item, + }, + data: { + is_excluded: updatedFields.is_excluded, + semester: updatedFields.semester, + }, + include: EPlanners.EItems.Arbitrary.Extended.include, + }); + } else { + throw new BadRequestException('Invalid Planner Item Type'); + } + } + + async getTakenPlannerItemByLecture( + plannerId: number, + lectureId: number, + ): Promise { + return await this.prisma.planner_takenplanneritem.findFirst({ + where: { + planner_id: plannerId, + lecture_id: lectureId, + }, + }); + } + + async getTakenPlannerItemByLectures( + plannerId: number, + lectureIds: number[], + ): Promise { + return await this.prisma.planner_takenplanneritem.findMany({ + where: { + planner_id: plannerId, + lecture_id: { + in: lectureIds, + }, + }, + }); + } + + @Transactional() + async updatePlanner( + plannerId: number, + updateFields: { + additional_track_ids: number[]; + start_year: number; + major_track_id: number; + general_track_id: number; + end_year: number; + }, + ): Promise { + const existedAdditonalTracks = + await this.prisma.planner_planner_additional_tracks.findMany({ + where: { + planner_id: plannerId, + }, + }); + + const disconnectAdditionalTracks = existedAdditonalTracks.filter( + (track) => + !updateFields.additional_track_ids.includes(track.additionaltrack_id), + ); + const connectAdditionalTrackIds = updateFields.additional_track_ids.filter( + (track) => + !existedAdditonalTracks + .map((t) => t.additionaltrack_id) + .includes(track), + ); + + await this.prisma.planner_planner_additional_tracks.deleteMany({ + where: { + planner_id: plannerId, + additionaltrack_id: { + in: disconnectAdditionalTracks.map( + (track) => track.additionaltrack_id, + ), + }, + }, + }); + + await this.prisma.planner_planner_additional_tracks.createMany({ + data: connectAdditionalTrackIds.map((track) => { + return { + planner_id: plannerId, + additionaltrack_id: track, + }; + }), + }); + + return this.prisma.planner_planner.update({ + where: { + id: plannerId, + }, + data: { + start_year: updateFields.start_year, + end_year: updateFields.end_year, + graduation_majortrack: { + connect: { + id: updateFields.major_track_id, + }, + }, + graduation_generaltrack: { + connect: { + id: updateFields.general_track_id, + }, + }, + }, + include: EPlanners.Details.include, + }); + } + + @Transactional() + async deleteFuturePlannerItemsWithWhere( + plannerId: number, + startYear: number, + endYear: number, + ) { + return await this.prisma.planner_futureplanneritem.deleteMany({ + where: { + year: { + lte: startYear, + gte: endYear, + }, + planner_id: plannerId, + }, + }); + } + + @Transactional() + async deleteArbitraryPlannerItemsWithWhere( + plannerId: number, + startYear: number, + endYear: number, + ) { + return await this.prisma.planner_arbitraryplanneritem.deleteMany({ + where: { + year: { + lte: startYear, + gte: endYear, + }, + planner_id: plannerId, + }, + }); + } + + @Transactional() + async deleteTakenPlannerItemsWithWhere( + plannerId: number, + where: Prisma.XOR< + Prisma.Subject_lectureRelationFilter, + Prisma.subject_lectureWhereInput + >, + ) { + return this.prisma.planner_takenplanneritem.deleteMany({ + where: { + planner_id: plannerId, + subject_lecture: { + ...where, + }, + }, + }); + } + + @Transactional() + async deletePlanner(plannerId: number) { + const planner = await this.prisma.planner_planner.findUniqueOrThrow({ + where: { + id: plannerId, + }, + }); + const userId = planner.user_id; + const planners = await this.prisma.planner_planner.findMany({ + where: { + user_id: userId, + }, + orderBy: { + arrange_order: 'asc', + }, + }); + const deletedPlanner = await this.prisma.planner_planner.delete({ + where: { + id: plannerId, + }, + }); + const needToUpdatePlanners = planners.filter( + (p) => p.arrange_order > deletedPlanner.arrange_order, + ); + await this.decrementOrders( + needToUpdatePlanners.map((p) => p.id), + planner.arrange_order + 1, + planners.length, + ); + return deletedPlanner; + } +} diff --git a/src/prisma/repositories/rates.repository.ts b/src/prisma/repositories/rates.repository.ts new file mode 100644 index 00000000..b03a3221 --- /dev/null +++ b/src/prisma/repositories/rates.repository.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@nestjs/common'; +import { PrismaService } from '../prisma.service'; + +@Injectable() +export class RateRepository { + constructor(private readonly prisma: PrismaService) {} + + async getRate(userId: number, year: number, version: string) { + return this.prisma.support_rate.findFirst({ + where: { + user_id: userId, + year, + version, + }, + }); + } + + async createRate( + userId: number, + score: number, + year: number, + version: string, + ) { + return this.prisma.support_rate.create({ + data: { + user_id: userId, + year, + score, + version, + }, + }); + } +} diff --git a/src/prisma/repositories/review.repository.ts b/src/prisma/repositories/review.repository.ts new file mode 100644 index 00000000..06425d04 --- /dev/null +++ b/src/prisma/repositories/review.repository.ts @@ -0,0 +1,401 @@ +import { Injectable } from '@nestjs/common'; +import { + review_humanitybestreview, + review_majorbestreview, + session_userprofile, + subject_department, +} from '@prisma/client'; +import { ELecture } from 'src/common/entities/ELecture'; +import { EReview } from 'src/common/entities/EReview'; +import { orderFilter } from 'src/common/utils/search.utils'; +import { PrismaService } from '../prisma.service'; + +@Injectable() +export class ReviewsRepository { + constructor(private readonly prisma: PrismaService) {} + + async findReviewByUser( + user: session_userprofile, + ): Promise { + const reviews = await this.prisma.review_review.findMany({ + where: { writer_id: user.id }, + include: { + course: { + include: { + subject_department: true, + subject_course_professors: { include: { professor: true } }, + lecture: true, + subject_courseuser: true, + }, + }, + lecture: { + include: { + subject_department: true, + subject_lecture_professors: { include: { professor: true } }, + subject_classtime: true, + subject_examtime: true, + }, + }, + review_reviewvote: true, + }, + }); + return reviews; + } + + async getReviewById(reviewId: number): Promise { + return await this.prisma.review_review.findUnique({ + where: { id: reviewId }, + include: { + course: { + include: { + subject_department: true, + subject_course_professors: { include: { professor: true } }, + lecture: true, + subject_courseuser: true, + }, + }, + lecture: { + include: { + subject_department: true, + subject_lecture_professors: { include: { professor: true } }, + subject_classtime: true, + subject_examtime: true, + }, + }, + review_reviewvote: true, + }, + }); + } + + async getReviewsByIds(reviewIds: number[]) { + return this.prisma.review_review.findMany({ + where: { + id: { + in: reviewIds, + }, + }, + include: EReview.Details.include, + }); + } + + public async getReviews( + order: string[], + offset: number, + limit: number, + lectureYear?: number, + lectureSemester?: number, + ): Promise { + let lectureFilter: object = {}; + const orderFilter: { [key: string]: string }[] = []; + if (lectureYear) { + lectureFilter = { ...lectureFilter, year: lectureYear }; + } + if (lectureSemester) { + lectureFilter = { ...lectureFilter, semester: lectureSemester }; + } + order.forEach((orderList) => { + const orderDict: { [key: string]: string } = {}; + let order = 'asc'; + const orderBy = orderList.split('-'); + if (orderBy[0] == '') { + order = 'desc'; + } + orderDict[orderBy[orderBy.length - 1]] = order; + orderFilter.push(orderDict); + }); + const reviews = await this.prisma.review_review.findMany({ + where: { + lecture: lectureFilter, + }, + include: { + course: { + include: { + subject_department: true, + subject_course_professors: { include: { professor: true } }, + lecture: true, + subject_courseuser: true, + }, + }, + lecture: { + include: { + subject_department: true, + subject_lecture_professors: { include: { professor: true } }, + subject_classtime: true, + subject_examtime: true, + }, + }, + review_reviewvote: true, + }, + skip: offset, + take: limit, + orderBy: orderFilter, + distinct: [ + 'id', + 'course_id', + 'lecture_id', + 'content', + 'grade', + 'load', + 'speech', + 'writer_id', + 'writer_label', + 'updated_datetime', + 'like', + 'is_deleted', + 'written_datetime', + ], + }); + + return reviews; + } + + async getReviewsOfLecture( + id: number, + order: string[], + offset: number, + limit: number, + ): Promise { + return await this.prisma.review_review.findMany({ + where: { lecture_id: id }, + include: EReview.Details.include, + orderBy: orderFilter(order), + skip: offset, + take: limit, + }); + } + + public async getRelatedReviewsOfLecture( + order: string[], + offset: number, + limit: number, + lecture: ELecture.Details, + ): Promise { + return await this.prisma.review_review.findMany({ + ...EReview.Details, + where: { + lecture: { + course_id: lecture.course_id, + subject_lecture_professors: { + some: { + professor_id: { + in: lecture.subject_lecture_professors.map( + (professor) => professor.professor_id, + ), + }, + }, + }, + }, + }, + skip: offset, + take: limit, + orderBy: orderFilter(order), + distinct: [ + 'id', + 'course_id', + 'lecture_id', + 'content', + 'grade', + 'load', + 'speech', + 'writer_id', + 'writer_label', + 'updated_datetime', + 'like', + 'is_deleted', + 'written_datetime', + ], + }); + } + + async isLiked(reviewId: number, userId: number): Promise { + return !!(await this.prisma.review_reviewvote.findUnique({ + where: { + review_id_userprofile_id: { + review_id: reviewId, + userprofile_id: userId, + }, + }, + })); + } + + public async getLikedReviews( + userId: number, + order: string[], + offset: number, + limit: number, + ): Promise { + const likedReviews = await this.prisma.review_review.findMany({ + where: { + review_reviewvote: { + some: { + userprofile_id: userId, + }, + }, + }, + include: EReview.Details.include, + take: limit, + skip: offset, + orderBy: orderFilter(order), + }); + + // const likedReviews = this.getReviewsByIds(likedReviewIds); + return likedReviews; + } + + public async getReviewsCount( + lectureYear?: number, + lectureSemester?: number, + ): Promise { + let lectureFilter: object = {}; + if (lectureYear) { + lectureFilter = { ...lectureFilter, year: lectureYear }; + } + if (lectureSemester) { + lectureFilter = { ...lectureFilter, semester: lectureSemester }; + } + const reviewsCount = await this.prisma.review_review.count({ + where: { + lecture: lectureFilter, + }, + }); + return reviewsCount; + } + + async upsertReview( + courseId: number, + lectureId: number, + content: string, + grade: number, + load: number, + speech: number, + writerId: number, + ): Promise { + return await this.prisma.review_review.upsert({ + where: { + writer_id_lecture_id: { writer_id: writerId, lecture_id: lectureId }, + }, + update: {}, + create: { + course: { connect: { id: courseId } }, + lecture: { connect: { id: lectureId } }, + content: content, + grade: grade, + load: load, + speech: speech, + writer: { connect: { id: writerId } }, + writer_label: '무학과 넙죽이', + written_datetime: new Date(), + updated_datetime: new Date(), + }, + include: { + course: { + include: { + subject_department: true, + subject_course_professors: { include: { professor: true } }, + lecture: true, + subject_courseuser: true, + }, + }, + lecture: { + include: { + subject_department: true, + subject_lecture_professors: { include: { professor: true } }, + subject_classtime: true, + subject_examtime: true, + }, + }, + review_reviewvote: true, + }, + }); + } + + async updateReview( + reviewId: number, + content?: string, + grade?: number, + load?: number, + speech?: number, + ): Promise { + return await this.prisma.review_review.update({ + where: { + id: reviewId, + }, + data: { + content, + grade, + load, + speech, + }, + include: { + course: { + include: { + subject_department: true, + subject_course_professors: { include: { professor: true } }, + lecture: true, + subject_courseuser: true, + }, + }, + lecture: { + include: { + subject_department: true, + subject_lecture_professors: { include: { professor: true } }, + subject_classtime: true, + subject_examtime: true, + }, + }, + review_reviewvote: true, + }, + }); + } + + public async getTopLikedReviews(n: number) { + return await this.prisma.review_review.findMany({ + include: EReview.Details.include, + orderBy: { + like: 'desc', + }, + take: n, + }); + } + + public async getRandomNHumanityBestReviews( + n: number, + ): Promise { + // Prisma does not support RAND() in ORDER BY. + return await this.prisma.$queryRaw` + SELECT * FROM review_humanitybestreview + ORDER BY RAND() + LIMIT ${n}`; + } + + public async getRandomNMajorBestReviews( + n: number, + department: subject_department, + ): Promise { + // Prisma does not support RAND() in ORDER BY. + return await this.prisma.$queryRaw` + SELECT mbr.* FROM review_majorbestreview mbr + INNER JOIN review_review r ON r.id = mbr.review_id + INNER JOIN subject_lecture l ON l.id = r.lecture_id + WHERE l.department_id = ${department.id} + ORDER BY RAND() + LIMIT ${n}`; + } + + async upsertReviewVote(reviewId: number, userId: number) { + await this.prisma.review_reviewvote.upsert({ + where: { + review_id_userprofile_id: { + review_id: reviewId, + userprofile_id: userId, + }, + }, + update: {}, + create: { + review: { connect: { id: reviewId } }, + userprofile: { connect: { id: userId } }, + }, + }); + return null; + } +} diff --git a/src/prisma/repositories/semester.repository.ts b/src/prisma/repositories/semester.repository.ts new file mode 100644 index 00000000..5307c362 --- /dev/null +++ b/src/prisma/repositories/semester.repository.ts @@ -0,0 +1,63 @@ +import { Injectable } from '@nestjs/common'; +import { Prisma, subject_semester } from '@prisma/client'; +import { PrismaService } from '../prisma.service'; + +@Injectable() +export class SemesterRepository { + constructor(private readonly prisma: PrismaService) {} + + async existsSemester(year: number, semester: number): Promise { + const existsSemester: boolean = await this.prisma.subject_semester + .findFirstOrThrow({ + where: { + year: year, + semester: semester, + }, + }) + .catch((e) => false) + .then((s) => true); + return existsSemester; + } + + async getSemesters(paginationAndSoring: { + orderBy?: Prisma.subject_semesterOrderByWithRelationInput[]; + skip?: number; + take?: number; + }) { + const orderBy = paginationAndSoring.orderBy; + const skip = paginationAndSoring.skip; + const take = paginationAndSoring.take; + + return await this.prisma.subject_semester.findMany({ + orderBy: orderBy, + skip: skip, + take: take, + }); + } + + async getNotWritableSemester(): Promise { + const now = new Date(); + return await this.prisma.subject_semester.findFirst({ + where: { + OR: [ + { courseAddDropPeriodEnd: { gte: now } }, + { beginning: { gte: now } }, + ], + }, + }); + } + + async findSemester( + year: number, + semester: number, + ): Promise { + return await this.prisma.subject_semester.findUnique({ + where: { + year_semester: { + year: year, + semester: semester, + }, + }, + }); + } +} diff --git a/src/prisma/repositories/timetable.repository.ts b/src/prisma/repositories/timetable.repository.ts new file mode 100644 index 00000000..339a855a --- /dev/null +++ b/src/prisma/repositories/timetable.repository.ts @@ -0,0 +1,162 @@ +import { Injectable } from '@nestjs/common'; +import { Prisma, PrismaClient, session_userprofile } from '@prisma/client'; +import { ELecture } from 'src/common/entities/ELecture'; +import { ETimetable } from 'src/common/entities/ETimetable'; +import { PrismaService } from '../prisma.service'; + +@Injectable() +export class TimetableRepository { + constructor(private readonly prisma: PrismaService) {} + + async getTimetables( + user: session_userprofile, + year?: number | null, + semester?: number | null, + paginationAndSorting?: { + orderBy?: Prisma.timetable_timetableOrderByWithRelationInput[]; + skip?: number; + take?: number; + }, + ): Promise { + const skip = paginationAndSorting?.skip; + const take = paginationAndSorting?.take; + const orderBy = paginationAndSorting?.orderBy; + + return await this.prisma.timetable_timetable.findMany({ + include: ETimetable.Details.include, + where: { + year: year ?? undefined, + semester: semester ?? undefined, + user_id: user.id, + }, + skip: skip, + take: take, + orderBy: orderBy, + }); + } + + async getTimetableBasics( + user: session_userprofile, + year: number, + semester: number, + paginationAndSorting: { + orderBy: Prisma.timetable_timetableOrderByWithRelationInput; + skip?: number; + take?: number; + }, + ): Promise { + const skip = paginationAndSorting.skip; + const take = paginationAndSorting.take; + const orderBy = paginationAndSorting.orderBy; + + return await this.prisma.timetable_timetable.findMany({ + where: { + year: year, + semester: semester, + user_id: user.id, + }, + skip: skip, + take: take, + orderBy: orderBy, + }); + } + + async createTimetable( + user: session_userprofile, + year: number, + semester: number, + arrangeOrder: number, + lectures: ELecture.Details[], + ): Promise { + return await this.prisma.timetable_timetable.create({ + data: { + user_id: user.id, + year: year, + semester: semester, + arrange_order: arrangeOrder, + timetable_timetable_lectures: { + createMany: { + data: lectures.map((lecture) => { + return { + lecture_id: lecture.id, + }; + }), + }, + }, + }, + include: ETimetable.Details.include, + }); + } + + async getTimeTableBasicById(timeTableId: number) { + return await this.prisma.timetable_timetable.findUniqueOrThrow({ + where: { + id: timeTableId, + }, + }); + } + + async addLectureToTimetable(timeTableId: number, lectureId: number) { + return await this.prisma.timetable_timetable_lectures.create({ + data: { + timetable_id: timeTableId, + lecture_id: lectureId, + }, + }); + } + + async getTimeTableById(timeTableId: number): Promise { + return await this.prisma.timetable_timetable.findUniqueOrThrow({ + include: ETimetable.Details.include, + where: { + id: timeTableId, + }, + }); + } + + async removeLectureFromTimetable(timeTableId: number, lectureId: number) { + return await this.prisma.timetable_timetable_lectures.delete({ + where: { + timetable_id_lecture_id: { + timetable_id: timeTableId, + lecture_id: lectureId, + }, + }, + }); + } + + async deleteById(timetableId: number, tx?: PrismaClient) { + let prismaClient: PrismaClient = this.prisma; + if (tx) { + prismaClient = tx; + } + await prismaClient.timetable_timetable_lectures.deleteMany({ + where: { + timetable_id: timetableId, + }, + }); + return await prismaClient.timetable_timetable.delete({ + where: { + id: timetableId, + }, + }); + } + + async updateOrder(id: number, arrange_order: number) { + return await this.prisma.timetable_timetable.update({ + where: { + id: id, + }, + data: { + arrange_order: arrange_order, + }, + }); + } + + async getLecturesWithClassTimes(timetableId: number) { + return this.prisma.timetable_timetable_lectures.findMany({ + where: { timetable_id: timetableId }, + include: ETimetable.WithLectureClasstimes.include, + }); + } +} diff --git a/src/prisma/repositories/track.repository.ts b/src/prisma/repositories/track.repository.ts new file mode 100644 index 00000000..91d4a03d --- /dev/null +++ b/src/prisma/repositories/track.repository.ts @@ -0,0 +1,60 @@ +import { Injectable } from '@nestjs/common'; +import { ETrack } from 'src/common/entities/ETrack'; +import { PrismaService } from '../prisma.service'; + +@Injectable() +export class TracksRepository { + constructor(private readonly prisma: PrismaService) {} + + public async getAllTracks(): Promise<{ + generalTracks: ETrack.General[]; + majorTracks: ETrack.Major[]; + addtionalTracks: ETrack.Additional[]; + }> { + const generalTracks = await this.prisma.graduation_generaltrack.findMany({ + orderBy: [ + { is_foreign: 'asc' }, + { start_year: 'asc' }, + { end_year: 'asc' }, + ], + }); + const majorTracks = await this.prisma.graduation_majortrack.findMany({ + include: { + subject_department: true, + }, + orderBy: [ + { + subject_department: { + code: 'asc', + }, + }, + { start_year: 'asc' }, + { end_year: 'asc' }, + ], + }); + const addtionalTracks = + await this.prisma.graduation_additionaltrack.findMany({ + include: { + subject_department: true, + }, + orderBy: [ + { + subject_department: { + code: 'asc', + }, + }, + { start_year: 'asc' }, + { end_year: 'asc' }, + ], + }); + const sortedAddtionalTracks = addtionalTracks.sort((a, b) => { + return a.type.length - b.type.length; + }); + + return { + generalTracks, + majorTracks, + addtionalTracks: sortedAddtionalTracks, + }; + } +} diff --git a/src/prisma/repositories/user.repository.ts b/src/prisma/repositories/user.repository.ts index dcc1960d..deac80df 100644 --- a/src/prisma/repositories/user.repository.ts +++ b/src/prisma/repositories/user.repository.ts @@ -1,27 +1,82 @@ -import { Injectable } from "@nestjs/common"; -import { PrismaService } from "../prisma.service"; -import { session_userprofile, Prisma} from "@prisma/client"; +import { Injectable } from '@nestjs/common'; +import { Prisma, session_userprofile, subject_semester } from '@prisma/client'; +import { PrismaService } from '../prisma.service'; @Injectable() -export class UserRepository{ - constructor(private readonly prisma: PrismaService){} +export class UserRepository { + constructor(private readonly prisma: PrismaService) {} - async findBySid(sid: string){ + async findBySid(sid: string) { return await this.prisma.session_userprofile.findFirst({ - where: { sid: sid } - }) + where: { sid: sid }, + }); } - async createUser(user: Prisma.session_userprofileCreateInput): Promise{ + async createUser( + user: Prisma.session_userprofileCreateInput, + ): Promise { return await this.prisma.session_userprofile.create({ - data: user - }) + data: user, + }); } - async updateUser(userId, user: Prisma.session_userprofileUpdateInput): Promise{ + async updateUser( + userId: number, + user: Prisma.session_userprofileUpdateInput, + ): Promise { return await this.prisma.session_userprofile.update({ data: user, - where: { id : userId } - }) + where: { id: userId }, + }); + } + + async changeFavoriteDepartments(userId: number, departmentIds: number[]) { + await this.prisma.session_userprofile_favorite_departments.deleteMany({ + where: { userprofile_id: userId }, + }); + return await this.prisma.session_userprofile.update({ + where: { id: userId }, + data: { + favorite_departments: { + create: departmentIds.map((department_id) => ({ department_id })), + }, + }, + }); + } + + async getTakenLectures( + userId: number, + notWritableSemester?: subject_semester | null, + ) { + return await this.prisma.session_userprofile_taken_lectures.findMany({ + include: { lecture: true }, + where: { + userprofile_id: userId, + lecture: { + AND: [ + { + year: { not: notWritableSemester?.year }, + }, + { semester: { not: notWritableSemester?.semester } }, + ], + }, + }, + }); + } + + async getTakenLecturesByYear( + userId: number, + from: number, + to: number, + notWritableSemester?: subject_semester | null, + ) { + const reviewWritableLecture = await this.getTakenLectures( + userId, + notWritableSemester, + ); + return reviewWritableLecture.filter( + (takenLecture) => + takenLecture.lecture.year >= from && takenLecture.lecture.year <= to, + ); } -} \ No newline at end of file +} diff --git a/src/prisma/repositories/wishlist.repository.ts b/src/prisma/repositories/wishlist.repository.ts new file mode 100644 index 00000000..c2ed0285 --- /dev/null +++ b/src/prisma/repositories/wishlist.repository.ts @@ -0,0 +1,61 @@ +import { Injectable } from '@nestjs/common'; +import { EWishlist } from 'src/common/entities/EWishlist'; +import { PrismaService } from '../prisma.service'; + +@Injectable() +export class WishlistRepository { + constructor(private readonly prisma: PrismaService) {} + + async getOrCreateWishlist(userId: number) { + return await this.prisma.timetable_wishlist.upsert({ + where: { user_id: userId }, + create: { user_id: userId }, + update: {}, + include: EWishlist.WithLectures.include, + }); + } + + async addLecture(wishlistId: number, lectureId: number) { + return await this.prisma.timetable_wishlist_lectures.upsert({ + where: { + wishlist_id_lecture_id: { + lecture_id: lectureId, + wishlist_id: wishlistId, + }, + }, + create: { wishlist_id: wishlistId, lecture_id: lectureId }, + update: {}, + }); + } + + async removeLecture(wishlistId: number, lectureId: number) { + return await this.prisma.timetable_wishlist_lectures.delete({ + where: { + wishlist_id_lecture_id: { + lecture_id: lectureId, + wishlist_id: wishlistId, + }, + }, + }); + } + + async getLectureInWishlist(wishlistId: number, lectureId: number) { + return await this.prisma.timetable_wishlist_lectures.findUnique({ + where: { + wishlist_id_lecture_id: { + wishlist_id: wishlistId, + lecture_id: lectureId, + }, + }, + }); + } + + async getWishlistWithLectures( + wishlistId: number, + ): Promise { + return await this.prisma.timetable_wishlist.findUnique({ + where: { id: wishlistId }, + include: EWishlist.WithLectures.include, + }); + } +} diff --git a/src/prisma/schema.prisma b/src/prisma/schema.prisma index c13e9c32..d6a4952f 100644 --- a/src/prisma/schema.prisma +++ b/src/prisma/schema.prisma @@ -226,61 +226,83 @@ model review_review { course_id Int lecture_id Int content String @db.MediumText - grade Int @db.SmallInt - load Int @db.SmallInt - speech Int @db.SmallInt + grade Int @default(0) @db.SmallInt + load Int @default(0) @db.SmallInt + speech Int @default(0) @db.SmallInt writer_id Int? writer_label String @db.VarChar(200) updated_datetime DateTime @db.DateTime(0) - like Int - is_deleted Int + like Int @default(0) + is_deleted Int @default(0) written_datetime DateTime? @db.DateTime(0) main_famoushumanityreviewdailyfeed_reviews main_famoushumanityreviewdailyfeed_reviews[] main_famousmajorreviewdailyfeed_reviews main_famousmajorreviewdailyfeed_reviews[] + course subject_course @relation(fields: [course_id], references: [id]) + lecture subject_lecture @relation(fields: [lecture_id], references: [id]) + writer session_userprofile? @relation(fields: [writer_id], references: [id]) + review_reviewvote review_reviewvote[] @@unique([writer_id, lecture_id], map: "review_comment_writer_id_af700a5d_uniq") @@index([written_datetime], map: "review_comment_e5e30a4a") + @@index([course_id], map: "review_review_course_id_fkey") + @@index([lecture_id], map: "review_review_lecture_id_fkey") } model review_reviewvote { - id Int @id @default(autoincrement()) + id Int @id @default(autoincrement()) review_id Int userprofile_id Int? - created_datetime DateTime? @db.DateTime(6) + created_datetime DateTime? @db.DateTime(6) + review review_review @relation(fields: [review_id], references: [id], onDelete: Cascade) + userprofile session_userprofile? @relation(fields: [userprofile_id], references: [id]) @@unique([review_id, userprofile_id], map: "review_commentvote_comment_id_e4594aea_uniq") @@index([created_datetime], map: "review_reviewvote_created_datetime_450f85e2") + @@index([userprofile_id], map: "review_reviewvote_userprofile_id_fkey") } model session_userprofile { - id Int @id @default(autoincrement()) - student_id String @db.VarChar(10) - sid String @db.VarChar(30) + id Int @id @default(autoincrement()) + user_id Int? @unique(map: "session_userprofile_user_id_09dd6af1_uniq") + student_id String @db.VarChar(10) + sid String @db.VarChar(30) + language String? @db.VarChar(15) + portal_check Int? @default(0) department_id Int? - email String? @db.VarChar(255) - date_joined DateTime @db.DateTime(0) - first_name String @db.VarChar(30) - last_name String @db.VarChar(150) - refresh_token String? @db.VarChar(255) + email String? @db.VarChar(255) + date_joined DateTime @db.DateTime(0) + first_name String @db.VarChar(30) + last_name String @db.VarChar(150) + refresh_token String? @db.VarChar(255) main_ratedailyuserfeed main_ratedailyuserfeed[] main_relatedcoursedailyuserfeed main_relatedcoursedailyuserfeed[] main_reviewwritedailyuserfeed main_reviewwritedailyuserfeed[] planner_planner planner_planner[] + reviews review_review[] + reviewvote review_reviewvote[] + department subject_department? @relation(fields: [department_id], references: [id], onUpdate: Restrict) + favorite_departments session_userprofile_favorite_departments[] session_userprofile_majors session_userprofile_majors[] session_userprofile_minors session_userprofile_minors[] session_userprofile_specialized_major session_userprofile_specialized_major[] + taken_lectures session_userprofile_taken_lectures[] subject_courseuser subject_courseuser[] support_rate support_rate[] timetable_timetable timetable_timetable[] timetable_wishlist timetable_wishlist? + + @@index([department_id], map: "session_userprofile_department_id_fkey") } model session_userprofile_favorite_departments { - id Int @id @default(autoincrement()) + id Int @id @default(autoincrement()) userprofile_id Int department_id Int + department subject_department @relation(fields: [department_id], references: [id], onUpdate: Restrict) + userprofile session_userprofile @relation(fields: [userprofile_id], references: [id], onUpdate: Restrict) @@unique([userprofile_id, department_id], map: "userprofile_id") + @@index([department_id], map: "session_userprofile_favorite_departments_department_id_fkey") } model session_userprofile_majors { @@ -317,11 +339,14 @@ model session_userprofile_specialized_major { } model session_userprofile_taken_lectures { - id Int @id @default(autoincrement()) + id Int @id @default(autoincrement()) userprofile_id Int lecture_id Int + lecture subject_lecture @relation(fields: [lecture_id], references: [id], onUpdate: Restrict) + userprofile session_userprofile @relation(fields: [userprofile_id], references: [id], onUpdate: Restrict) @@unique([userprofile_id, lecture_id], map: "userprofile_id") + @@index([lecture_id], map: "session_userprofile_taken_lectures_lecture_id_fkey") } model subject_classtime { @@ -358,21 +383,36 @@ model subject_course { load Float speech Float latest_written_datetime DateTime? @db.DateTime(0) + title_no_space String @db.VarChar(100) + title_en_no_space String @db.VarChar(200) main_relatedcoursedailyuserfeed main_relatedcoursedailyuserfeed[] planner_futureplanneritem planner_futureplanneritem[] + review review_review[] + subject_department subject_department @relation(fields: [department_id], references: [id], onUpdate: Restrict) + subject_course_professors subject_course_professors[] subject_course_related_courses_posterior_subject_course_related_courses_posterior_from_course_idTosubject_course subject_course_related_courses_posterior[] @relation("subject_course_related_courses_posterior_from_course_idTosubject_course") subject_course_related_courses_posterior_subject_course_related_courses_posterior_to_course_idTosubject_course subject_course_related_courses_posterior[] @relation("subject_course_related_courses_posterior_to_course_idTosubject_course") subject_course_related_courses_prior_subject_course_related_courses_prior_from_course_idTosubject_course subject_course_related_courses_prior[] @relation("subject_course_related_courses_prior_from_course_idTosubject_course") subject_course_related_courses_prior_subject_course_related_courses_prior_to_course_idTosubject_course subject_course_related_courses_prior[] @relation("subject_course_related_courses_prior_to_course_idTosubject_course") subject_courseuser subject_courseuser[] + lecture subject_lecture[] + + @@index([department_id], map: "subject_course_department_id_fkey") + @@index([title_en_no_space], map: "subject_course_title_en_no_space_index") + @@index([title], map: "subject_course_title_index") + @@index([title_no_space], map: "subject_course_title_no_space_index") + @@index([title_en], map: "subject_course_title_en_index") } model subject_course_professors { - id Int @id @default(autoincrement()) + id Int @id @default(autoincrement()) course_id Int professor_id Int + course subject_course @relation(fields: [course_id], references: [id], onUpdate: Restrict) + professor subject_professor @relation(fields: [professor_id], references: [id], onUpdate: Restrict) @@unique([course_id, professor_id], map: "course_id") + @@index([professor_id], map: "subject_course_professors_professor_id_fkey") } model subject_course_related_courses_posterior { @@ -410,19 +450,23 @@ model subject_courseuser { } model subject_department { - id Int @id - num_id String @db.VarChar(4) - code String @db.VarChar(5) - name String @db.VarChar(60) - name_en String? @db.VarChar(60) - visible Boolean - graduation_additionaltrack graduation_additionaltrack[] - graduation_majortrack graduation_majortrack[] - main_famousmajorreviewdailyfeed main_famousmajorreviewdailyfeed[] - planner_arbitraryplanneritem planner_arbitraryplanneritem[] - session_userprofile_majors session_userprofile_majors[] - session_userprofile_minors session_userprofile_minors[] - session_userprofile_specialized_major session_userprofile_specialized_major[] + id Int @id + num_id String @db.VarChar(4) + code String @db.VarChar(5) + name String @db.VarChar(60) + name_en String? @db.VarChar(60) + visible Boolean + graduation_additionaltrack graduation_additionaltrack[] + graduation_majortrack graduation_majortrack[] + main_famousmajorreviewdailyfeed main_famousmajorreviewdailyfeed[] + planner_arbitraryplanneritem planner_arbitraryplanneritem[] + session_userprofile session_userprofile[] + session_userprofile_favorite_departments session_userprofile_favorite_departments[] + session_userprofile_majors session_userprofile_majors[] + session_userprofile_minors session_userprofile_minors[] + session_userprofile_specialized_major session_userprofile_specialized_major[] + subject_course subject_course[] + subject_lecture subject_lecture[] } model subject_examtime { @@ -437,17 +481,17 @@ model subject_examtime { } model subject_lecture { - id Int @id @default(autoincrement()) - code String @db.VarChar(10) - old_code String @db.VarChar(10) + id Int @id @default(autoincrement()) + code String @db.VarChar(10) + old_code String @db.VarChar(10) year Int - semester Int @db.SmallInt + semester Int @db.SmallInt department_id Int - class_no String @db.VarChar(4) - title String @db.VarChar(100) - title_en String @db.VarChar(200) - type String @db.VarChar(12) - type_en String @db.VarChar(36) + class_no String @db.VarChar(4) + title String @db.VarChar(100) + title_en String @db.VarChar(200) + type String @db.VarChar(12) + type_en String @db.VarChar(36) audience Int credit Int num_classes Int @@ -465,51 +509,61 @@ model subject_lecture { load Float speech Float review_total_weight Float - class_title String? @db.VarChar(100) - class_title_en String? @db.VarChar(100) - common_title String? @db.VarChar(100) - common_title_en String? @db.VarChar(100) + class_title String? @db.VarChar(100) + class_title_en String? @db.VarChar(100) + common_title String? @db.VarChar(100) + common_title_en String? @db.VarChar(100) + title_no_space String @db.VarChar(100) + title_en_no_space String @db.VarChar(200) main_reviewwritedailyuserfeed main_reviewwritedailyuserfeed[] planner_takenplanneritem planner_takenplanneritem[] + review review_review[] + students session_userprofile_taken_lectures[] subject_classtime subject_classtime[] subject_examtime subject_examtime[] + course subject_course @relation(fields: [course_id], references: [id], onUpdate: Restrict) + subject_department subject_department @relation(fields: [department_id], references: [id], onUpdate: Restrict) + subject_lecture_professors subject_lecture_professors[] timetable_oldtimetable_lectures timetable_oldtimetable_lectures[] timetable_timetable_lectures timetable_timetable_lectures[] timetable_wishlist_lectures timetable_wishlist_lectures[] @@index([deleted], map: "subject_lecture_deleted_bedc6156_uniq") @@index([type_en], map: "subject_lecture_type_en_45ee2d3a_uniq") + @@index([course_id], map: "subject_lecture_course_id_fkey") + @@index([department_id], map: "subject_lecture_department_id_fkey") + @@index([title_en_no_space], map: "subject_lecture_title_en_no_space_index") + @@index([title_no_space], map: "subject_lecture_title_no_space_index") + @@index([title_en], map: "subject_lecture_title_en_index") + @@index([title], map: "subject_lecture_title_index") } model subject_lecture_professors { - id Int @id @default(autoincrement()) + id Int @id @default(autoincrement()) lecture_id Int professor_id Int + lecture subject_lecture @relation(fields: [lecture_id], references: [id], onUpdate: Restrict) + professor subject_professor @relation(fields: [professor_id], references: [id], onUpdate: Restrict) @@unique([lecture_id, professor_id], map: "lecture_id") + @@index([professor_id], map: "subject_lecture_professors_professor_id_fkey") } model subject_professor { - id Int @id @default(autoincrement()) - professor_name String @db.VarChar(100) - professor_name_en String? @db.VarChar(100) - professor_id Int - major String @db.VarChar(30) - grade_sum Float - load_sum Float - speech_sum Float - review_total_weight Float - grade Float - load Float - speech Float -} - -model subject_professor_course_list { - id Int @id @default(autoincrement()) - professor_id Int - course_id Int - - @@unique([professor_id, course_id], map: "professor_id") + id Int @id @default(autoincrement()) + professor_name String @db.VarChar(100) + professor_name_en String? @db.VarChar(100) + professor_id Int + major String @db.VarChar(30) + grade_sum Float + load_sum Float + speech_sum Float + review_total_weight Float + grade Float + load Float + speech Float + subject_course_professors subject_course_professors[] + subject_lecture_professors subject_lecture_professors[] } model subject_semester { @@ -544,7 +598,7 @@ model support_rate { id Int @id @default(autoincrement()) score Int @db.SmallInt year Int @db.SmallInt - created_datetime DateTime? @db.DateTime(0) + created_datetime DateTime @default(now()) @db.DateTime(0) user_id Int version String @db.VarChar(20) session_userprofile session_userprofile @relation(fields: [user_id], references: [id], onUpdate: Restrict, map: "support_rate_user_id_6d69ec9d_fk_session_userprofile_id") @@ -689,8 +743,8 @@ model planner_arbitraryplanneritem { credit_au Int department_id Int? planner_id Int - subject_department subject_department? @relation(fields: [department_id], references: [id], onDelete: Restrict, onUpdate: Restrict, map: "planner_arbitrarypla_department_id_0dc7ce25_fk_subject_d") - planner_planner planner_planner @relation(fields: [planner_id], references: [id], onUpdate: Restrict, map: "planner_arbitrarypla_planner_id_d6069d2c_fk_planner_p") + subject_department subject_department? @relation(fields: [department_id], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "planner_arbitrarypla_department_id_0dc7ce25_fk_subject_d") + planner_planner planner_planner @relation(fields: [planner_id], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "planner_arbitrarypla_planner_id_d6069d2c_fk_planner_p") @@index([department_id], map: "planner_arbitrarypla_department_id_0dc7ce25_fk_subject_d") @@index([planner_id], map: "planner_arbitrarypla_planner_id_d6069d2c_fk_planner_p") @@ -705,8 +759,8 @@ model planner_futureplanneritem { semester Int course_id Int planner_id Int - subject_course subject_course @relation(fields: [course_id], references: [id], onUpdate: Restrict, map: "planner_futureplanne_course_id_b1a06444_fk_subject_c") - planner_planner planner_planner @relation(fields: [planner_id], references: [id], onUpdate: Restrict, map: "planner_futureplanne_planner_id_dfd70193_fk_planner_p") + subject_course subject_course @relation(fields: [course_id], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "planner_futureplanne_course_id_b1a06444_fk_subject_c") + planner_planner planner_planner @relation(fields: [planner_id], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "planner_futureplanne_planner_id_dfd70193_fk_planner_p") @@index([course_id], map: "planner_futureplanne_course_id_b1a06444_fk_subject_c") @@index([planner_id], map: "planner_futureplanne_planner_id_dfd70193_fk_planner_p") @@ -742,8 +796,8 @@ model planner_planner_additional_tracks { id Int @id @default(autoincrement()) planner_id Int additionaltrack_id Int - graduation_additionaltrack graduation_additionaltrack @relation(fields: [additionaltrack_id], references: [id], onUpdate: Restrict, map: "planner_planner_addi_additionaltrack_id_c46b8c4e_fk_graduatio") - planner_planner planner_planner @relation(fields: [planner_id], references: [id], onUpdate: Restrict, map: "planner_planner_addi_planner_id_e439a309_fk_planner_p") + graduation_additionaltrack graduation_additionaltrack @relation(fields: [additionaltrack_id], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "planner_planner_addi_additionaltrack_id_c46b8c4e_fk_graduatio") + planner_planner planner_planner @relation(fields: [planner_id], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "planner_planner_addi_planner_id_e439a309_fk_planner_p") @@unique([planner_id, additionaltrack_id], map: "planner_planner_addition_planner_id_additionaltra_2298c5cd_uniq") @@index([additionaltrack_id], map: "planner_planner_addi_additionaltrack_id_c46b8c4e_fk_graduatio") @@ -754,9 +808,17 @@ model planner_takenplanneritem { is_excluded Boolean lecture_id Int planner_id Int - subject_lecture subject_lecture @relation(fields: [lecture_id], references: [id], onUpdate: Restrict, map: "planner_takenplanner_lecture_id_9b2d30d8_fk_subject_l") - planner_planner planner_planner @relation(fields: [planner_id], references: [id], onUpdate: Restrict, map: "planner_takenplanner_planner_id_b725ff83_fk_planner_p") + subject_lecture subject_lecture @relation(fields: [lecture_id], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "planner_takenplanner_lecture_id_9b2d30d8_fk_subject_l") + planner_planner planner_planner @relation(fields: [planner_id], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "planner_takenplanner_planner_id_b725ff83_fk_planner_p") @@unique([planner_id, lecture_id], map: "planner_takenplanneritem_planner_id_lecture_id_4b39b432_uniq") @@index([lecture_id], map: "planner_takenplanner_lecture_id_9b2d30d8_fk_subject_l") } + +model subject_professor_course_list { + id Int @id @default(autoincrement()) + professor_id Int + course_id Int + + @@unique([professor_id, course_id], map: "professor_id") +} diff --git a/src/prisma/transactionManager.ts b/src/prisma/transactionManager.ts new file mode 100644 index 00000000..8e72ae66 --- /dev/null +++ b/src/prisma/transactionManager.ts @@ -0,0 +1,33 @@ +import { ModuleRef } from '@nestjs/core'; +import { Injectable } from '@nestjs/common'; +import * as runtime from '@prisma/client/runtime/library'; +import { PrismaClient } from '@prisma/client'; +import { PrismaService } from './prisma.service'; + +@Injectable() +export class TranManager { + private static staticPrismaService: PrismaService; + private static txClient: Omit; + + constructor( + private readonly ref: ModuleRef, + private readonly prismaService: PrismaService, + ) { + TranManager.staticPrismaService = prismaService; + } + + public static async transaction(bizLogic: () => unknown) { + const prisma = this.staticPrismaService; + return await prisma.$transaction(async (tx) => { + TranManager.saveTx(tx); + return bizLogic(); + }); + } + private static saveTx(tx: Omit) { + this.txClient = tx; + } + + public static getTx() { + return TranManager.txClient ?? TranManager.staticPrismaService; + } +} diff --git a/src/settings.ts b/src/settings.ts index 43614f2d..e9a8432d 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -1,11 +1,9 @@ +import { Prisma } from '@prisma/client'; import dotenv from 'dotenv'; import { dotEnvOptions } from './dotenv-options'; -import { PrismaClientOptions } from 'prisma/prisma-client/runtime'; -import { JwtModuleOptions, JwtOptionsFactory } from '@nestjs/jwt'; dotenv.config(dotEnvOptions); console.log(`NODE_ENV environment: ${process.env.NODE_ENV}`); -console.log(`DATABASE_URL: ${process.env.DATABASE_URL}`); export default () => { return { @@ -14,10 +12,47 @@ export default () => { awsconfig: () => getAWSConfig(), getJwtConfig: () => getJwtConfig(), getSsoConfig: () => getSsoConfig(), + getCorsConfig: () => getCorsConfig(), + getVersion: () => getVersion(), + getStaticConfig: () => staticConfig(), }; }; -const getPrismaConfig = (): PrismaClientOptions => { +const getCorsConfig = () => { + const { NODE_ENV } = process.env; + if (NODE_ENV === 'prod') { + return { + origin: [ + 'https://otl.kaist.ac.kr', + 'http://otl.kaist.ac.kr', + 'https://otl.sparcs.org', + 'http://otl.sparcs.org', + ], + methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', + credentials: true, + preflightContinue: false, + optionsSuccessStatus: 204, + }; + } else if (NODE_ENV === 'dev') { + return { + origin: 'http://3.37.146.183', + methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', + credentials: true, + preflightContinue: false, + optionsSuccessStatus: 204, + }; + } else { + return { + origin: 'http://localhost:5173', + methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', + credentials: true, + preflightContinue: false, + optionsSuccessStatus: 204, + }; + } +}; + +const getPrismaConfig = (): Prisma.PrismaClientOptions => { return { datasources: { db: { @@ -25,11 +60,28 @@ const getPrismaConfig = (): PrismaClientOptions => { }, }, errorFormat: 'pretty', - log: [`error`], + log: [ + // { + // emit: 'event', + // level: 'query', + // }, + { + emit: 'stdout', + level: 'error', + }, + { + emit: 'stdout', + level: 'info', + }, + // { + // emit: 'stdout', + // level: 'warn', + // }, + ], }; }; -const getReplicatedPrismaConfig = (): PrismaClientOptions => { +const getReplicatedPrismaConfig = (): Prisma.PrismaClientOptions => { return {}; }; @@ -44,7 +96,7 @@ const getJwtConfig = () => { expiresIn: process.env.EXPIRES_IN, refreshExpiresIn: process.env.REFRESH_EXPIRES_IN, }, - } + }; }; const getSsoConfig = (): any => { @@ -53,4 +105,17 @@ const getSsoConfig = (): any => { ssoClientId: process.env.SSO_CLIENT_ID, ssoSecretKey: process.env.SSO_SECRET_KEY, }; -}; \ No newline at end of file +}; + +const getVersion = () => { + return String(process.env.npm_package_version); +}; + +const staticConfig = (): any => { + return { + file_path: + process.env.DOCKER_DEPLOY === 'true' + ? '/var/www/otlplus-server/static/' + : 'static/', + }; +}; diff --git a/static/fonts/NanumBarunGothic.ttf b/static/fonts/NanumBarunGothic.ttf new file mode 100644 index 00000000..c3148683 Binary files /dev/null and b/static/fonts/NanumBarunGothic.ttf differ diff --git a/static/fonts/NotoSansKR-Regular.otf b/static/fonts/NotoSansKR-Regular.otf new file mode 100644 index 00000000..7c5c2fae Binary files /dev/null and b/static/fonts/NotoSansKR-Regular.otf differ diff --git a/static/img/Image_template_5days.png b/static/img/Image_template_5days.png new file mode 100644 index 00000000..e9ee828a Binary files /dev/null and b/static/img/Image_template_5days.png differ diff --git a/static/img/Image_template_7days.png b/static/img/Image_template_7days.png new file mode 100644 index 00000000..3b66fc23 Binary files /dev/null and b/static/img/Image_template_7days.png differ diff --git a/test/app.e2e-spec.ts b/test/app.e2e-spec.ts index 50cda623..846b1c28 100644 --- a/test/app.e2e-spec.ts +++ b/test/app.e2e-spec.ts @@ -1,6 +1,6 @@ -import { Test, TestingModule } from '@nestjs/testing'; import { INestApplication } from '@nestjs/common'; -import * as request from 'supertest'; +import { Test, TestingModule } from '@nestjs/testing'; +import request from 'supertest'; import { AppModule } from './../src/app.module'; describe('AppController (e2e)', () => { diff --git a/test/jest.json b/test/jest.json new file mode 100644 index 00000000..e69de29b diff --git a/test/n+1/nplus1.spec.ts b/test/n+1/nplus1.spec.ts new file mode 100644 index 00000000..331ea4fc --- /dev/null +++ b/test/n+1/nplus1.spec.ts @@ -0,0 +1,27 @@ +import { INestApplication } from '@nestjs/common'; +import { Test, TestingModule } from '@nestjs/testing'; +import { ETimetable } from '@src/common/entities/ETimetable'; +import { PrismaService } from '@src/prisma/prisma.service'; +import { AppModule } from '@src/app.module'; + +describe('AppController (e2e)', () => { + let app: INestApplication; + + beforeEach(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + }); + + it('/session/info (GET)', async () => { + const prisma = app.get(PrismaService); + const timetableId = 82; + return prisma.timetable_timetable_lectures.findMany({ + where: { timetable_id: timetableId }, + include: ETimetable.WithLectureClasstimes.include, + }); + }, 100000000); +}); diff --git a/test/prisma.spec.ts b/test/prisma.spec.ts new file mode 100644 index 00000000..1e7a2d7e --- /dev/null +++ b/test/prisma.spec.ts @@ -0,0 +1,166 @@ +import { INestApplication } from '@nestjs/common'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AppModule } from '../src/app.module'; +import { PrismaService } from '../src/prisma/prisma.service'; +import { CourseRepository } from '../src/prisma/repositories/course.repository'; +import { ELecture } from '@src/common/entities/ELecture'; +import { applyOffset, applyOrder } from '@src/common/utils/search.utils'; +import { LectureRepository } from '@src/prisma/repositories/lecture.repository'; +import { set } from 'date-fns'; + +describe('AppController (e2e)', () => { + let app: INestApplication; + + beforeEach(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + }); + + // it('distinct count', async () => { + // const prisma = app.get(PrismaService); + // console.log('transaction test with tranManager'); + // const result = await prisma.timetable_timetable.groupBy({ + // by: ['user_id'], + // where: { + // timetable_timetable_lectures: { + // some: { + // lecture_id: 16155, + // }, + // }, + // }, + // _count: { + // user_id: true, + // }, + // }); + // console.log(result); + // }, 10000); + + // it('distinct count', async () => { + // const prisma = app.get(PrismaService); + // const result = + // ( + // await prisma.timetable_timetable.findMany({ + // distinct: ['user_id'], + // where: { + // timetable_timetable_lectures: { + // some: { + // lecture_id: 16155, + // }, + // }, + // }, + // }) + // )?.length ?? 0; + // console.log(result); + // }); + + it('join three table', async () => { + const lectureRepo = app.get(LectureRepository); + const courseRepo = app.get(CourseRepository); + const prisma = app.get(PrismaService); + const DEFAULT_LIMIT = 300; + const DEFAULT_ORDER = ['year', 'semester', 'old_code', 'class_no']; + const researchTypes = [ + 'Individual Study', + 'Thesis Study(Undergraduate)', + 'Thesis Research(MA/phD)', + ]; + + const semesterFilter = lectureRepo.semesterFilter(2024, 3); + const timeFilter = lectureRepo.timeFilter(0, 5, 13); + // const begin = new Date().setHours(10, 30, 0, 0); + // const end = new Date().setHours(14, 30, 0, 0); + // const timeFilter = { + // subject_classtime: { + // some: + // { + // day: 0, + // begin: { + // gte: set(new Date(), { hours: 10, minutes: 30 }) + // }, + // end: { + // lte: set(new Date(), { hours: 14, minutes: 30 }) + // } + // } + // } + // }; + const departmentFilter = courseRepo.departmentFilter(['CS']); + const typeFilter = courseRepo.typeFilter(['ME']); + const groupFilter = courseRepo.groupFilter(undefined); + const keywordFilter = courseRepo.keywordFilter(undefined, false); + const defaultFilter = { + AND: [ + { + deleted: false, + }, + { + type_en: { + notIn: researchTypes, + }, + }, + ], + }; + + const filters: object[] = [ + semesterFilter, + timeFilter, + departmentFilter, + typeFilter, + groupFilter, + keywordFilter, + defaultFilter, + ].filter((filter): filter is object => filter !== null); + + const options = { + include: { + subject_department: true, + subject_lecture_professors: { include: { professor: true } }, + subject_classtime: true, + subject_examtime: true, + }, + where: { + AND: filters, + }, + take: DEFAULT_LIMIT, + }; + console.log(JSON.stringify(options, null, 2)); + const queryResult = await prisma.subject_lecture.findMany(options); + const levelFilteredResult = courseRepo.levelFilter( + queryResult, + ['ALL'], + ); + + const orderedQuery = applyOrder( + levelFilteredResult, + (['old_code', 'class_no'] ?? DEFAULT_ORDER) as (keyof ELecture.Details)[], + ); + const result = applyOffset(orderedQuery, 0); + console.log(JSON.stringify(result, null, 2)); + }); + + // it("select classtime", async () => { + // const prisma = app.get(PrismaService); + // const result1 = await prisma.subject_classtime.findMany({ + // where:{ + // day: 0 + // } + // }) + // console.log(result1) + // const result = await prisma.subject_classtime.findMany({ + // where: { + // day: 0, + // begin: { + // gte: new Date('1970-01-01T10:30:00.000Z') + // }, + // end: { + // lte: new Date('1970-01-01T14:30:00.000Z') + // } + // } + // }); + // console.log(result.length); + // + // }) +}); diff --git a/test/session/session.spec.ts b/test/session/session.spec.ts new file mode 100644 index 00000000..4a1cbea9 --- /dev/null +++ b/test/session/session.spec.ts @@ -0,0 +1,57 @@ +import { INestApplication } from '@nestjs/common'; +import { Test, TestingModule } from '@nestjs/testing'; +import axios from 'axios'; +import { AppModule } from '../../src/app.module'; +import { UserService } from '../../src/modules/user/user.service'; +import { PrismaService } from '../../src/prisma/prisma.service'; + +describe('AppController (e2e)', () => { + let app: INestApplication; + + beforeEach(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + }); + + it('/session/info (GET)', async () => { + const prismaService = app.get(PrismaService); + const userService = app.get(UserService); + + const sidList = await axios.get('http://localhost:58000/session'); + + const BATCH_SIZE = 10; + const BATCH_COUNT = Math.floor(sidList.data.length / BATCH_SIZE) + 1; + for (let i = 0; i < BATCH_COUNT; i++) { + const batch = sidList.data.slice(i * BATCH_SIZE, (i + 1) * BATCH_SIZE); + const promises = batch.map(async (sid: string) => { + const r = await axios.get( + `http://localhost:58000/session/login?sid=${sid}`, + ); + const { data } = r; + const user = prismaService.session_userprofile + .findFirst({ + where: { sid }, + }) + .then((user) => { + try { + return userService.getProfile(user); + } catch (e) { + console.log('error with sid: ', sid); + console.error(e); + } + }) + .then((profile) => { + expect(profile).toEqual(data); + return profile; + }); + return user; + }); + await Promise.all(promises); + console.log(`batch ${i} / ${BATCH_COUNT} done`); + } + }, 100000000); +}); diff --git a/test/transaction.spec.ts b/test/transaction.spec.ts new file mode 100644 index 00000000..7ea82faf --- /dev/null +++ b/test/transaction.spec.ts @@ -0,0 +1,140 @@ +import { INestApplication } from '@nestjs/common'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AppModule } from '../src/app.module'; +import { PrismaService } from '../src/prisma/prisma.service'; +import { TranManager } from '../src/prisma/transactionManager'; +import { CourseRepository } from '../src/prisma/repositories/course.repository'; +import { ECourse } from '@src/common/entities/ECourse'; +import { CoursesService } from '@src/modules/courses/courses.service'; +import { session_userprofile } from '@prisma/client'; + +describe('AppController (e2e)', () => { + let app: INestApplication; + + beforeEach(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + }); + + it('tranManager', async () => { + const courseRepository = app.get(CourseRepository); + console.log('transaction test with tranManager'); + + await TranManager.transaction(async () => { + await courseRepository.getCourseById(10); + await courseRepository.getCourseById(11); + }); + }, 10000); + + it('tranManager multi request', async () => { + const courseRepository = app.get(CourseRepository); + console.log('transaction test with tranManager, multi request'); + + await Promise.all([ + TranManager.transaction(async () => { + console.log('batch 1'); + await courseRepository.getCourseById(10); + await courseRepository.getCourseById(11); + }), + TranManager.transaction(async () => { + console.log('batch 2'); + await courseRepository.getCourseById(12); + await courseRepository.getCourseById(13); + throw Error('test error'); + }), + ]); + }, 10000); + + it('TranManager multi request, propagation', async () => { + const courseRepository = app.get(CourseRepository); + console.log('transaction test with tranManager, multi request propagation'); + const prisma = app.get(PrismaService); + await Promise.all([ + TranManager.transaction(async () => { + console.log('batch 1'); + await TranManager.getTx().subject_course.findFirst({ + where: { + id: 10, + }, + }); + console.log(TranManager.getTx()); + await TranManager.getTx().subject_course.findFirst({ + where: { + id: 11, + }, + }); + console.log(TranManager.getTx()); + }), + TranManager.transaction(async () => { + console.log('batch 2'); + await TranManager.getTx().subject_course.findFirst({ + where: { + id: 12, + }, + }); + console.log(TranManager.getTx()); + + await TranManager.getTx().subject_course.findFirst({ + where: { + id: 13, + }, + }); + console.log(TranManager.getTx()); + + throw Error('test error'); + }), + ]); + }); + + it('$transaction', async () => { + console.log('transaction test with prismaService'); + const prismaService = app.get(PrismaService); + await prismaService.$transaction(async (tx) => { + await tx.subject_course.findUnique({ + include: ECourse.Details.include, + where: { + id: 10, + }, + }); + await tx.subject_course.findUnique({ + include: ECourse.Details.include, + where: { + id: 11, + }, + }); + }); + }, 10000); + + it('transaction test with cls', async () => { + console.log('transaction test with cls'); + const prismaService = app.get(PrismaService); + const coursesService = app.get(CoursesService); + const user: session_userprofile = + (await prismaService.session_userprofile.findUnique({ + where: { + id: 13933, + }, + })) as session_userprofile; + const courses = await coursesService.getCourses({ keyword: '기계' }, user); + }); + + it('transaction test with cls, multi request', async () => { + console.log('transaction test with cls, multi request'); + const prismaService = app.get(PrismaService); + const coursesService = app.get(CoursesService); + const user: session_userprofile = + (await prismaService.session_userprofile.findUnique({ + where: { + id: 13933, + }, + })) as session_userprofile; + await Promise.all([ + coursesService.getCourseByIds([10, 11], user), + coursesService.getCourseByIds([12, 13], user), + ]); + }); +}); diff --git a/test/typeTest.ts b/test/typeTest.ts new file mode 100644 index 00000000..eed19f68 --- /dev/null +++ b/test/typeTest.ts @@ -0,0 +1,33 @@ +import { Prisma, session_userprofile } from '@prisma/client'; +import { PrismaService } from '../src/prisma/prisma.service'; +import settings from '../src/settings'; + +const ormSettings = settings().ormconfig(); + +const prismaService = new PrismaService(); + +async function findReviewByUserTest(user: session_userprofile) { + return await this.prisma.review_review.findMany({ + where: { writer_id: user.id }, + include: { + course: { + include: { + subject_department: true, + subject_course_professors: { include: { professor: true } }, + lecture: true, + subject_courseuser: true, + }, + }, + lecture: { + include: { + subject_department: true, + subject_lecture_professors: { include: { professor: true } }, + subject_classtime: true, + subject_examtime: true, + }, + }, + }, + }); +} + +type ReviewDetails = Prisma.PromiseReturnType; diff --git a/tsconfig.build.json b/tsconfig.build.json index a576a685..462d24d5 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -1,4 +1,10 @@ { "extends": "./tsconfig.json", - "exclude": ["node_modules", "test", "dist", "**/*spec.ts","src/prisma/migrations"] + "exclude": [ + "node_modules", + "test", + "dist", + "**/*spec.ts", + "src/prisma/migrations" + ] } diff --git a/tsconfig.json b/tsconfig.json index 2a384126..787f9ee6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,10 +14,13 @@ "baseUrl": "./", "incremental": true, "skipLibCheck": true, - "strictNullChecks": false, - "noImplicitAny": false, - "strictBindCallApply": false, - "forceConsistentCasingInFileNames": false, - "noFallthroughCasesInSwitch": false + "strict": true, + "alwaysStrict": true, + "noImplicitAny": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": false, + "paths": { + "@src/*": ["src/*"] + } } }