diff --git a/.devcontainer/compose.yaml b/.devcontainer/compose.yaml index 51fc68c..4b80b4f 100644 --- a/.devcontainer/compose.yaml +++ b/.devcontainer/compose.yaml @@ -1,23 +1,23 @@ services: workspace: build: - context: './' - dockerfile: './Dockerfile' + context: "./" + dockerfile: "./Dockerfile" volumes: - - '..:/workspace:cached' - command: 'sleep infinity' - network_mode: 'host' + - "..:/workspace:cached" + command: "sleep infinity" + network_mode: "host" thream-database: - image: 'postgres:15.4' + image: "postgres:15.4" environment: - POSTGRES_USER: 'thream_user' - POSTGRES_PASSWORD: 'password' - POSTGRES_DB: 'thream' + POSTGRES_USER: "thream_user" + POSTGRES_PASSWORD: "password" + POSTGRES_DB: "thream" volumes: - - 'thream-postgres-data:/var/lib/postgresql/data' - restart: 'unless-stopped' - network_mode: 'host' + - "thream-postgres-data:/var/lib/postgresql/data" + restart: "unless-stopped" + network_mode: "host" volumes: thream-postgres-data: diff --git a/.github/ISSUE_TEMPLATE/BUG.md b/.github/ISSUE_TEMPLATE/BUG.md index e1fc7e7..d47b126 100644 --- a/.github/ISSUE_TEMPLATE/BUG.md +++ b/.github/ISSUE_TEMPLATE/BUG.md @@ -1,8 +1,8 @@ --- -name: '🐛 Bug Report' -about: 'Report an unexpected problem or unintended behavior.' -title: '[Bug]' -labels: 'bug' +name: "🐛 Bug Report" +about: "Report an unexpected problem or unintended behavior." +title: "[Bug]" +labels: "bug" --- diff --git a/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md b/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md index 3307418..dc9a49c 100644 --- a/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md +++ b/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md @@ -1,8 +1,8 @@ --- -name: '✨ Feature Request' -about: 'Suggest a new feature idea.' -title: '[Feature]' -labels: 'feature request' +name: "✨ Feature Request" +about: "Suggest a new feature idea." +title: "[Feature]" +labels: "feature request" --- diff --git a/.github/ISSUE_TEMPLATE/IMPROVEMENT.md b/.github/ISSUE_TEMPLATE/IMPROVEMENT.md index b5bdbb7..e50e289 100644 --- a/.github/ISSUE_TEMPLATE/IMPROVEMENT.md +++ b/.github/ISSUE_TEMPLATE/IMPROVEMENT.md @@ -1,8 +1,8 @@ --- -name: '🔧 Improvement' -about: 'Improve structure/format/performance/refactor/tests of the code.' -title: '[Improvement]' -labels: 'improvement' +name: "🔧 Improvement" +about: "Improve structure/format/performance/refactor/tests of the code." +title: "[Improvement]" +labels: "improvement" --- diff --git a/.github/ISSUE_TEMPLATE/QUESTION.md b/.github/ISSUE_TEMPLATE/QUESTION.md index c18eaa3..df6acdc 100644 --- a/.github/ISSUE_TEMPLATE/QUESTION.md +++ b/.github/ISSUE_TEMPLATE/QUESTION.md @@ -1,8 +1,8 @@ --- -name: '🙋 Question' -about: 'Further information is requested.' -title: '[Question]' -labels: 'question' +name: "🙋 Question" +about: "Further information is requested." +title: "[Question]" +labels: "question" --- ### Question diff --git a/.github/workflows/analyze.yml b/.github/workflows/analyze.yml index e92a73d..a238ef3 100644 --- a/.github/workflows/analyze.yml +++ b/.github/workflows/analyze.yml @@ -1,4 +1,4 @@ -name: 'Analyze' +name: "Analyze" on: push: @@ -8,20 +8,20 @@ on: jobs: analyze: - runs-on: 'ubuntu-latest' + runs-on: "ubuntu-latest" strategy: fail-fast: false matrix: - language: ['javascript'] + language: ["javascript"] steps: - - uses: 'actions/checkout@v4.0.0' + - uses: "actions/checkout@v4.0.0" - - name: 'Initialize CodeQL' - uses: 'github/codeql-action/init@v2' + - name: "Initialize CodeQL" + uses: "github/codeql-action/init@v2" with: languages: ${{ matrix.language }} - - name: 'Perform CodeQL Analysis' - uses: 'github/codeql-action/analyze@v1' + - name: "Perform CodeQL Analysis" + uses: "github/codeql-action/analyze@v1" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3d0b390..b587501 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,4 @@ -name: 'Build' +name: "Build" on: push: @@ -8,20 +8,20 @@ on: jobs: build: - runs-on: 'ubuntu-latest' + runs-on: "ubuntu-latest" steps: - - uses: 'actions/checkout@v4.0.0' + - uses: "actions/checkout@v4.0.0" - - name: 'Setup Node.js' - uses: 'actions/setup-node@v3.8.1' + - name: "Setup Node.js" + uses: "actions/setup-node@v3.8.1" with: - node-version: '20.x' - cache: 'npm' + node-version: "20.x" + cache: "npm" - - name: 'Install dependencies' - run: 'npm clean-install' + - name: "Install dependencies" + run: "npm clean-install" - - name: 'Build' - run: 'npm run build' + - name: "Build" + run: "npm run build" - - run: 'npm run build:typescript' + - run: "npm run build:typescript" diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index ea248db..da3cb0f 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,4 +1,4 @@ -name: 'Lint' +name: "Lint" on: push: @@ -8,33 +8,33 @@ on: jobs: lint: - runs-on: 'ubuntu-latest' + runs-on: "ubuntu-latest" steps: - - uses: 'actions/checkout@v4.0.0' + - uses: "actions/checkout@v4.0.0" - - name: 'Setup Node.js' - uses: 'actions/setup-node@v3.8.1' + - name: "Setup Node.js" + uses: "actions/setup-node@v3.8.1" with: - node-version: '20.x' - cache: 'npm' + node-version: "20.x" + cache: "npm" - - name: 'Install dependencies' - run: 'npm clean-install' + - name: "Install dependencies" + run: "npm clean-install" - - name: 'lint:commit' + - name: "lint:commit" run: 'npm run lint:commit -- --to "${{ github.sha }}"' - - name: 'lint:editorconfig' - run: 'npm run lint:editorconfig' + - name: "lint:editorconfig" + run: "npm run lint:editorconfig" - - name: 'lint:markdown' - run: 'npm run lint:markdown' + - name: "lint:markdown" + run: "npm run lint:markdown" - - name: 'lint:eslint' - run: 'npm run lint:eslint' + - name: "lint:eslint" + run: "npm run lint:eslint" - - name: 'lint:prettier' - run: 'npm run lint:prettier' + - name: "lint:prettier" + run: "npm run lint:prettier" - - name: 'prisma:validate' - run: 'cp .env.example .env && npm run prisma:validate' + - name: "prisma:validate" + run: "cp .env.example .env && npm run prisma:validate" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4aa5cb3..17648f1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,4 @@ -name: 'Release' +name: "Release" on: push: @@ -6,36 +6,36 @@ on: jobs: release: - runs-on: 'ubuntu-latest' + runs-on: "ubuntu-latest" steps: - - uses: 'actions/checkout@v4.0.0' + - uses: "actions/checkout@v4.0.0" with: fetch-depth: 0 persist-credentials: false - - name: 'Import GPG key' - uses: 'crazy-max/ghaction-import-gpg@v6.0.0' + - name: "Import GPG key" + uses: "crazy-max/ghaction-import-gpg@v6.0.0" with: gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} git_user_signingkey: true git_commit_gpgsign: true - - name: 'Setup Node.js' - uses: 'actions/setup-node@v3.8.1' + - name: "Setup Node.js" + uses: "actions/setup-node@v3.8.1" with: - node-version: '20.x' - cache: 'npm' + node-version: "20.x" + cache: "npm" - - name: 'Install dependencies' - run: 'npm clean-install' + - name: "Install dependencies" + run: "npm clean-install" - - name: 'Build' - run: 'npm run build' + - name: "Build" + run: "npm run build" - - run: 'npm run build:typescript' + - run: "npm run build:typescript" - - name: 'Release' - run: 'npm run release' + - name: "Release" + run: "npm run release" env: GH_TOKEN: ${{ secrets.GH_TOKEN }} GIT_COMMITTER_NAME: ${{ secrets.GIT_NAME }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f4d2732..f635b06 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,4 +1,4 @@ -name: 'Test' +name: "Test" on: push: @@ -8,23 +8,23 @@ on: jobs: test: - runs-on: 'ubuntu-latest' + runs-on: "ubuntu-latest" steps: - - uses: 'actions/checkout@v4.0.0' + - uses: "actions/checkout@v4.0.0" - - name: 'Setup Node.js' - uses: 'actions/setup-node@v3.8.1' + - name: "Setup Node.js" + uses: "actions/setup-node@v3.8.1" with: - node-version: '20.x' - cache: 'npm' + node-version: "20.x" + cache: "npm" - - name: 'Install dependencies' - run: 'npm clean-install' + - name: "Install dependencies" + run: "npm clean-install" - - name: 'Build' - run: 'npm run build' + - name: "Build" + run: "npm run build" - - run: 'cp .env.example .env' + - run: "cp .env.example .env" - - name: 'Test' - run: 'npm run test' + - name: "Test" + run: "npm run test" diff --git a/.prettierrc.json b/.prettierrc.json index 83ef1ce..cce9d3c 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,6 +1,3 @@ { - "singleQuote": true, - "jsxSingleQuote": true, - "semi": false, - "trailingComma": "none" + "semi": false } diff --git a/Dockerfile b/Dockerfile index fbabe09..5b1dfa6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,15 +1,15 @@ -FROM node:20.6.1 AS dependencies +FROM node:20.9.0 AS dependencies WORKDIR /usr/src/app COPY ./package*.json ./ RUN npm clean-install -FROM node:20.6.1 AS builder +FROM node:20.9.0 AS builder WORKDIR /usr/src/app COPY --from=dependencies /usr/src/app/node_modules ./node_modules COPY ./ ./ RUN npm run prisma:generate && npm run build -FROM node:20.6.1 AS runner +FROM node:20.9.0 AS runner WORKDIR /usr/src/app ENV NODE_ENV=production ENV NODE_OPTIONS=--enable-source-maps diff --git a/compose.yaml b/compose.yaml index 185db21..773a6bd 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,27 +1,27 @@ services: thream-api: - container_name: 'thream-api' - image: 'thream-api' - restart: 'unless-stopped' - network_mode: 'host' + container_name: "thream-api" + image: "thream-api" + restart: "unless-stopped" + network_mode: "host" build: - context: './' - env_file: '.env' + context: "./" + env_file: ".env" depends_on: - - 'thream-database' + - "thream-database" thream-database: - container_name: 'thream-database' - image: 'postgres:15.4' - restart: 'unless-stopped' - network_mode: 'host' - env_file: '.env' + container_name: "thream-database" + image: "postgres:15.4" + restart: "unless-stopped" + network_mode: "host" + env_file: ".env" environment: POSTGRES_USER: ${DATABASE_USER} POSTGRES_PASSWORD: ${DATABASE_PASSWORD} POSTGRES_DB: ${DATABASE_NAME} volumes: - - 'thream-postgres-data:/var/lib/postgresql/data' + - "thream-postgres-data:/var/lib/postgresql/data" volumes: thream-postgres-data: diff --git a/generators/service/index.js b/generators/service/index.js index 7ca2f1f..5ed1569 100644 --- a/generators/service/index.js +++ b/generators/service/index.js @@ -1,45 +1,45 @@ /** @type {import('node-plop').PlopGeneratorConfig} */ export const serviceGenerator = { - description: 'REST API endpoint', + description: "REST API endpoint", prompts: [ { - type: 'input', - name: 'url', - message: 'url' + type: "input", + name: "url", + message: "url", }, { - type: 'list', - name: 'httpMethod', - message: 'httpMethod', - choices: ['GET', 'POST', 'PUT', 'DELETE'] + type: "list", + name: "httpMethod", + message: "httpMethod", + choices: ["GET", "POST", "PUT", "DELETE"], }, { - type: 'input', - name: 'description', - message: 'description' + type: "input", + name: "description", + message: "description", }, { - type: 'list', - name: 'tag', - message: 'tag', - choices: ['users', 'oauth2', 'guilds', 'channels', 'messages', 'members'] + type: "list", + name: "tag", + message: "tag", + choices: ["users", "oauth2", "guilds", "channels", "messages", "members"], }, { - type: 'confirm', - name: 'shouldBeAuthenticated', - message: 'shouldBeAuthenticated' - } + type: "confirm", + name: "shouldBeAuthenticated", + message: "shouldBeAuthenticated", + }, ], actions: [ { - type: 'add', - path: 'src/services/{{url}}/{{lowerCase httpMethod}}.ts', - templateFile: 'generators/service/service.ts.hbs' + type: "add", + path: "src/services/{{url}}/{{lowerCase httpMethod}}.ts", + templateFile: "generators/service/service.ts.hbs", }, { - type: 'add', - path: 'src/services/{{url}}/__test__/{{lowerCase httpMethod}}.test.ts', - templateFile: 'generators/service/service.test.ts.hbs' - } - ] + type: "add", + path: "src/services/{{url}}/__test__/{{lowerCase httpMethod}}.test.ts", + templateFile: "generators/service/service.test.ts.hbs", + }, + ], } diff --git a/package-lock.json b/package-lock.json index 2eb7e24..0cc909e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,71 +9,71 @@ "version": "1.2.9", "hasInstallScript": true, "dependencies": { - "@fastify/cors": "8.3.0", + "@fastify/cors": "8.4.0", "@fastify/helmet": "11.1.1", - "@fastify/multipart": "7.7.3", + "@fastify/multipart": "8.0.0", "@fastify/rate-limit": "8.0.3", - "@fastify/sensible": "5.3.0", - "@fastify/swagger": "8.10.0", - "@fastify/swagger-ui": "1.9.3", - "@prisma/client": "5.3.1", - "@sinclair/typebox": "0.31.15", + "@fastify/sensible": "5.5.0", + "@fastify/swagger": "8.12.0", + "@fastify/swagger-ui": "1.10.1", + "@prisma/client": "5.4.2", + "@sinclair/typebox": "0.31.18", "@thream/socketio-jwt": "3.1.3", - "axios": "1.5.0", + "axios": "1.5.1", "bcryptjs": "2.4.3", "dotenv": "16.3.1", "ejs": "3.1.9", - "fastify": "4.23.2", + "fastify": "4.24.3", "fastify-plugin": "4.5.1", "form-data": "4.0.0", "http-errors": "2.0.0", "jsonwebtoken": "9.0.2", "ms": "2.1.3", - "nodemailer": "6.9.5", + "nodemailer": "6.9.7", "read-pkg": "8.1.0", "socket.io": "4.7.2" }, "devDependencies": { - "@commitlint/cli": "17.7.1", - "@commitlint/config-conventional": "17.7.0", - "@saithodev/semantic-release-backmerge": "3.2.0", + "@commitlint/cli": "18.0.0", + "@commitlint/config-conventional": "18.0.0", + "@saithodev/semantic-release-backmerge": "3.2.1", "@semantic-release/git": "10.0.1", "@swc/cli": "0.1.62", - "@swc/core": "1.3.85", + "@swc/core": "1.3.94", "@tsconfig/strictest": "2.0.2", - "@types/bcryptjs": "2.4.4", - "@types/busboy": "1.5.1", - "@types/ejs": "3.1.2", - "@types/http-errors": "2.0.2", - "@types/jsonwebtoken": "9.0.3", - "@types/ms": "0.7.31", - "@types/node": "20.6.2", - "@types/nodemailer": "6.4.10", - "@types/sinon": "10.0.16", - "@typescript-eslint/eslint-plugin": "6.7.2", - "@typescript-eslint/parser": "6.7.2", + "@types/bcryptjs": "2.4.5", + "@types/busboy": "1.5.2", + "@types/ejs": "3.1.4", + "@types/http-errors": "2.0.3", + "@types/jsonwebtoken": "9.0.4", + "@types/ms": "0.7.33", + "@types/node": "20.8.7", + "@types/nodemailer": "6.4.13", + "@types/sinon": "10.0.20", + "@typescript-eslint/eslint-plugin": "6.9.0", + "@typescript-eslint/parser": "6.9.0", "chokidar": "3.5.3", - "concurrently": "8.2.1", + "concurrently": "8.2.2", "cross-env": "7.0.3", "editorconfig-checker": "5.1.1", - "eslint": "8.49.0", - "eslint-config-conventions": "11.0.1", + "eslint": "8.52.0", + "eslint-config-conventions": "12.0.0", "eslint-config-prettier": "9.0.0", - "eslint-plugin-import": "2.28.1", - "eslint-plugin-prettier": "5.0.0", + "eslint-plugin-import": "2.29.0", + "eslint-plugin-prettier": "5.0.1", "eslint-plugin-promise": "6.1.1", "eslint-plugin-unicorn": "48.0.1", "husky": "8.0.3", - "lint-staged": "14.0.1", + "lint-staged": "15.0.2", "maildev": "2.1.0", "markdownlint-cli2": "0.10.0", "markdownlint-rule-relative-links": "2.1.0", "plop": "4.0.0", "prettier": "3.0.3", - "prisma": "5.3.1", - "rimraf": "5.0.1", + "prisma": "5.4.2", + "rimraf": "5.0.5", "semantic-release": "21.1.1", - "sinon": "16.0.0", + "sinon": "17.0.0", "typescript": "5.2.2" }, "engines": { @@ -252,9 +252,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.15.tgz", - "integrity": "sha512-T0O+aa+4w0u06iNmapipJXMV4HoUir03hpx3/YqXXhu9xim3w+dVphjFWl1OH8NbZHw5Lbm9k45drDkgq2VNNA==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", + "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", "dev": true, "dependencies": { "regenerator-runtime": "^0.14.0" @@ -274,16 +274,16 @@ } }, "node_modules/@commitlint/cli": { - "version": "17.7.1", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-17.7.1.tgz", - "integrity": "sha512-BCm/AT06SNCQtvFv921iNhudOHuY16LswT0R3OeolVGLk8oP+Rk9TfQfgjH7QPMjhvp76bNqGFEcpKojxUNW1g==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-18.0.0.tgz", + "integrity": "sha512-0HuJB7VfxSqia0r+mZ9VLgTc5gPaRDiQtloLfwCBp63PvpFn2sZsidiZBbym3jDC6+P47HSuIdRRg3/JsCcBwA==", "dev": true, "dependencies": { - "@commitlint/format": "^17.4.4", - "@commitlint/lint": "^17.7.0", - "@commitlint/load": "^17.7.1", - "@commitlint/read": "^17.5.1", - "@commitlint/types": "^17.4.4", + "@commitlint/format": "^18.0.0", + "@commitlint/lint": "^18.0.0", + "@commitlint/load": "^18.0.0", + "@commitlint/read": "^18.0.0", + "@commitlint/types": "^18.0.0", "execa": "^5.0.0", "lodash.isfunction": "^3.0.9", "resolve-from": "5.0.0", @@ -294,41 +294,41 @@ "commitlint": "cli.js" }, "engines": { - "node": ">=v14" + "node": ">=v18" } }, "node_modules/@commitlint/config-conventional": { - "version": "17.7.0", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-17.7.0.tgz", - "integrity": "sha512-iicqh2o6et+9kWaqsQiEYZzfLbtoWv9uZl8kbI8EGfnc0HeGafQBF7AJ0ylN9D/2kj6txltsdyQs8+2fTMwWEw==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-18.0.0.tgz", + "integrity": "sha512-XZW3MDwhMWwr7pf+jO5F18ohBwrJ8mt+1OPhkVyO8bYrY0dKPE5tPpdXlDt+JCFurl4VnUDPmu6xZ4/cKCH7cA==", "dev": true, "dependencies": { - "conventional-changelog-conventionalcommits": "^6.1.0" + "conventional-changelog-conventionalcommits": "^7.0.2" }, "engines": { - "node": ">=v14" + "node": ">=v18" } }, "node_modules/@commitlint/config-validator": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-17.6.7.tgz", - "integrity": "sha512-vJSncmnzwMvpr3lIcm0I8YVVDJTzyjy7NZAeXbTXy+MPUdAr9pKyyg7Tx/ebOQ9kqzE6O9WT6jg2164br5UdsQ==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-18.0.0.tgz", + "integrity": "sha512-PlXy5QZzQeMgQM7jb0odIhxsI6GWcbGgfy+Hkz5ap31KES/oJgtEvgD8pjg0Z9Ri296bT6zK3ts6brS0MAcMgg==", "dev": true, "dependencies": { - "@commitlint/types": "^17.4.4", + "@commitlint/types": "^18.0.0", "ajv": "^8.11.0" }, "engines": { - "node": ">=v14" + "node": ">=v18" } }, "node_modules/@commitlint/ensure": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-17.6.7.tgz", - "integrity": "sha512-mfDJOd1/O/eIb/h4qwXzUxkmskXDL9vNPnZ4AKYKiZALz4vHzwMxBSYtyL2mUIDeU9DRSpEUins8SeKtFkYHSw==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-18.0.0.tgz", + "integrity": "sha512-AhzK4ybWGVmO7lwC33dGujS0k2IZDNFiew80M1H2R5Ou7Qkqq7sbuMZoof+yjOQcqltYP4IHLd0YECZoGermvQ==", "dev": true, "dependencies": { - "@commitlint/types": "^17.4.4", + "@commitlint/types": "^18.0.0", "lodash.camelcase": "^4.3.0", "lodash.kebabcase": "^4.1.1", "lodash.snakecase": "^4.1.1", @@ -336,70 +336,70 @@ "lodash.upperfirst": "^4.3.1" }, "engines": { - "node": ">=v14" + "node": ">=v18" } }, "node_modules/@commitlint/execute-rule": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-17.4.0.tgz", - "integrity": "sha512-LIgYXuCSO5Gvtc0t9bebAMSwd68ewzmqLypqI2Kke1rqOqqDbMpYcYfoPfFlv9eyLIh4jocHWwCK5FS7z9icUA==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-18.0.0.tgz", + "integrity": "sha512-eNUSaHajb+g3sgZeIrfc6cXNnKIkYN2SXtDVXuiE+hOa055T0bLdZK29gSd945JCztxPVwdOkPLDeLg3NfDubg==", "dev": true, "engines": { - "node": ">=v14" + "node": ">=v18" } }, "node_modules/@commitlint/format": { - "version": "17.4.4", - "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-17.4.4.tgz", - "integrity": "sha512-+IS7vpC4Gd/x+uyQPTAt3hXs5NxnkqAZ3aqrHd5Bx/R9skyCAWusNlNbw3InDbAK6j166D9asQM8fnmYIa+CXQ==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-18.0.0.tgz", + "integrity": "sha512-etvUmOufihecdNm0r5+rCetrO2Yj7fSkJ4f0k8xOzqrrNdID9G/6iK14i/ufISBLSSy0XzsiiV+Rn9TX/cg46Q==", "dev": true, "dependencies": { - "@commitlint/types": "^17.4.4", + "@commitlint/types": "^18.0.0", "chalk": "^4.1.0" }, "engines": { - "node": ">=v14" + "node": ">=v18" } }, "node_modules/@commitlint/is-ignored": { - "version": "17.7.0", - "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-17.7.0.tgz", - "integrity": "sha512-043rA7m45tyEfW7Zv2vZHF++176MLHH9h70fnPoYlB1slKBeKl8BwNIlnPg4xBdRBVNPaCqvXxWswx2GR4c9Hw==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-18.0.0.tgz", + "integrity": "sha512-fAUOF4GlKTmreE5hxhLlVUcXd1aIexxn+edocbMSwOa4dcm8OoHbsLcDB/rhwXIK+jzK4DybCMCx1VaTeRC4SQ==", "dev": true, "dependencies": { - "@commitlint/types": "^17.4.4", + "@commitlint/types": "^18.0.0", "semver": "7.5.4" }, "engines": { - "node": ">=v14" + "node": ">=v18" } }, "node_modules/@commitlint/lint": { - "version": "17.7.0", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-17.7.0.tgz", - "integrity": "sha512-TCQihm7/uszA5z1Ux1vw+Nf3yHTgicus/+9HiUQk+kRSQawByxZNESeQoX9ujfVd3r4Sa+3fn0JQAguG4xvvbA==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-18.0.0.tgz", + "integrity": "sha512-Vqg6sIPm7nELu/U4lEzKMdLYaELgwSySGZzKc2YjJxdJWyiQo0b1hfCpxXsWGdNBOt0+CM4eBAGoX/Kjq5AYNA==", "dev": true, "dependencies": { - "@commitlint/is-ignored": "^17.7.0", - "@commitlint/parse": "^17.7.0", - "@commitlint/rules": "^17.7.0", - "@commitlint/types": "^17.4.4" + "@commitlint/is-ignored": "^18.0.0", + "@commitlint/parse": "^18.0.0", + "@commitlint/rules": "^18.0.0", + "@commitlint/types": "^18.0.0" }, "engines": { - "node": ">=v14" + "node": ">=v18" } }, "node_modules/@commitlint/load": { - "version": "17.7.1", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-17.7.1.tgz", - "integrity": "sha512-S/QSOjE1ztdogYj61p6n3UbkUvweR17FQ0zDbNtoTLc+Hz7vvfS7ehoTMQ27hPSjVBpp7SzEcOQu081RLjKHJQ==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-18.0.0.tgz", + "integrity": "sha512-ocvMSkzNZCJ4yV673xjd4Y7sFVG/mg7S6yvL5ioM0OIG2XTbcCdzpmq+BeJcIwsRYU9g/b688yh7RDzGlbai6w==", "dev": true, "dependencies": { - "@commitlint/config-validator": "^17.6.7", - "@commitlint/execute-rule": "^17.4.0", - "@commitlint/resolve-extends": "^17.6.7", - "@commitlint/types": "^17.4.4", - "@types/node": "20.4.7", + "@commitlint/config-validator": "^18.0.0", + "@commitlint/execute-rule": "^18.0.0", + "@commitlint/resolve-extends": "^18.0.0", + "@commitlint/types": "^18.0.0", + "@types/node": "^18.11.9", "chalk": "^4.1.0", "cosmiconfig": "^8.0.0", "cosmiconfig-typescript-loader": "^4.0.0", @@ -408,121 +408,121 @@ "lodash.uniq": "^4.5.0", "resolve-from": "^5.0.0", "ts-node": "^10.8.1", - "typescript": "^4.6.4 || ^5.0.0" + "typescript": "^5.2.2" }, "engines": { - "node": ">=v14" + "node": ">=v18" } }, "node_modules/@commitlint/load/node_modules/@types/node": { - "version": "20.4.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.7.tgz", - "integrity": "sha512-bUBrPjEry2QUTsnuEjzjbS7voGWCc30W0qzgMf90GPeDGFRakvrz47ju+oqDAKCXLUCe39u57/ORMl/O/04/9g==", + "version": "18.18.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.6.tgz", + "integrity": "sha512-wf3Vz+jCmOQ2HV1YUJuCWdL64adYxumkrxtc+H1VUQlnQI04+5HtH+qZCOE21lBE7gIrt+CwX2Wv8Acrw5Ak6w==", "dev": true }, "node_modules/@commitlint/message": { - "version": "17.4.2", - "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-17.4.2.tgz", - "integrity": "sha512-3XMNbzB+3bhKA1hSAWPCQA3lNxR4zaeQAQcHj0Hx5sVdO6ryXtgUBGGv+1ZCLMgAPRixuc6en+iNAzZ4NzAa8Q==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-18.0.0.tgz", + "integrity": "sha512-FJmEBn81RMEZrG1E1BxmjNoe/Rz918IeIGJRe5YXxM7IXboFhKS69rHPttNjKwSS038t6GUsRUJrAWqZnA6nCw==", "dev": true, "engines": { - "node": ">=v14" + "node": ">=v18" } }, "node_modules/@commitlint/parse": { - "version": "17.7.0", - "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-17.7.0.tgz", - "integrity": "sha512-dIvFNUMCUHqq5Abv80mIEjLVfw8QNuA4DS7OWip4pcK/3h5wggmjVnlwGCDvDChkw2TjK1K6O+tAEV78oxjxag==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-18.0.0.tgz", + "integrity": "sha512-qbZYOCpIZzfFzV35s5ei3JkBG/8KUOyGYluAeaYIcYC9z0uKXEVK6O2F2P/KLvbTkgZ4Q9OOLDrrtoWzPb2pjg==", "dev": true, "dependencies": { - "@commitlint/types": "^17.4.4", + "@commitlint/types": "^18.0.0", "conventional-changelog-angular": "^6.0.0", - "conventional-commits-parser": "^4.0.0" + "conventional-commits-parser": "^5.0.0" }, "engines": { - "node": ">=v14" + "node": ">=v18" } }, "node_modules/@commitlint/read": { - "version": "17.5.1", - "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-17.5.1.tgz", - "integrity": "sha512-7IhfvEvB//p9aYW09YVclHbdf1u7g7QhxeYW9ZHSO8Huzp8Rz7m05aCO1mFG7G8M+7yfFnXB5xOmG18brqQIBg==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-18.0.0.tgz", + "integrity": "sha512-VclRvG+ZvHOjYP4rmKPfNwS7eSI5m68D/CPtT2do/D4EBljmLBwqCYVL1I/3y2E6Fbub1GfYoXiRHThd1cVlag==", "dev": true, "dependencies": { - "@commitlint/top-level": "^17.4.0", - "@commitlint/types": "^17.4.4", + "@commitlint/top-level": "^18.0.0", + "@commitlint/types": "^18.0.0", "fs-extra": "^11.0.0", "git-raw-commits": "^2.0.11", "minimist": "^1.2.6" }, "engines": { - "node": ">=v14" + "node": ">=v18" } }, "node_modules/@commitlint/resolve-extends": { - "version": "17.6.7", - "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-17.6.7.tgz", - "integrity": "sha512-PfeoAwLHtbOaC9bGn/FADN156CqkFz6ZKiVDMjuC2N5N0740Ke56rKU7Wxdwya8R8xzLK9vZzHgNbuGhaOVKIg==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-18.0.0.tgz", + "integrity": "sha512-MD9+6GSiWvqgdJtfos+1gqz+zmy2vV7TbUVz2ETZzpfECgmUZSZSYzyivivBAQK6feS71KxmMLL8+YFF9+FFRQ==", "dev": true, "dependencies": { - "@commitlint/config-validator": "^17.6.7", - "@commitlint/types": "^17.4.4", + "@commitlint/config-validator": "^18.0.0", + "@commitlint/types": "^18.0.0", "import-fresh": "^3.0.0", "lodash.mergewith": "^4.6.2", "resolve-from": "^5.0.0", "resolve-global": "^1.0.0" }, "engines": { - "node": ">=v14" + "node": ">=v18" } }, "node_modules/@commitlint/rules": { - "version": "17.7.0", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-17.7.0.tgz", - "integrity": "sha512-J3qTh0+ilUE5folSaoK91ByOb8XeQjiGcdIdiB/8UT1/Rd1itKo0ju/eQVGyFzgTMYt8HrDJnGTmNWwcMR1rmA==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-18.0.0.tgz", + "integrity": "sha512-J7xjMKC4H1hfZR8fmPPuV35wS8Vgy3YX9xIpfPJhsDnRk2BL/dyZjNEXPdvlbpvkNgkfIWi0A0IDtW1dDoViag==", "dev": true, "dependencies": { - "@commitlint/ensure": "^17.6.7", - "@commitlint/message": "^17.4.2", - "@commitlint/to-lines": "^17.4.0", - "@commitlint/types": "^17.4.4", + "@commitlint/ensure": "^18.0.0", + "@commitlint/message": "^18.0.0", + "@commitlint/to-lines": "^18.0.0", + "@commitlint/types": "^18.0.0", "execa": "^5.0.0" }, "engines": { - "node": ">=v14" + "node": ">=v18" } }, "node_modules/@commitlint/to-lines": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-17.4.0.tgz", - "integrity": "sha512-LcIy/6ZZolsfwDUWfN1mJ+co09soSuNASfKEU5sCmgFCvX5iHwRYLiIuoqXzOVDYOy7E7IcHilr/KS0e5T+0Hg==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-18.0.0.tgz", + "integrity": "sha512-HwwBvzXX+3x7XYvBrEKy69S9w/clUEL0gMwkXTtg5awUpvEvzmTz1FBE/5WIiUICyOGdH1NhV1WgKb7UwOnI4A==", "dev": true, "engines": { - "node": ">=v14" + "node": ">=v18" } }, "node_modules/@commitlint/top-level": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-17.4.0.tgz", - "integrity": "sha512-/1loE/g+dTTQgHnjoCy0AexKAEFyHsR2zRB4NWrZ6lZSMIxAhBJnmCqwao7b4H8888PsfoTBCLBYIw8vGnej8g==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-18.0.0.tgz", + "integrity": "sha512-6+jjZhe9pF7ioD9IR2HChteTh95KPXa7GUUV52e0YhEsFArkCnyhzSL72ko1c2Xad0sWNBL0e76mifi20wD/Bg==", "dev": true, "dependencies": { "find-up": "^5.0.0" }, "engines": { - "node": ">=v14" + "node": ">=v18" } }, "node_modules/@commitlint/types": { - "version": "17.4.4", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-17.4.4.tgz", - "integrity": "sha512-amRN8tRLYOsxRr6mTnGGGvB5EmW/4DDjLMgiwK3CCVEmN6Sr/6xePGEpWaspKkckILuUORCwe6VfDBw6uj4axQ==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-18.0.0.tgz", + "integrity": "sha512-FDzAdSm7kIir0NW0bZLENdrEgf/9Ihs1AAqE9DK9R+dRFby4ookkxPMaz7elZmG+e5rBl7hGrWJzJINqG9cDDg==", "dev": true, "dependencies": { "chalk": "^4.1.0" }, "engines": { - "node": ">=v14" + "node": ">=v18" } }, "node_modules/@cspotcode/source-map-support": { @@ -553,9 +553,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.1.tgz", - "integrity": "sha512-PWiOzLIUAjN/w5K17PoF4n6sKBw0gqLHPhywmYHP4t1VFQQVYeb1yWsJwnMVEMl3tUHME7X/SJPZLmtG7XBDxQ==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.9.1.tgz", + "integrity": "sha512-Y27x+MBLjXa+0JWDhykM3+JE+il3kHKAEqabfEWq3SDhZjLYb6/BHL/JKFnH3fe207JaXkyDo685Oc2Glt6ifA==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" @@ -607,9 +607,9 @@ "dev": true }, "node_modules/@eslint/js": { - "version": "8.49.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.49.0.tgz", - "integrity": "sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w==", + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.52.0.tgz", + "integrity": "sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -645,9 +645,9 @@ } }, "node_modules/@fastify/cors": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-8.3.0.tgz", - "integrity": "sha512-oj9xkka2Tg0MrwuKhsSUumcAkfp2YCnKxmFEusi01pjk1YrdDsuSYTHXEelWNW+ilSy/ApZq0c2SvhKrLX0H1g==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-8.4.0.tgz", + "integrity": "sha512-MlVvMTenltToByTpLwlWtO+7dQ3l2J+1OpmGrx9JpSNWo1d+dhfNCOi23zHhxdFhtpDzfwGwCsKu9DTeG7k7nQ==", "dependencies": { "fastify-plugin": "^4.0.0", "mnemonist": "0.39.5" @@ -659,9 +659,9 @@ "integrity": "sha512-J8TOSBq3SoZbDhM9+R/u77hP93gz/rajSA+K2kGyijPpORPWUXHUpTaleoj+92As0S9uPRP7Oi8IqMf0u+ro6A==" }, "node_modules/@fastify/error": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.3.0.tgz", - "integrity": "sha512-dj7vjIn1Ar8sVXj2yAXiMNCJDmS9MQ9XMlIecX2dIzzhjSHCyKo4DdXjXMs7wKW2kj6yvVRSpuQjOZ3YLrh56w==" + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.4.0.tgz", + "integrity": "sha512-e/mafFwbK3MNqxUcFBLgHhgxsF8UT1m8aj0dAlqEa2nJEgPsRtpHTZ3ObgrgkZ2M1eJHPTwgyUl/tXkvabsZdQ==" }, "node_modules/@fastify/fast-json-stringify-compiler": { "version": "4.3.0", @@ -681,16 +681,15 @@ } }, "node_modules/@fastify/multipart": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/@fastify/multipart/-/multipart-7.7.3.tgz", - "integrity": "sha512-MG4Gd9FNEXc8qx0OgqoXM10EGO/dN/0iVQ8SrpFMU3d6F6KUfcqD2ZyoQhkm9LWrbiMgdHv5a43x78lASdn5GA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@fastify/multipart/-/multipart-8.0.0.tgz", + "integrity": "sha512-xaH1pGIqYnIJjYs5qG6ryhPSFnWuJIfSXYqEUtzmcyREkMk0SwONd2y+SZ9JXfDmETAC/Ogtc/SRbz+AjZhCkw==", "dependencies": { "@fastify/busboy": "^1.0.0", "@fastify/deepmerge": "^1.0.0", "@fastify/error": "^3.0.0", "@fastify/swagger": "^8.3.1", "@fastify/swagger-ui": "^1.8.0", - "end-of-stream": "^1.4.4", "fastify-plugin": "^4.0.0", "secure-json-parse": "^2.4.0", "stream-wormhole": "^1.1.0" @@ -719,9 +718,9 @@ } }, "node_modules/@fastify/sensible": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@fastify/sensible/-/sensible-5.3.0.tgz", - "integrity": "sha512-/7n8kMO6G7up05MOohZ7OX3u2pUGdfOU8ai2XzY3TNSguVYUcQfyu+A0On41ijxSKo9Z9US5OMfEy+KITF4RQQ==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@fastify/sensible/-/sensible-5.5.0.tgz", + "integrity": "sha512-D0zpl+nocsRXLceSbc4gasQaO3ZNQR4dy9Uu8Ym0mh8VUdrjpZ4g8Ca9O3pGXbBVOnPIGHUJNTV7Yf9dg/OYdg==", "dependencies": { "@lukeed/ms": "^2.0.1", "fast-deep-equal": "^3.1.1", @@ -746,9 +745,9 @@ } }, "node_modules/@fastify/swagger": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/@fastify/swagger/-/swagger-8.10.0.tgz", - "integrity": "sha512-0o6nd0qWpJbVSv/vbK4bzHSYe7l+PTGPqrQVwWIXVGd7CvXr585SBx+h8EgrMOY80bcOnGreqnjYFOV0osGP5A==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/@fastify/swagger/-/swagger-8.12.0.tgz", + "integrity": "sha512-IMRc0xYuzRvtFDMuaWHyVbvM7CuAi0g3o2jaVgLDvETXPrXWAMWsHYR5niIdWBDPgGUq+soHkag1DKXyhPDB0w==", "dependencies": { "fastify-plugin": "^4.0.0", "json-schema-resolver": "^2.0.0", @@ -758,9 +757,9 @@ } }, "node_modules/@fastify/swagger-ui": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@fastify/swagger-ui/-/swagger-ui-1.9.3.tgz", - "integrity": "sha512-YYqce4CydjDIEry6Zo4JLjVPe5rjS8iGnk3fHiIQnth9sFSLeyG0U1DCH+IyYmLddNDg1uWJOuErlVqnu/jI3w==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@fastify/swagger-ui/-/swagger-ui-1.10.1.tgz", + "integrity": "sha512-u3EJqNKvVr3X+6jY5i6pbs6/tXCrSlqc2Y+PVjnHBTOGh/d36uHMz+z4jPFy9gie2my6iHUrAdM8itlVmoUjog==", "dependencies": { "@fastify/static": "^6.0.0", "fastify-plugin": "^4.0.0", @@ -770,12 +769,12 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.11", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", - "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==", + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", + "@humanwhocodes/object-schema": "^2.0.1", "debug": "^4.1.1", "minimatch": "^3.0.5" }, @@ -797,9 +796,9 @@ } }, "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.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", "dev": true }, "node_modules/@isaacs/cliui": { @@ -872,10 +871,13 @@ } }, "node_modules/@ljharb/through": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.9.tgz", - "integrity": "sha512-yN599ZBuMPPK4tdoToLlvgJB4CLK8fGl7ntfy0Wn7U6ttNvHYurd81bfUiK/6sMkiIwm65R6ck4L6+Y3DfVbNQ==", + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.11.tgz", + "integrity": "sha512-ccfcIDlogiXNq5KcbAwbaO7lMh3Tm1i3khMPYpxlK8hH/W53zN81KM9coerRLOnTGu3nfXIniAmQbRI9OxbC0w==", "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, "engines": { "node": ">= 0.4" } @@ -952,16 +954,16 @@ } }, "node_modules/@octokit/core": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.0.0.tgz", - "integrity": "sha512-YbAtMWIrbZ9FCXbLwT9wWB8TyLjq9mxpKdgB3dUNxQcIVTf9hJ70gRPwAcqGZdY6WdJPZ0I7jLaaNDCiloGN2A==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.0.1.tgz", + "integrity": "sha512-lyeeeZyESFo+ffI801SaBKmCfsvarO+dgV8/0gD8u1d87clbEdWsP5yC+dSj3zLhb2eIf5SJrn6vDz9AheETHw==", "dev": true, "dependencies": { "@octokit/auth-token": "^4.0.0", "@octokit/graphql": "^7.0.0", "@octokit/request": "^8.0.2", "@octokit/request-error": "^5.0.0", - "@octokit/types": "^11.0.0", + "@octokit/types": "^12.0.0", "before-after-hook": "^2.2.0", "universal-user-agent": "^6.0.0" }, @@ -970,12 +972,12 @@ } }, "node_modules/@octokit/endpoint": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.0.tgz", - "integrity": "sha512-szrQhiqJ88gghWY2Htt8MqUDO6++E/EIXqJ2ZEp5ma3uGS46o7LZAzSLt49myB7rT+Hfw5Y6gO3LmOxGzHijAQ==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.1.tgz", + "integrity": "sha512-hRlOKAovtINHQPYHZlfyFwaM8OyetxeoC81lAkBy34uLb8exrZB50SQdeW3EROqiY9G9yxQTpp5OHTV54QD+vA==", "dev": true, "dependencies": { - "@octokit/types": "^11.0.0", + "@octokit/types": "^12.0.0", "is-plain-object": "^5.0.0", "universal-user-agent": "^6.0.0" }, @@ -984,13 +986,13 @@ } }, "node_modules/@octokit/graphql": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.0.1.tgz", - "integrity": "sha512-T5S3oZ1JOE58gom6MIcrgwZXzTaxRnxBso58xhozxHpOqSTgDS6YNeEUvZ/kRvXgPrRz/KHnZhtb7jUMRi9E6w==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.0.2.tgz", + "integrity": "sha512-OJ2iGMtj5Tg3s6RaXH22cJcxXRi7Y3EBqbHTBRq+PQAqfaS8f/236fUrWhfSn8P4jovyzqucxme7/vWSSZBX2Q==", "dev": true, "dependencies": { "@octokit/request": "^8.0.1", - "@octokit/types": "^11.0.0", + "@octokit/types": "^12.0.0", "universal-user-agent": "^6.0.0" }, "engines": { @@ -998,18 +1000,18 @@ } }, "node_modules/@octokit/openapi-types": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-18.0.0.tgz", - "integrity": "sha512-V8GImKs3TeQRxRtXFpG2wl19V7444NIOTDF24AWuIbmNaNYOQMWRbjcGDXV5B+0n887fgDcuMNOmlul+k+oJtw==", + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-19.0.0.tgz", + "integrity": "sha512-PclQ6JGMTE9iUStpzMkwLCISFn/wDeRjkZFIKALpvJQNBGwDoYYi2fFvuHwssoQ1rXI5mfh6jgTgWuddeUzfWw==", "dev": true }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-8.0.0.tgz", - "integrity": "sha512-2xZ+baZWUg+qudVXnnvXz7qfrTmDeYPCzangBVq/1gXxii/OiS//4shJp9dnCCvj1x+JAm9ji1Egwm1BA47lPQ==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.0.0.tgz", + "integrity": "sha512-oIJzCpttmBTlEhBmRvb+b9rlnGpmFgDtZ0bB6nq39qIod6A5DP+7RkVLMOixIgRCYSHDTeayWqmiJ2SZ6xgfdw==", "dev": true, "dependencies": { - "@octokit/types": "^11.0.0" + "@octokit/types": "^12.0.0" }, "engines": { "node": ">= 18" @@ -1019,13 +1021,13 @@ } }, "node_modules/@octokit/plugin-retry": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-6.0.0.tgz", - "integrity": "sha512-a1/A4A+PB1QoAHQfLJxGHhLfSAT03bR1jJz3GgQJZvty2ozawFWs93MiBQXO7SL2YbO7CIq0Goj4qLOBj8JeMQ==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-6.0.1.tgz", + "integrity": "sha512-SKs+Tz9oj0g4p28qkZwl/topGcb0k0qPNX/i7vBKmDsjoeqnVfFUquqrE/O9oJY7+oLzdCtkiWSXLpLjvl6uog==", "dev": true, "dependencies": { "@octokit/request-error": "^5.0.0", - "@octokit/types": "^11.0.0", + "@octokit/types": "^12.0.0", "bottleneck": "^2.15.3" }, "engines": { @@ -1036,12 +1038,12 @@ } }, "node_modules/@octokit/plugin-throttling": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-7.0.0.tgz", - "integrity": "sha512-KL2k/d0uANc8XqP5S64YcNFCudR3F5AaKO39XWdUtlJIjT9Ni79ekWJ6Kj5xvAw87udkOMEPcVf9xEge2+ahew==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-8.0.1.tgz", + "integrity": "sha512-i373s7TgaoAOlzOepjUTvyMXqjBu9b26SvLyLD5onBdgexIOeu43yOH1e3z3VPAzbEyRfKDHcqfAsOyKl7Jtxg==", "dev": true, "dependencies": { - "@octokit/types": "^11.0.0", + "@octokit/types": "^12.0.0", "bottleneck": "^2.15.3" }, "engines": { @@ -1052,14 +1054,14 @@ } }, "node_modules/@octokit/request": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.1.1.tgz", - "integrity": "sha512-8N+tdUz4aCqQmXl8FpHYfKG9GelDFd7XGVzyN8rc6WxVlYcfpHECnuRkgquzz+WzvHTK62co5di8gSXnzASZPQ==", + "version": "8.1.4", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.1.4.tgz", + "integrity": "sha512-M0aaFfpGPEKrg7XoA/gwgRvc9MSXHRO2Ioki1qrPDbl1e9YhjIwVoHE7HIKmv/m3idzldj//xBujcFNqGX6ENA==", "dev": true, "dependencies": { "@octokit/endpoint": "^9.0.0", "@octokit/request-error": "^5.0.0", - "@octokit/types": "^11.1.0", + "@octokit/types": "^12.0.0", "is-plain-object": "^5.0.0", "universal-user-agent": "^6.0.0" }, @@ -1068,12 +1070,12 @@ } }, "node_modules/@octokit/request-error": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.0.0.tgz", - "integrity": "sha512-1ue0DH0Lif5iEqT52+Rf/hf0RmGO9NWFjrzmrkArpG9trFfDM/efx00BJHdLGuro4BR/gECxCU2Twf5OKrRFsQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.0.1.tgz", + "integrity": "sha512-X7pnyTMV7MgtGmiXBwmO6M5kIPrntOXdyKZLigNfQWSEQzVxR4a4vo49vJjTWX70mPndj8KhfT4Dx+2Ng3vnBQ==", "dev": true, "dependencies": { - "@octokit/types": "^11.0.0", + "@octokit/types": "^12.0.0", "deprecation": "^2.0.0", "once": "^1.4.0" }, @@ -1082,12 +1084,12 @@ } }, "node_modules/@octokit/types": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-11.1.0.tgz", - "integrity": "sha512-Fz0+7GyLm/bHt8fwEqgvRBWwIV1S6wRRyq+V6exRKLVWaKGsuy6H9QFYeBVDV7rK6fO3XwHgQOPxv+cLj2zpXQ==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.0.0.tgz", + "integrity": "sha512-EzD434aHTFifGudYAygnFlS1Tl6KhbTynEWELQXIbTY8Msvb5nEqTZIm7sbPEt4mQYLZwu3zPKVdeIrw0g7ovg==", "dev": true, "dependencies": { - "@octokit/openapi-types": "^18.0.0" + "@octokit/openapi-types": "^19.0.0" } }, "node_modules/@pkgjs/parseargs": { @@ -1162,12 +1164,12 @@ } }, "node_modules/@prisma/client": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.3.1.tgz", - "integrity": "sha512-ArOKjHwdFZIe1cGU56oIfy7wRuTn0FfZjGuU/AjgEBOQh+4rDkB6nF+AGHP8KaVpkBIiHGPQh3IpwQ3xDMdO0Q==", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.4.2.tgz", + "integrity": "sha512-2xsPaz4EaMKj1WS9iW6MlPhmbqtBsXAOeVttSePp8vTFTtvzh2hZbDgswwBdSCgPzmmwF+tLB259QzggvCmJqA==", "hasInstallScript": true, "dependencies": { - "@prisma/engines-version": "5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59" + "@prisma/engines-version": "5.4.1-2.ac9d7041ed77bcc8a8dbd2ab6616b39013829574" }, "engines": { "node": ">=16.13" @@ -1182,21 +1184,21 @@ } }, "node_modules/@prisma/engines": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.3.1.tgz", - "integrity": "sha512-6QkILNyfeeN67BNEPEtkgh3Xo2tm6D7V+UhrkBbRHqKw9CTaz/vvTP/ROwYSP/3JT2MtIutZm/EnhxUiuOPVDA==", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.4.2.tgz", + "integrity": "sha512-fqeucJ3LH0e1eyFdT0zRx+oETLancu5+n4lhiYECyEz6H2RDskPJHJYHkVc0LhkU4Uv7fuEnppKU3nVKNzMh8g==", "devOptional": true, "hasInstallScript": true }, "node_modules/@prisma/engines-version": { - "version": "5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.3.1-2.61e140623197a131c2a6189271ffee05a7aa9a59.tgz", - "integrity": "sha512-y5qbUi3ql2Xg7XraqcXEdMHh0MocBfnBzDn5GbV1xk23S3Mq8MGs+VjacTNiBh3dtEdUERCrUUG7Z3QaJ+h79w==" + "version": "5.4.1-2.ac9d7041ed77bcc8a8dbd2ab6616b39013829574", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.4.1-2.ac9d7041ed77bcc8a8dbd2ab6616b39013829574.tgz", + "integrity": "sha512-wvupDL4AA1vf4TQNANg7kR7y98ITqPsk6aacfBxZKtrJKRIsWjURHkZCGcQliHdqCiW/hGreO6d6ZuSv9MhdAA==" }, "node_modules/@saithodev/semantic-release-backmerge": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@saithodev/semantic-release-backmerge/-/semantic-release-backmerge-3.2.0.tgz", - "integrity": "sha512-EAdt4eKvOnOg0p5DPW7w0cg0k58IpFuAHY/pqKZUF2uKQ/HkzHqIhteNeOCoxVOmLs4F0Lp5F6J02I9QDCGZRg==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@saithodev/semantic-release-backmerge/-/semantic-release-backmerge-3.2.1.tgz", + "integrity": "sha512-mC9cA6lhNzpy4rKOT5jjRLhlT3myubmVeo+BqWyR2WqF5PRJpG0jaU8pBC87S11GCUHKxqXf0QQDZNPbbNP1bA==", "dev": true, "dependencies": { "@semantic-release/error": "^3.0.0", @@ -1204,7 +1206,7 @@ "debug": "^4.3.4", "execa": "^5.1.1", "lodash": "^4.17.21", - "semantic-release": ">=20.0.0" + "semantic-release": ">=20.0.0 <22.0.0" } }, "node_modules/@semantic-release/commit-analyzer": { @@ -1228,69 +1230,6 @@ "semantic-release": ">=20.1.0" } }, - "node_modules/@semantic-release/commit-analyzer/node_modules/conventional-commits-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", - "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==", - "dev": true, - "dependencies": { - "is-text-path": "^2.0.0", - "JSONStream": "^1.3.5", - "meow": "^12.0.1", - "split2": "^4.0.0" - }, - "bin": { - "conventional-commits-parser": "cli.mjs" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@semantic-release/commit-analyzer/node_modules/is-text-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz", - "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==", - "dev": true, - "dependencies": { - "text-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@semantic-release/commit-analyzer/node_modules/meow": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", - "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", - "dev": true, - "engines": { - "node": ">=16.10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@semantic-release/commit-analyzer/node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "dev": true, - "engines": { - "node": ">= 10.x" - } - }, - "node_modules/@semantic-release/commit-analyzer/node_modules/text-extensions": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz", - "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@semantic-release/error": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@semantic-release/error/-/error-3.0.0.tgz", @@ -1323,15 +1262,15 @@ } }, "node_modules/@semantic-release/github": { - "version": "9.0.6", - "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-9.0.6.tgz", - "integrity": "sha512-GBGt9c3c2UdSvso4jcyQQSUpZA9hbfHqGQerZKN9WvVzCIkaBy8xkhOyiFVX08LjRHHT/H221SJNBLtuihX5iw==", + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-9.2.1.tgz", + "integrity": "sha512-fEn9uOe6jwWR6ro2Wh6YNBCBuZ5lRi8Myz+1j3KDTSt8OuUGlpVM4lFac/0bDrql2NOKrIEAMGCfWb9WMIdzIg==", "dev": true, "dependencies": { "@octokit/core": "^5.0.0", - "@octokit/plugin-paginate-rest": "^8.0.0", + "@octokit/plugin-paginate-rest": "^9.0.0", "@octokit/plugin-retry": "^6.0.0", - "@octokit/plugin-throttling": "^7.0.0", + "@octokit/plugin-throttling": "^8.0.0", "@semantic-release/error": "^4.0.0", "aggregate-error": "^5.0.0", "debug": "^4.3.4", @@ -1755,24 +1694,6 @@ "node": ">=16" } }, - "node_modules/@semantic-release/release-notes-generator/node_modules/conventional-commits-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", - "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==", - "dev": true, - "dependencies": { - "is-text-path": "^2.0.0", - "JSONStream": "^1.3.5", - "meow": "^12.0.1", - "split2": "^4.0.0" - }, - "bin": { - "conventional-commits-parser": "cli.mjs" - }, - "engines": { - "node": ">=16" - } - }, "node_modules/@semantic-release/release-notes-generator/node_modules/find-up": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", @@ -1801,18 +1722,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@semantic-release/release-notes-generator/node_modules/is-text-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz", - "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==", - "dev": true, - "dependencies": { - "text-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@semantic-release/release-notes-generator/node_modules/locate-path": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", @@ -1828,18 +1737,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@semantic-release/release-notes-generator/node_modules/meow": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", - "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", - "dev": true, - "engines": { - "node": ">=16.10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@semantic-release/release-notes-generator/node_modules/p-limit": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", @@ -1896,31 +1793,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@semantic-release/release-notes-generator/node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "dev": true, - "engines": { - "node": ">= 10.x" - } - }, - "node_modules/@semantic-release/release-notes-generator/node_modules/text-extensions": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz", - "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@semantic-release/release-notes-generator/node_modules/type-fest": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.3.1.tgz", - "integrity": "sha512-pphNW/msgOUSkJbH58x8sqpq8uQj6b0ZKGxEsLKMUnGorRcDjrUaLS+39+/ub41JNTwrrMyJcUB8+YZs3mbwqw==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.5.0.tgz", + "integrity": "sha512-diLQivFzddJl4ylL3jxSkEc39Tpw7o1QeEHIPxVwryDK2lpB7Nqhzhuo6v5/Ls08Z0yPSAhsyAWlv1/H0ciNmw==", "dev": true, "engines": { "node": ">=16" @@ -1942,9 +1818,9 @@ } }, "node_modules/@sinclair/typebox": { - "version": "0.31.15", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.31.15.tgz", - "integrity": "sha512-gheE0Z2QWB/EuUwirniP+vq17N0MdQ+9bKyy2lPJzcBin6piBxOrazTYOB18N+oeBwVVepAmlqqo9KbpSl9DOA==" + "version": "0.31.18", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.31.18.tgz", + "integrity": "sha512-p2JGz+SciGJVl1zokCIK15f7LYDrI2ZsxItcLhkAyx50hEYEj/Qdy7z30qRYiakzdIu8dV4DfBi+e6xEZuugiQ==" }, "node_modules/@sindresorhus/is": { "version": "4.6.0", @@ -1968,9 +1844,9 @@ } }, "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz", + "integrity": "sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw==", "dev": true, "dependencies": { "@sinonjs/commons": "^3.0.0" @@ -2039,13 +1915,14 @@ } }, "node_modules/@swc/core": { - "version": "1.3.85", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.85.tgz", - "integrity": "sha512-qnoxp+2O0GtvRdYnXgR1v8J7iymGGYpx6f6yCK9KxipOZOjrlKILFANYlghQxZyPUfXwK++TFxfSlX4r9wK+kg==", + "version": "1.3.94", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.94.tgz", + "integrity": "sha512-jTHn8UJOGgERKZLy8euEixVAzC/w/rUSuMlM3e7hxgap/TC595hSkuQwtkpL238dsuEPveD44GMy2A5UBtSvjg==", "dev": true, "hasInstallScript": true, "dependencies": { - "@swc/types": "^0.1.4" + "@swc/counter": "^0.1.1", + "@swc/types": "^0.1.5" }, "engines": { "node": ">=10" @@ -2055,16 +1932,16 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.3.85", - "@swc/core-darwin-x64": "1.3.85", - "@swc/core-linux-arm-gnueabihf": "1.3.85", - "@swc/core-linux-arm64-gnu": "1.3.85", - "@swc/core-linux-arm64-musl": "1.3.85", - "@swc/core-linux-x64-gnu": "1.3.85", - "@swc/core-linux-x64-musl": "1.3.85", - "@swc/core-win32-arm64-msvc": "1.3.85", - "@swc/core-win32-ia32-msvc": "1.3.85", - "@swc/core-win32-x64-msvc": "1.3.85" + "@swc/core-darwin-arm64": "1.3.94", + "@swc/core-darwin-x64": "1.3.94", + "@swc/core-linux-arm-gnueabihf": "1.3.94", + "@swc/core-linux-arm64-gnu": "1.3.94", + "@swc/core-linux-arm64-musl": "1.3.94", + "@swc/core-linux-x64-gnu": "1.3.94", + "@swc/core-linux-x64-musl": "1.3.94", + "@swc/core-win32-arm64-msvc": "1.3.94", + "@swc/core-win32-ia32-msvc": "1.3.94", + "@swc/core-win32-x64-msvc": "1.3.94" }, "peerDependencies": { "@swc/helpers": "^0.5.0" @@ -2076,9 +1953,9 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.3.85", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.85.tgz", - "integrity": "sha512-jTikp+i4nO4Ofe6qGm4I3sFeebD1OvueBCHITux5tQKD6umN1c2z4CRGv6K49NIz/qEpUcdr6Qny6K+3yibVFQ==", + "version": "1.3.94", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.94.tgz", + "integrity": "sha512-KNuE6opIy/wAXiGUWLhGWhCG3wA/AdjG6eYkv6dstrAURLaQMAoD8vDfVm8pxS8FA8Kx+0Z4QiDNPqk5aKIsqg==", "cpu": [ "arm64" ], @@ -2092,9 +1969,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.3.85", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.85.tgz", - "integrity": "sha512-3uHYkjVU+2F+YbVYtq5rH0uCJIztFTALaS3mQEfQUZKXZ5/8jD5titTCRqFKtSlQg0CzaFZgsYsuqwYBmgN0mA==", + "version": "1.3.94", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.94.tgz", + "integrity": "sha512-HypemhyehQrLqXwfJv5ronD4BMAXdgMCP4Ei7rt3B6Ftmt9axwGvdwGiXxsYR9h1ncyxoVxN+coGxbNIhKhahw==", "cpu": [ "x64" ], @@ -2108,9 +1985,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.3.85", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.85.tgz", - "integrity": "sha512-ouHzAHsFaEOkRuoTAOI/8n2m8BQAAnb4vr/xbMhhDOmix0lp5eNsW5Iac/EcJ2uG6B3n7P2K8oycj9SWkj+pfw==", + "version": "1.3.94", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.94.tgz", + "integrity": "sha512-KzKN54c7Y6X1db+bBVSXG4+bXmAPvXtDWk+TgwNJH4yYliOrnP/RKkHA5QZ9VFSnqJF06/sAO4kYBiL/aVQDBQ==", "cpu": [ "arm" ], @@ -2124,9 +2001,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.3.85", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.85.tgz", - "integrity": "sha512-/Z1CZOWiO+NqJEh1J20PIxQFHMH43upQJ1l7FJ5Z7+MyuYF8WkeJ7OSovau729pBR+38vvvccEJrMZIztfv7hQ==", + "version": "1.3.94", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.94.tgz", + "integrity": "sha512-iAcR8Ho0Uck/SLSrgYfXkpcGOXuN5waMZO7GlL/52QODr7GJtOfZ0H1MCZLbIFkPJp/iXoJpYgym4d/qSd477Q==", "cpu": [ "arm64" ], @@ -2140,9 +2017,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.3.85", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.85.tgz", - "integrity": "sha512-gfh7CfKavi076dbMBTzfdawSGcYfZ4+1Q+8aRkSesqepKHcIWIJti8Cf3zB4a6CHNhJe+VN0Gb7DEfumydAm1w==", + "version": "1.3.94", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.94.tgz", + "integrity": "sha512-VCHL1Mb9ENHx+sAeubSSg481MUeP9/PYzPPy9tfswunj/w35M+vEWflwK2dzQL9kUTFD3zcFTpAgsKnj6aX24w==", "cpu": [ "arm64" ], @@ -2156,9 +2033,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.3.85", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.85.tgz", - "integrity": "sha512-lWVqjHKzofb9q1qrBM4dLqO7CIisp08/xMS5Hz9DWex1gTc5F2b6yJO6Ceqwa256GMweJcdP6A5EvEFQAiZ5dg==", + "version": "1.3.94", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.94.tgz", + "integrity": "sha512-gjq7U6clhJi0Oel2a4gwR4MbSu+THQ2hmBNVCOSA3JjPZWZTkJXaJDpnh/r7PJxKBwUDlo0VPlwiwjepAQR2Rw==", "cpu": [ "x64" ], @@ -2172,9 +2049,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.3.85", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.85.tgz", - "integrity": "sha512-EPJmlfqC05TUetnlErxNRyIp7Nc3B2w9abET6oQ/EgldeAeQnZ3M6svMViET/c2QSomgrU3rdP+Qcozkt62/4A==", + "version": "1.3.94", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.94.tgz", + "integrity": "sha512-rSylruWyeol2ujZDHmwiovupMR5ukMXivlA7DDxmQ1dFUV9HuiPknQrU5rEbI3V2V3V5RkpbEKjnADen7AeMPQ==", "cpu": [ "x64" ], @@ -2188,9 +2065,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.3.85", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.85.tgz", - "integrity": "sha512-ibckJDZw8kNosciMexwk0z75ZyUhwtiFMV9rSBpup0opa7NNCUCoERCJ1e9LRyMdhsVUoLpZg/KZiHCdTw96hQ==", + "version": "1.3.94", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.94.tgz", + "integrity": "sha512-OenDUr5MQkz506ebVQq6ezoZ3GZ26nchgf5mPnwab4gx2TEiyR9zn7MdX5LWskTmOK3+FszPbGK0B5oLK6Y5yw==", "cpu": [ "arm64" ], @@ -2204,9 +2081,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.3.85", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.85.tgz", - "integrity": "sha512-hY4MpHGUVQHL1T2kgRXOigDho4DTIpVPYzJ4uyy8VQRbS7GzN5XtvdGP/fA4zp8+2BQjcig+6J7Y92SY15ouNQ==", + "version": "1.3.94", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.94.tgz", + "integrity": "sha512-mi6NcmtJKnaiHAxLtVz+WzunscsEwPdA0j15DuiYVx06Xo+MdRLJj4eVBgVLwGD1AI3IqKs4MVVx2cD7n0h5mg==", "cpu": [ "ia32" ], @@ -2220,9 +2097,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.3.85", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.85.tgz", - "integrity": "sha512-ktxWOMFJ0iqKn6WUHtXqi4CS7xkyHmrRtjllGRuGqxmLmDX/HSOfuQ55Tm1KXKk5oHLacJkUbOSF2kBrpZ8dpg==", + "version": "1.3.94", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.94.tgz", + "integrity": "sha512-Ba0ZLcGMnqPWWF9Xa+rWhhnkpvE7XoQegMP/VCF2JIHb2ieGBC8jChO6nKRFKZjib/3wghGzxakyDQx3LDhDug==", "cpu": [ "x64" ], @@ -2235,10 +2112,16 @@ "node": ">=10" } }, + "node_modules/@swc/counter": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.2.tgz", + "integrity": "sha512-9F4ys4C74eSTEUNndnER3VJ15oru2NumfQxS8geE+f3eB5xvfxpWyqE5XlVnxb/R14uoXi6SLbBwwiDSkv+XEw==", + "dev": true + }, "node_modules/@swc/types": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.4.tgz", - "integrity": "sha512-z/G02d+59gyyUb7KYhKi9jOhicek6QD2oMaotUyG+lUkybpXoV49dY9bj7Ah5Q+y7knK2jU67UTX9FyfGzaxQg==", + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz", + "integrity": "sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==", "dev": true }, "node_modules/@szmarczak/http-timer": { @@ -2314,15 +2197,15 @@ "dev": true }, "node_modules/@types/bcryptjs": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.4.tgz", - "integrity": "sha512-9wlJI7k5gRyJEC4yrV7DubzNQFTPiykYxUA6lBtsk5NlOfW9oWLJ1HdIA4YtE+6C3i3mTpDQQEosJ2rVZfBWnw==", + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.5.tgz", + "integrity": "sha512-tOF6TivOIvq+TWQm78335CMdyVJhpBG3NUdWQDAp95ax4E2rSKbws/ELHLk5EBoucwx/tHt3/hhLOHwWJgVrSw==", "dev": true }, "node_modules/@types/busboy": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@types/busboy/-/busboy-1.5.1.tgz", - "integrity": "sha512-JAymE2skNionWnBUwby3MatzPUw4D/6/7FX1qxBXLzmRnFxmqU0luIof7om0I8R3B/rSr9FKUnFCqxZ/NeGbrw==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@types/busboy/-/busboy-1.5.2.tgz", + "integrity": "sha512-OYkRy+dkQWwoGch3oyQ9UE08PO1jSte7aBtPemLXAP9evwh7fNAfikWdB2VMeP8syY3H/XvGq+pB38dDvTe06g==", "dev": true, "dependencies": { "@types/node": "*" @@ -2346,41 +2229,41 @@ "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" }, "node_modules/@types/cors": { - "version": "2.8.14", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.14.tgz", - "integrity": "sha512-RXHUvNWYICtbP6s18PnOCaqToK8y14DnLd75c6HfyKf228dxy7pHNOQkxPtvXKp/hINFMDjbYzsj63nnpPMSRQ==", + "version": "2.8.15", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.15.tgz", + "integrity": "sha512-n91JxbNLD8eQIuXDIChAN1tCKNWCEgpceU9b7ZMbFA+P+Q4yIeh80jizFLEvolRPc1ES0VdwFlGv+kJTSirogw==", "dependencies": { "@types/node": "*" } }, "node_modules/@types/ejs": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.2.tgz", - "integrity": "sha512-ZmiaE3wglXVWBM9fyVC17aGPkLo/UgaOjEiI2FXQfyczrCefORPxIe+2dVmnmk3zkVIbizjrlQzmPGhSYGXG5g==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.4.tgz", + "integrity": "sha512-fnM/NjByiWdSRJRrmGxgqOSAnmOnsvX1QcNYk5TVyIIj+7ZqOKMb9gQa4OIl/lil2w/8TiTWV+nz3q8yqxez/w==", "dev": true }, "node_modules/@types/fined": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@types/fined/-/fined-1.1.3.tgz", - "integrity": "sha512-CWYnSRnun3CGbt6taXeVo2lCbuaj4mchVJ4UF/BdU5TSuIn3AmS13pGMwCsBUoehGbhZrBrpNJZSZI5EVilXww==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@types/fined/-/fined-1.1.4.tgz", + "integrity": "sha512-mZ0onxTS5OyfSwBNecTKT0h79e4XXHrc9RI5tQfEAf+Fp6NbBmNnc0kg59HO+97V+y3opS+sfo4k4qpYwLt6NQ==", "dev": true }, "node_modules/@types/http-cache-semantics": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.2.tgz", - "integrity": "sha512-FD+nQWA2zJjh4L9+pFXqWOi0Hs1ryBCfI+985NjluQ1p8EYtoLvjLOKidXBtZ4/IcxDX4o8/E8qDS3540tNliw==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.3.tgz", + "integrity": "sha512-V46MYLFp08Wf2mmaBhvgjStM3tPa+2GAdy/iqoX+noX1//zje2x4XmrIU0cAwyClATsTmahbtoQ2EwP7I5WSiA==", "dev": true }, "node_modules/@types/http-errors": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.2.tgz", - "integrity": "sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.3.tgz", + "integrity": "sha512-pP0P/9BnCj1OVvQR2lF41EkDG/lWWnDyA203b/4Fmi2eTyORnBtcDoKDwjWQthELrBvWkMOrvSOnZ8OVlW6tXA==", "dev": true }, "node_modules/@types/inquirer": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-9.0.3.tgz", - "integrity": "sha512-CzNkWqQftcmk2jaCWdBTf9Sm7xSw4rkI1zpU/Udw3HX5//adEZUIm9STtoRP1qgWj0CWQtJ9UTvqmO2NNjhMJw==", + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-9.0.6.tgz", + "integrity": "sha512-1Go1AAP/yOy3Pth5Xf1DC3nfZ03cJLCPx6E2YnSN/5I3w1jHBVH4170DkZ+JxfmA7c9kL9+bf9z3FRGa4kNAqg==", "dev": true, "dependencies": { "@types/through": "*", @@ -2388,9 +2271,9 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.13", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz", - "integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==", + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz", + "integrity": "sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==", "dev": true }, "node_modules/@types/json5": { @@ -2400,9 +2283,9 @@ "dev": true }, "node_modules/@types/jsonwebtoken": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", - "integrity": "sha512-b0jGiOgHtZ2jqdPgPnP6WLCXZk1T8p06A/vPGzUvxpFGgKMbjXJDjC5m52ErqBnIuWZFgGoIJyRdeG5AyreJjA==", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.4.tgz", + "integrity": "sha512-8UYapdmR0QlxgvJmyE8lP7guxD0UGVMfknsdtCFZh4ovShdBl3iOI4zdvqBHrB/IS+xUj3PSx73Qkey1fhWz+g==", "dev": true, "dependencies": { "@types/node": "*" @@ -2418,9 +2301,9 @@ } }, "node_modules/@types/liftoff": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/liftoff/-/liftoff-4.0.1.tgz", - "integrity": "sha512-GUUFIP1wHuB6DEg65hkgJdqqmEeK5Vj+Wy5Tza26F9FuaHhtm4BxN00N3PhVUdCcryY9pn3SkcXGGQDLBisAPQ==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/liftoff/-/liftoff-4.0.2.tgz", + "integrity": "sha512-X7Su8/zN7UgpA9nULCQwx9qy1RqcjUFmUVhj9kkjxXF5gIjZYC97lMRggyhV1/Y7M4rmEZ90jijAWVFWDVN//w==", "dev": true, "dependencies": { "@types/fined": "*", @@ -2428,86 +2311,89 @@ } }, "node_modules/@types/minimist": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", - "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.4.tgz", + "integrity": "sha512-Kfe/D3hxHTusnPNRbycJE1N77WHDsdS4AjUYIzlDzhDrS47NrwuL3YW4VITxwR7KCVpzwgy4Rbj829KSSQmwXQ==", "dev": true }, "node_modules/@types/ms": { - "version": "0.7.31", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", - "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==", + "version": "0.7.33", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.33.tgz", + "integrity": "sha512-AuHIyzR5Hea7ij0P9q7vx7xu4z0C28ucwjAZC0ja7JhINyCnOw8/DnvAPQQ9TfOlCtZAmCERKQX9+o1mgQhuOQ==", "dev": true }, "node_modules/@types/node": { - "version": "20.6.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.6.2.tgz", - "integrity": "sha512-Y+/1vGBHV/cYk6OI1Na/LHzwnlNCAfU3ZNGrc1LdRe/LAIbdDPTTv/HU3M7yXN448aTVDq3eKRm2cg7iKLb8gw==" + "version": "20.8.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.7.tgz", + "integrity": "sha512-21TKHHh3eUHIi2MloeptJWALuCu5H7HQTdTrWIFReA8ad+aggoX+lRes3ex7/FtpC+sVUpFMQ+QTfYr74mruiQ==", + "dependencies": { + "undici-types": "~5.25.1" + } }, "node_modules/@types/nodemailer": { - "version": "6.4.10", - "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.10.tgz", - "integrity": "sha512-oPW/IdhkU3FyZc1dzeqmS+MBjrjZNiiINnrEOrWALzccJlP5xTlbkNr2YnTnnyj9Eqm5ofjRoASEbrCYpA7BrA==", + "version": "6.4.13", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.13.tgz", + "integrity": "sha512-889Vq/77eEpidCwh52sVWpbnqQmIwL8yVBekNbrztVEaWKOCRH3Eq6hjIJh1jwsGDEAJEH0RR+YhpH9mfELLKA==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/normalize-package-data": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==" + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.3.tgz", + "integrity": "sha512-ehPtgRgaULsFG8x0NeYJvmyH1hmlfsNLujHe9dQEia/7MAJYdzMSi19JtchUHjmBA6XC/75dK55mzZH+RyieSg==" }, "node_modules/@types/responselike": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", - "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha512-/4YQT5Kp6HxUDb4yhRkm0bJ7TbjvTddqX7PZ5hz6qV3pxSo72f/6YPRo+Mu2DU307tm9IioO69l7uAwn5XNcFA==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/semver": { - "version": "7.5.2", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.2.tgz", - "integrity": "sha512-7aqorHYgdNO4DM36stTiGO3DvKoex9TQRwsJU6vMaFGyqpBA1MNZkz+PG3gaNUPpTAOYhT1WR7M1JyA3fbS9Cw==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==", "dev": true }, "node_modules/@types/sinon": { - "version": "10.0.16", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.16.tgz", - "integrity": "sha512-j2Du5SYpXZjJVJtXBokASpPRj+e2z+VUhCPHmM6WMfe3dpHu6iVKJMU6AiBcMp/XTAYnEj6Wc1trJUWwZ0QaAQ==", + "version": "10.0.20", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.20.tgz", + "integrity": "sha512-2APKKruFNCAZgx3daAyACGzWuJ028VVCUDk6o2rw/Z4PXT0ogwdV4KUegW0MwVs0Zu59auPXbbuBJHF12Sx1Eg==", "dev": true, "dependencies": { "@types/sinonjs__fake-timers": "*" } }, "node_modules/@types/sinonjs__fake-timers": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.2.tgz", - "integrity": "sha512-9GcLXF0/v3t80caGs5p2rRfkB+a8VBGLJZVih6CNFkx8IZ994wiKKLSRs9nuFwk1HevWs/1mnUmkApGrSGsShA==", + "version": "8.1.4", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.4.tgz", + "integrity": "sha512-GDV68H0mBSN449sa5HEj51E0wfpVQb8xNSMzxf/PrypMFcLTMwJMOM/cgXiv71Mq5drkOQmUGvL1okOZcu6RrQ==", "dev": true }, "node_modules/@types/through": { - "version": "0.0.31", - "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.31.tgz", - "integrity": "sha512-LpKpmb7FGevYgXnBXYs6HWnmiFyVG07Pt1cnbgM1IhEacITTiUaBXXvOR3Y50ksaJWGSfhbEvQFivQEFGCC55w==", + "version": "0.0.32", + "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.32.tgz", + "integrity": "sha512-7XsfXIsjdfJM2wFDRAtEWp3zb2aVPk5QeyZxGlVK57q4u26DczMHhJmlhr0Jqv0THwxam/L8REXkj8M2I/lcvw==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.2.tgz", - "integrity": "sha512-ooaHxlmSgZTM6CHYAFRlifqh1OAr3PAQEwi7lhYhaegbnXrnh7CDcHmc3+ihhbQC7H0i4JF0psI5ehzkF6Yl6Q==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.9.0.tgz", + "integrity": "sha512-lgX7F0azQwRPB7t7WAyeHWVfW1YJ9NIgd9mvGhfQpRY56X6AVf8mwM8Wol+0z4liE7XX3QOt8MN1rUKCfSjRIA==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.7.2", - "@typescript-eslint/type-utils": "6.7.2", - "@typescript-eslint/utils": "6.7.2", - "@typescript-eslint/visitor-keys": "6.7.2", + "@typescript-eslint/scope-manager": "6.9.0", + "@typescript-eslint/type-utils": "6.9.0", + "@typescript-eslint/utils": "6.9.0", + "@typescript-eslint/visitor-keys": "6.9.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -2533,15 +2419,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.2.tgz", - "integrity": "sha512-KA3E4ox0ws+SPyxQf9iSI25R6b4Ne78ORhNHeVKrPQnoYsb9UhieoiRoJgrzgEeKGOXhcY1i8YtOeCHHTDa6Fw==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.9.0.tgz", + "integrity": "sha512-GZmjMh4AJ/5gaH4XF2eXA8tMnHWP+Pm1mjQR2QN4Iz+j/zO04b9TOvJYOX2sCNIQHtRStKTxRY1FX7LhpJT4Gw==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.7.2", - "@typescript-eslint/types": "6.7.2", - "@typescript-eslint/typescript-estree": "6.7.2", - "@typescript-eslint/visitor-keys": "6.7.2", + "@typescript-eslint/scope-manager": "6.9.0", + "@typescript-eslint/types": "6.9.0", + "@typescript-eslint/typescript-estree": "6.9.0", + "@typescript-eslint/visitor-keys": "6.9.0", "debug": "^4.3.4" }, "engines": { @@ -2561,13 +2447,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.2.tgz", - "integrity": "sha512-bgi6plgyZjEqapr7u2mhxGR6E8WCzKNUFWNh6fkpVe9+yzRZeYtDTbsIBzKbcxI+r1qVWt6VIoMSNZ4r2A+6Yw==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.9.0.tgz", + "integrity": "sha512-1R8A9Mc39n4pCCz9o79qRO31HGNDvC7UhPhv26TovDsWPBDx+Sg3rOZdCELIA3ZmNoWAuxaMOT7aWtGRSYkQxw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.7.2", - "@typescript-eslint/visitor-keys": "6.7.2" + "@typescript-eslint/types": "6.9.0", + "@typescript-eslint/visitor-keys": "6.9.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -2578,13 +2464,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.2.tgz", - "integrity": "sha512-36F4fOYIROYRl0qj95dYKx6kybddLtsbmPIYNK0OBeXv2j9L5nZ17j9jmfy+bIDHKQgn2EZX+cofsqi8NPATBQ==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.9.0.tgz", + "integrity": "sha512-XXeahmfbpuhVbhSOROIzJ+b13krFmgtc4GlEuu1WBT+RpyGPIA4Y/eGnXzjbDj5gZLzpAXO/sj+IF/x2GtTMjQ==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.7.2", - "@typescript-eslint/utils": "6.7.2", + "@typescript-eslint/typescript-estree": "6.9.0", + "@typescript-eslint/utils": "6.9.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -2605,9 +2491,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.2.tgz", - "integrity": "sha512-flJYwMYgnUNDAN9/GAI3l8+wTmvTYdv64fcH8aoJK76Y+1FCZ08RtI5zDerM/FYT5DMkAc+19E4aLmd5KqdFyg==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.9.0.tgz", + "integrity": "sha512-+KB0lbkpxBkBSiVCuQvduqMJy+I1FyDbdwSpM3IoBS7APl4Bu15lStPjgBIdykdRqQNYqYNMa8Kuidax6phaEw==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -2618,13 +2504,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.2.tgz", - "integrity": "sha512-kiJKVMLkoSciGyFU0TOY0fRxnp9qq1AzVOHNeN1+B9erKFCJ4Z8WdjAkKQPP+b1pWStGFqezMLltxO+308dJTQ==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.9.0.tgz", + "integrity": "sha512-NJM2BnJFZBEAbCfBP00zONKXvMqihZCrmwCaik0UhLr0vAgb6oguXxLX1k00oQyD+vZZ+CJn3kocvv2yxm4awQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.7.2", - "@typescript-eslint/visitor-keys": "6.7.2", + "@typescript-eslint/types": "6.9.0", + "@typescript-eslint/visitor-keys": "6.9.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -2645,17 +2531,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.2.tgz", - "integrity": "sha512-ZCcBJug/TS6fXRTsoTkgnsvyWSiXwMNiPzBUani7hDidBdj1779qwM1FIAmpH4lvlOZNF3EScsxxuGifjpLSWQ==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.9.0.tgz", + "integrity": "sha512-5Wf+Jsqya7WcCO8me504FBigeQKVLAMPmUzYgDbWchINNh1KJbxCgVya3EQ2MjvJMVeXl3pofRmprqX6mfQkjQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.7.2", - "@typescript-eslint/types": "6.7.2", - "@typescript-eslint/typescript-estree": "6.7.2", + "@typescript-eslint/scope-manager": "6.9.0", + "@typescript-eslint/types": "6.9.0", + "@typescript-eslint/typescript-estree": "6.9.0", "semver": "^7.5.4" }, "engines": { @@ -2670,12 +2556,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.2.tgz", - "integrity": "sha512-uVw9VIMFBUTz8rIeaUT3fFe8xIUx8r4ywAdlQv1ifH+6acn/XF8Y6rwJ7XNmkNMDrTW+7+vxFFPIF40nJCVsMQ==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.9.0.tgz", + "integrity": "sha512-dGtAfqjV6RFOtIP8I0B4ZTBRrlTT8NHHlZZSchQx3qReaoDeXhYM++M4So2AgFK9ZB0emRPA6JI1HkafzA2Ibg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.7.2", + "@typescript-eslint/types": "6.9.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -2686,6 +2572,12 @@ "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/abab": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", @@ -3143,9 +3035,9 @@ } }, "node_modules/axios": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz", - "integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.1.tgz", + "integrity": "sha512-Q28iYCWzNHjAm+yEAot5QaAMxhMghWLFVf7rRdwhUI+c2jix2DUXjAHXVi+s1ibs3mjPO/cCgbA++3BjD0vP/A==", "dependencies": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -3337,12 +3229,6 @@ "which": "bin/which" } }, - "node_modules/bin-check/node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", - "dev": true - }, "node_modules/bin-version": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/bin-version/-/bin-version-6.0.0.tgz", @@ -3622,13 +3508,14 @@ } }, "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.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3781,9 +3668,9 @@ } }, "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": [ { @@ -4111,9 +3998,9 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/concurrently": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.1.tgz", - "integrity": "sha512-nVraf3aXOpIcNud5pB9M82p1tynmZkrSGQ1p6X/VY8cJ+2LMVqAgXsJxYYefACSHbTYlm92O1xuhdGTjwoEvbQ==", + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz", + "integrity": "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==", "dev": true, "dependencies": { "chalk": "^4.1.2", @@ -4206,15 +4093,15 @@ } }, "node_modules/conventional-changelog-conventionalcommits": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-6.1.0.tgz", - "integrity": "sha512-3cS3GEtR78zTfMzk0AizXKKIdN4OvSh7ibNz6/DPbhWWQu7LqE/8+/GqSodV+sywUR2gpJAdP/1JFf4XtN7Zpw==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-7.0.2.tgz", + "integrity": "sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==", "dev": true, "dependencies": { "compare-func": "^2.0.0" }, "engines": { - "node": ">=14" + "node": ">=16" } }, "node_modules/conventional-changelog-writer": { @@ -4238,6 +4125,97 @@ "node": ">=14" } }, + "node_modules/conventional-changelog-writer/node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-writer/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==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-writer/node_modules/meow": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", + "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", + "dev": true, + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conventional-changelog-writer/node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-writer/node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/conventional-changelog-writer/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/conventional-changelog-writer/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/conventional-commits-filter": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-3.0.0.tgz", @@ -4252,21 +4230,21 @@ } }, "node_modules/conventional-commits-parser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-4.0.0.tgz", - "integrity": "sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", + "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==", "dev": true, "dependencies": { - "is-text-path": "^1.0.1", + "is-text-path": "^2.0.0", "JSONStream": "^1.3.5", - "meow": "^8.1.2", - "split2": "^3.2.2" + "meow": "^12.0.1", + "split2": "^4.0.0" }, "bin": { - "conventional-commits-parser": "cli.js" + "conventional-commits-parser": "cli.mjs" }, "engines": { - "node": ">=14" + "node": ">=16" } }, "node_modules/cookie": { @@ -4761,9 +4739,9 @@ } }, "node_modules/define-data-property": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.0.tgz", - "integrity": "sha512-UzGwzcjyv3OtAvolTj1GoyNYzfFR+iqbGjcnBEENZVCpM4/Ng1yhGNvS3lR/xDS74Tb2wGG9WzNSNIOS9UVb2g==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", "dev": true, "dependencies": { "get-intrinsic": "^1.2.1", @@ -5143,14 +5121,15 @@ "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, "dependencies": { "once": "^1.4.0" } }, "node_modules/engine.io": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.2.tgz", - "integrity": "sha512-IXsMcGpw/xRfjra46sVZVHiSWo/nJ/3g1337q9KNXtS6YRzbW5yIzTCb9DjhrBe7r3GZQR0I4+nq+4ODk5g/cA==", + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.3.tgz", + "integrity": "sha512-IML/R4eG/pUS5w7OfcDE0jKrljWS9nwnEfsxWCIJF5eO6AHo6+Hlv+lQbdlAYsiJPHzUthLm1RUjnBzWOs45cw==", "dependencies": { "@types/cookie": "^0.4.1", "@types/cors": "^2.8.12", @@ -5347,26 +5326,26 @@ } }, "node_modules/es-abstract": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.2.tgz", - "integrity": "sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA==", + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", + "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.0", "arraybuffer.prototype.slice": "^1.0.2", "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "call-bind": "^1.0.5", "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.1", + "get-intrinsic": "^1.2.2", "get-symbol-description": "^1.0.0", "globalthis": "^1.0.3", "gopd": "^1.0.1", - "has": "^1.0.3", "has-property-descriptors": "^1.0.0", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", + "hasown": "^2.0.0", "internal-slot": "^1.0.5", "is-array-buffer": "^3.0.2", "is-callable": "^1.2.7", @@ -5376,7 +5355,7 @@ "is-string": "^1.0.7", "is-typed-array": "^1.1.12", "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", + "object-inspect": "^1.13.1", "object-keys": "^1.1.1", "object.assign": "^4.1.4", "regexp.prototype.flags": "^1.5.1", @@ -5390,7 +5369,7 @@ "typed-array-byte-offset": "^1.0.0", "typed-array-length": "^1.0.4", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.11" + "which-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" @@ -5400,26 +5379,26 @@ } }, "node_modules/es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", + "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" + "get-intrinsic": "^1.2.2", + "has-tostringtag": "^1.0.0", + "hasown": "^2.0.0" }, "engines": { "node": ">= 0.4" } }, "node_modules/es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", "dev": true, "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.0" } }, "node_modules/es-to-primitive": { @@ -5497,18 +5476,19 @@ } }, "node_modules/eslint": { - "version": "8.49.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.49.0.tgz", - "integrity": "sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ==", + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.52.0.tgz", + "integrity": "sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "8.49.0", - "@humanwhocodes/config-array": "^0.11.11", + "@eslint/js": "8.52.0", + "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -5551,19 +5531,19 @@ } }, "node_modules/eslint-config-conventions": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/eslint-config-conventions/-/eslint-config-conventions-11.0.1.tgz", - "integrity": "sha512-Q64CuntM0w2eh5CjtEG0GuomdzxuGo+UIKlRAZDCQWAxfCteD6LUEIV+DAtfDOl3xTvDySCEh9Q7kOOkYMiuCA==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-conventions/-/eslint-config-conventions-12.0.0.tgz", + "integrity": "sha512-BFqhjK8rImf7BNC1ne+G7tc0suRlTKjUrZPllQT9l0USfw8FLh+qdyLlrcUWxll/kRFwLGbwQJhTt/LM+Gc0cQ==", "dev": true, "engines": { - "node": ">=16.0.0", + "node": ">=18.0.0", "npm": ">=9.0.0" }, "peerDependencies": { - "eslint": "^8.44.0", - "eslint-plugin-import": "^2.27.5", + "eslint": "^8.51.0", + "eslint-plugin-import": "^2.28.1", "eslint-plugin-promise": "^6.1.1", - "eslint-plugin-unicorn": "^48.0.0" + "eslint-plugin-unicorn": "^48.0.1" } }, "node_modules/eslint-config-prettier": { @@ -5625,26 +5605,26 @@ } }, "node_modules/eslint-plugin-import": { - "version": "2.28.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.28.1.tgz", - "integrity": "sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==", + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.0.tgz", + "integrity": "sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg==", "dev": true, "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.findlastindex": "^1.2.2", - "array.prototype.flat": "^1.3.1", - "array.prototype.flatmap": "^1.3.1", + "array-includes": "^3.1.7", + "array.prototype.findlastindex": "^1.2.3", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", "debug": "^3.2.7", "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.7", + "eslint-import-resolver-node": "^0.3.9", "eslint-module-utils": "^2.8.0", - "has": "^1.0.3", - "is-core-module": "^2.13.0", + "hasown": "^2.0.0", + "is-core-module": "^2.13.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", - "object.fromentries": "^2.0.6", - "object.groupby": "^1.0.0", - "object.values": "^1.1.6", + "object.fromentries": "^2.0.7", + "object.groupby": "^1.0.1", + "object.values": "^1.1.7", "semver": "^6.3.1", "tsconfig-paths": "^3.14.2" }, @@ -5686,9 +5666,9 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.0.tgz", - "integrity": "sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz", + "integrity": "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==", "dev": true, "dependencies": { "prettier-linter-helpers": "^1.0.0", @@ -6085,9 +6065,9 @@ } }, "node_modules/fast-content-type-parse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-1.0.0.tgz", - "integrity": "sha512-Xbc4XcysUXcsP5aHUU7Nq3OwvHq97C+WnbkeIefpeYLX+ryzFJlU6OStFJhs6Ol0LkUGpcK+wL0JwfM+FCU5IA==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-1.1.0.tgz", + "integrity": "sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==" }, "node_modules/fast-decode-uri-component": { "version": "1.0.1", @@ -6168,26 +6148,26 @@ "integrity": "sha512-cIusKBIt/R/oI6z/1nyfe2FvGKVTohVRfvkOhvx0nCEW+xf5NoCXjAHcWp93uOUBchzYcsvPlrapAdX1uW+YGg==" }, "node_modules/fastify": { - "version": "4.23.2", - "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.23.2.tgz", - "integrity": "sha512-WFSxsHES115svC7NrerNqZwwM0UOxbC/P6toT9LRHgAAFvG7o2AN5W+H4ihCtOGuYXjZf4z+2jXC89rVEoPWOA==", + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.24.3.tgz", + "integrity": "sha512-6HHJ+R2x2LS3y1PqxnwEIjOTZxFl+8h4kSC/TuDPXtA+v2JnV9yEtOsNSKK1RMD7sIR2y1ZsA4BEFaid/cK5pg==", "dependencies": { "@fastify/ajv-compiler": "^3.5.0", - "@fastify/error": "^3.2.0", + "@fastify/error": "^3.4.0", "@fastify/fast-json-stringify-compiler": "^4.3.0", "abstract-logging": "^2.0.1", "avvio": "^8.2.1", - "fast-content-type-parse": "^1.0.0", - "fast-json-stringify": "^5.7.0", - "find-my-way": "^7.6.0", - "light-my-request": "^5.9.1", - "pino": "^8.12.0", + "fast-content-type-parse": "^1.1.0", + "fast-json-stringify": "^5.8.0", + "find-my-way": "^7.7.0", + "light-my-request": "^5.11.0", + "pino": "^8.16.0", "process-warning": "^2.2.0", "proxy-addr": "^2.0.7", "rfdc": "^1.3.0", - "secure-json-parse": "^2.5.0", - "semver": "^7.5.0", - "toad-cache": "^3.2.0" + "secure-json-parse": "^2.7.0", + "semver": "^7.5.4", + "toad-cache": "^3.3.0" } }, "node_modules/fastify-plugin": { @@ -6362,9 +6342,9 @@ "dev": true }, "node_modules/find-my-way": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-7.6.2.tgz", - "integrity": "sha512-0OjHn1b1nCX3eVbm9ByeEHiscPYiHLfhei1wOUU9qffQkk98wE0Lo8VrVYfSGMgnSnDh86DxedduAnBf4nwUEw==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-7.7.0.tgz", + "integrity": "sha512-+SrHpvQ52Q6W9f3wJoJBbAQULJuNEEQwBvlvYwACDhBTLOTMiQ0HYWh4+vC3OivGP2ENcTI1oKlFA2OepJNjhQ==", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-querystring": "^1.0.0", @@ -6446,12 +6426,12 @@ } }, "node_modules/flat-cache": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz", - "integrity": "sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.1.tgz", + "integrity": "sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==", "dev": true, "dependencies": { - "flatted": "^3.2.7", + "flatted": "^3.2.9", "keyv": "^4.5.3", "rimraf": "^3.0.2" }, @@ -6501,9 +6481,9 @@ "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.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", "funding": [ { "type": "individual", @@ -6687,9 +6667,12 @@ } }, "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/function.prototype.name": { "version": "1.1.6", @@ -6728,15 +6711,15 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", + "function-bind": "^1.1.2", "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6858,6 +6841,120 @@ "node": ">=10" } }, + "node_modules/git-raw-commits/node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/git-raw-commits/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==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/git-raw-commits/node_modules/meow": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", + "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", + "dev": true, + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/git-raw-commits/node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/git-raw-commits/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/git-raw-commits/node_modules/split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "dev": true, + "dependencies": { + "readable-stream": "^3.0.0" + } + }, + "node_modules/git-raw-commits/node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/git-raw-commits/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/git-raw-commits/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/glob": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", @@ -6962,9 +7059,9 @@ } }, "node_modules/globals": { - "version": "13.21.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", - "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -7099,17 +7196,6 @@ "node": ">=6" } }, - "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-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -7128,12 +7214,12 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.1" + "get-intrinsic": "^1.2.2" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7178,6 +7264,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/header-case": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz", @@ -7221,15 +7318,14 @@ } }, "node_modules/hosted-git-info": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", - "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", - "dev": true, + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.1.tgz", + "integrity": "sha512-+K84LB1DYwMHoHSgaOY/Jfhw3ucPmSET5v98Ke/HdNSw4a0UktWzyW1mjhjpuxxTqOOsfWT/7iVshHmVZ4IpOA==", "dependencies": { - "lru-cache": "^6.0.0" + "lru-cache": "^10.0.1" }, "engines": { - "node": ">=10" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/html-encoding-sniffer": { @@ -7719,13 +7815,13 @@ } }, "node_modules/internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", + "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", + "get-intrinsic": "^1.2.2", + "hasown": "^2.0.0", "side-channel": "^1.0.4" }, "engines": { @@ -7871,11 +7967,11 @@ } }, "node_modules/is-core-module": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", - "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -8147,15 +8243,15 @@ } }, "node_modules/is-text-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", - "integrity": "sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz", + "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==", "dev": true, "dependencies": { - "text-extensions": "^1.0.0" + "text-extensions": "^2.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/is-typed-array": { @@ -8295,9 +8391,9 @@ } }, "node_modules/jackspeak": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.3.tgz", - "integrity": "sha512-R2bUw+kVZFS/h1AZqBKrSgDmdmjApzgY0AlCPumopFiAlbUxE2gf+SCuBzQ0cP5hHmUmFYF5yw55T97Th5Kstg==", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", "dev": true, "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -8560,9 +8656,9 @@ } }, "node_modules/keyv": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", - "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==", + "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" @@ -8644,27 +8740,27 @@ } }, "node_modules/lint-staged": { - "version": "14.0.1", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-14.0.1.tgz", - "integrity": "sha512-Mw0cL6HXnHN1ag0mN/Dg4g6sr8uf8sn98w2Oc1ECtFto9tvRF7nkXGJRbx8gPlHyoR0pLyBr2lQHbWwmUHe1Sw==", + "version": "15.0.2", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.0.2.tgz", + "integrity": "sha512-vnEy7pFTHyVuDmCAIFKR5QDO8XLVlPFQQyujQ/STOxe40ICWqJ6knS2wSJ/ffX/Lw0rz83luRDh+ET7toN+rOw==", "dev": true, "dependencies": { "chalk": "5.3.0", - "commander": "11.0.0", + "commander": "11.1.0", "debug": "4.3.4", - "execa": "7.2.0", + "execa": "8.0.1", "lilconfig": "2.1.0", - "listr2": "6.6.1", + "listr2": "7.0.2", "micromatch": "4.0.5", "pidtree": "0.6.0", "string-argv": "0.3.2", - "yaml": "2.3.1" + "yaml": "2.3.3" }, "bin": { "lint-staged": "bin/lint-staged.js" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">=18.12.0" }, "funding": { "url": "https://opencollective.com/lint-staged" @@ -8683,44 +8779,56 @@ } }, "node_modules/lint-staged/node_modules/commander": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", - "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", "dev": true, "engines": { "node": ">=16" } }, "node_modules/lint-staged/node_modules/execa": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", - "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, "dependencies": { "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", + "get-stream": "^8.0.1", + "human-signals": "^5.0.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", + "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" }, "engines": { - "node": "^14.18.0 || ^16.14.0 || >=18.0.0" + "node": ">=16.17" }, "funding": { "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/lint-staged/node_modules/human-signals": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", - "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "node_modules/lint-staged/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", "dev": true, "engines": { - "node": ">=14.18.0" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "engines": { + "node": ">=16.17.0" } }, "node_modules/lint-staged/node_modules/is-stream": { @@ -8789,6 +8897,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lint-staged/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/lint-staged/node_modules/strip-final-newline": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", @@ -8801,19 +8921,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lint-staged/node_modules/yaml": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", - "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", - "dev": true, - "engines": { - "node": ">= 14" - } - }, "node_modules/listr2": { - "version": "6.6.1", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-6.6.1.tgz", - "integrity": "sha512-+rAXGHh0fkEWdXBmX+L6mmfmXmXvDGEKzkjxO+8mP3+nI/r/CWznVBvsibXdxda9Zz0OW2e2ikphN3OwCT/jSg==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-7.0.2.tgz", + "integrity": "sha512-rJysbR9GKIalhTbVL2tYbF2hVyDnrf7pFUZBwjPaMIdadYHmeT+EVi/Bu3qd7ETQPahTotg2WRCatXwRBW554g==", "dev": true, "dependencies": { "cli-truncate": "^3.1.0", @@ -8825,14 +8936,6 @@ }, "engines": { "node": ">=16.0.0" - }, - "peerDependencies": { - "enquirer": ">= 2.3.0 < 3" - }, - "peerDependenciesMeta": { - "enquirer": { - "optional": true - } } }, "node_modules/load-json-file": { @@ -9111,14 +9214,11 @@ } }, "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" - }, + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz", + "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==", "engines": { - "node": ">=10" + "node": "14 || >=16.14" } }, "node_modules/maildev": { @@ -9425,6 +9525,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/markdownlint-cli2/node_modules/yaml": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.2.tgz", + "integrity": "sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, "node_modules/markdownlint-micromark": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/markdownlint-micromark/-/markdownlint-micromark-0.1.7.tgz", @@ -9533,37 +9642,12 @@ } }, "node_modules/meow": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", - "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", - "dev": true, - "dependencies": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/meow/node_modules/type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", + "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", "dev": true, "engines": { - "node": ">=10" + "node": ">=16.10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -9704,9 +9788,9 @@ } }, "node_modules/minipass": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.3.tgz", - "integrity": "sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", "dev": true, "engines": { "node": ">=16 || 14 >=14.17" @@ -9785,9 +9869,9 @@ "dev": true }, "node_modules/nise": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.4.tgz", - "integrity": "sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.5.tgz", + "integrity": "sha512-VJuPIfUFaXNRzETTQEEItTOP8Y171ijr+JLq42wHes3DiryR8vT+1TXQW/Rx8JNUhyYYWyIvjXTU6dOhJcs9Nw==", "dev": true, "dependencies": { "@sinonjs/commons": "^2.0.0", @@ -9806,6 +9890,24 @@ "type-detect": "4.0.8" } }, + "node_modules/nise/node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/nise/node_modules/@sinonjs/fake-timers/node_modules/@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, "node_modules/nise/node_modules/isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", @@ -9896,26 +9998,25 @@ } }, "node_modules/nodemailer": { - "version": "6.9.5", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.5.tgz", - "integrity": "sha512-/dmdWo62XjumuLc5+AYQZeiRj+PRR8y8qKtFCOyuOl1k/hckZd8durUUHs/ucKx6/8kN+wFxqKJlQ/LK/qR5FA==", + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.7.tgz", + "integrity": "sha512-rUtR77ksqex/eZRLmQ21LKVH5nAAsVicAtAYudK7JgwenEDZ0UIQ1adUGqErz7sMkWYxWTTU1aeP2Jga6WQyJw==", "engines": { "node": ">=6.0.0" } }, "node_modules/normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", - "dev": true, + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.0.tgz", + "integrity": "sha512-UL7ELRVxYBHBgYEtZCXjxuD5vPxnmvMGq0jp/dGPKKrN7tfsBh2IY7TlJ15WWwdjRWD3RJbnsygUurTK3xkPkg==", "dependencies": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" + "hosted-git-info": "^7.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" }, "engines": { - "node": ">=10" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/normalize-path": { @@ -9940,9 +10041,9 @@ } }, "node_modules/npm": { - "version": "9.8.1", - "resolved": "https://registry.npmjs.org/npm/-/npm-9.8.1.tgz", - "integrity": "sha512-AfDvThQzsIXhYgk9zhbk5R+lh811lKkLAeQMMhSypf1BM7zUafeIIBzMzespeuVEJ0+LvY36oRQYf7IKLzU3rw==", + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/npm/-/npm-9.9.0.tgz", + "integrity": "sha512-wkd7sjz4KmdmddYQcd0aTP73P1cEuPlekeulz4jTDeMVx/Zo5XZ5KQ1z3eUzV3Q/WZpEO0NJXTrD5FNFe6fhCA==", "bundleDependencies": [ "@isaacs/string-locale-compare", "@npmcli/arborist", @@ -9987,6 +10088,7 @@ "ms", "node-gyp", "nopt", + "normalize-package-data", "npm-audit-report", "npm-install-checks", "npm-package-arg", @@ -10003,6 +10105,7 @@ "read", "semver", "sigstore", + "spdx-expression-parse", "ssri", "supports-color", "tar", @@ -10016,8 +10119,8 @@ "dev": true, "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^6.3.0", - "@npmcli/config": "^6.2.1", + "@npmcli/arborist": "^6.5.0", + "@npmcli/config": "^6.4.0", "@npmcli/fs": "^3.1.0", "@npmcli/map-workspaces": "^3.0.4", "@npmcli/package-json": "^4.0.1", @@ -10041,12 +10144,12 @@ "is-cidr": "^4.0.2", "json-parse-even-better-errors": "^3.0.0", "libnpmaccess": "^7.0.2", - "libnpmdiff": "^5.0.19", - "libnpmexec": "^6.0.3", - "libnpmfund": "^4.0.19", + "libnpmdiff": "^5.0.20", + "libnpmexec": "^6.0.4", + "libnpmfund": "^4.2.1", "libnpmhook": "^9.0.3", "libnpmorg": "^5.0.4", - "libnpmpack": "^5.0.19", + "libnpmpack": "^5.0.20", "libnpmpublish": "^7.5.0", "libnpmsearch": "^6.0.2", "libnpmteam": "^5.0.3", @@ -10058,10 +10161,11 @@ "ms": "^2.1.2", "node-gyp": "^9.4.0", "nopt": "^7.2.0", + "normalize-package-data": "^5.0.0", "npm-audit-report": "^5.0.0", - "npm-install-checks": "^6.1.1", + "npm-install-checks": "^6.2.0", "npm-package-arg": "^10.1.0", - "npm-pick-manifest": "^8.0.1", + "npm-pick-manifest": "^8.0.2", "npm-profile": "^7.0.1", "npm-registry-fetch": "^14.0.5", "npm-user-validate": "^2.0.0", @@ -10073,7 +10177,8 @@ "qrcode-terminal": "^0.12.0", "read": "^2.1.0", "semver": "^7.5.4", - "sigstore": "^1.7.0", + "sigstore": "^1.9.0", + "spdx-expression-parse": "^3.0.1", "ssri": "^10.0.4", "supports-color": "^9.4.0", "tar": "^6.1.15", @@ -10188,7 +10293,7 @@ "license": "ISC" }, "node_modules/npm/node_modules/@npmcli/arborist": { - "version": "6.3.0", + "version": "6.5.0", "dev": true, "inBundle": true, "license": "ISC", @@ -10211,7 +10316,7 @@ "json-stringify-nice": "^1.1.4", "minimatch": "^9.0.0", "nopt": "^7.0.0", - "npm-install-checks": "^6.0.0", + "npm-install-checks": "^6.2.0", "npm-package-arg": "^10.1.0", "npm-pick-manifest": "^8.0.1", "npm-registry-fetch": "^14.0.3", @@ -10235,7 +10340,7 @@ } }, "node_modules/npm/node_modules/@npmcli/config": { - "version": "6.2.1", + "version": "6.4.0", "dev": true, "inBundle": true, "license": "ISC", @@ -10428,8 +10533,20 @@ "node": ">=14" } }, + "node_modules/npm/node_modules/@sigstore/bundle": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.2.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/npm/node_modules/@sigstore/protobuf-specs": { - "version": "0.1.0", + "version": "0.2.1", "dev": true, "inBundle": true, "license": "Apache-2.0", @@ -10437,13 +10554,27 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm/node_modules/@sigstore/tuf": { - "version": "1.0.2", + "node_modules/npm/node_modules/@sigstore/sign": { + "version": "1.0.0", "dev": true, "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@sigstore/protobuf-specs": "^0.1.0", + "@sigstore/bundle": "^1.1.0", + "@sigstore/protobuf-specs": "^0.2.0", + "make-fetch-happen": "^11.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/tuf": { + "version": "1.0.3", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.2.0", "tuf-js": "^1.1.7" }, "engines": { @@ -11432,12 +11563,12 @@ } }, "node_modules/npm/node_modules/libnpmdiff": { - "version": "5.0.19", + "version": "5.0.20", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^6.3.0", + "@npmcli/arborist": "^6.5.0", "@npmcli/disparity-colors": "^3.0.0", "@npmcli/installed-package-contents": "^2.0.2", "binary-extensions": "^2.2.0", @@ -11452,12 +11583,12 @@ } }, "node_modules/npm/node_modules/libnpmexec": { - "version": "6.0.3", + "version": "6.0.4", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^6.3.0", + "@npmcli/arborist": "^6.5.0", "@npmcli/run-script": "^6.0.0", "ci-info": "^3.7.1", "npm-package-arg": "^10.1.0", @@ -11474,12 +11605,12 @@ } }, "node_modules/npm/node_modules/libnpmfund": { - "version": "4.0.19", + "version": "4.2.1", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^6.3.0" + "@npmcli/arborist": "^6.5.0" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -11512,12 +11643,12 @@ } }, "node_modules/npm/node_modules/libnpmpack": { - "version": "5.0.19", + "version": "5.0.20", "dev": true, "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^6.3.0", + "@npmcli/arborist": "^6.5.0", "@npmcli/run-script": "^6.0.0", "npm-package-arg": "^10.1.0", "pacote": "^15.0.8" @@ -12063,7 +12194,7 @@ } }, "node_modules/npm/node_modules/npm-install-checks": { - "version": "6.1.1", + "version": "6.2.0", "dev": true, "inBundle": true, "license": "BSD-2-Clause", @@ -12111,7 +12242,7 @@ } }, "node_modules/npm/node_modules/npm-pick-manifest": { - "version": "8.0.1", + "version": "8.0.2", "dev": true, "inBundle": true, "license": "ISC", @@ -12605,13 +12736,15 @@ } }, "node_modules/npm/node_modules/sigstore": { - "version": "1.7.0", + "version": "1.9.0", "dev": true, "inBundle": true, "license": "Apache-2.0", "dependencies": { - "@sigstore/protobuf-specs": "^0.1.0", - "@sigstore/tuf": "^1.0.1", + "@sigstore/bundle": "^1.1.0", + "@sigstore/protobuf-specs": "^0.2.0", + "@sigstore/sign": "^1.0.0", + "@sigstore/tuf": "^1.0.3", "make-fetch-happen": "^11.0.1" }, "bin": { @@ -13082,9 +13215,9 @@ } }, "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.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -13209,9 +13342,12 @@ "integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==" }, "node_modules/on-exit-leak-free": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.0.tgz", - "integrity": "sha512-VuCaZZAjReZ3vUwgOB8LxAosIurDiAW0s13rI1YwmaP++jvcxP77AWoQvenZebpCA2m8WC1/EosPYPMjnRAp/w==" + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "engines": { + "node": ">=14.0.0" + } }, "node_modules/on-finished": { "version": "2.4.1", @@ -13345,9 +13481,9 @@ } }, "node_modules/ora/node_modules/emoji-regex": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.2.1.tgz", - "integrity": "sha512-97g6QgOk8zlDRdgq1WxwgTMgEWGVAQvB5Fdpgc1MkNy56la5SKP9GsMXKDOdqwn90/41a8yPwIGk1Y6WVbeMQA==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", + "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", "dev": true }, "node_modules/ora/node_modules/string-width": { @@ -13742,15 +13878,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz", - "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==", - "dev": true, - "engines": { - "node": "14 || >=16.14" - } - }, "node_modules/path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", @@ -13819,9 +13946,9 @@ } }, "node_modules/pino": { - "version": "8.15.1", - "resolved": "https://registry.npmjs.org/pino/-/pino-8.15.1.tgz", - "integrity": "sha512-Cp4QzUQrvWCRJaQ8Lzv0mJzXVk4z2jlq8JNKMGaixC2Pz5L4l2p95TkuRvYbrEbe85NQsDKrAd4zalf7Ml6WiA==", + "version": "8.16.1", + "resolved": "https://registry.npmjs.org/pino/-/pino-8.16.1.tgz", + "integrity": "sha512-3bKsVhBmgPjGV9pyn4fO/8RtoVDR8ssW1ev819FsRXlRNgW8gR/9Kx+gCK4UPWd4JjrRDLWpzd/pb1AyWm3MGA==", "dependencies": { "atomic-sleep": "^1.0.0", "fast-redact": "^3.1.1", @@ -13832,7 +13959,7 @@ "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", - "sonic-boom": "^3.1.0", + "sonic-boom": "^3.7.0", "thread-stream": "^2.0.0" }, "bin": { @@ -13848,14 +13975,6 @@ "split2": "^4.0.0" } }, - "node_modules/pino-abstract-transport/node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "engines": { - "node": ">= 10.x" - } - }, "node_modules/pino-std-serializers": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz", @@ -14012,13 +14131,13 @@ } }, "node_modules/prisma": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.3.1.tgz", - "integrity": "sha512-Wp2msQIlMPHe+5k5Od6xnsI/WNG7UJGgFUJgqv/ygc7kOECZapcSz/iU4NIEzISs3H1W9sFLjAPbg/gOqqtB7A==", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.4.2.tgz", + "integrity": "sha512-GDMZwZy7mysB2oXU+angQqJ90iaPFdD0rHaZNkn+dio5NRkGLmMqmXs31//tg/qXT3iB0cTQwnGGQNuirhSTZg==", "devOptional": true, "hasInstallScript": true, "dependencies": { - "@prisma/engines": "5.3.1" + "@prisma/engines": "5.4.2" }, "bin": { "prisma": "build/index.js" @@ -14381,17 +14500,6 @@ "node": ">=8" } }, - "node_modules/read-pkg/node_modules/hosted-git-info": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.1.tgz", - "integrity": "sha512-+K84LB1DYwMHoHSgaOY/Jfhw3ucPmSET5v98Ke/HdNSw4a0UktWzyW1mjhjpuxxTqOOsfWT/7iVshHmVZ4IpOA==", - "dependencies": { - "lru-cache": "^10.0.1" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, "node_modules/read-pkg/node_modules/json-parse-even-better-errors": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz", @@ -14408,28 +14516,6 @@ "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, - "node_modules/read-pkg/node_modules/lru-cache": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz", - "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==", - "engines": { - "node": "14 || >=16.14" - } - }, - "node_modules/read-pkg/node_modules/normalize-package-data": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.0.tgz", - "integrity": "sha512-UL7ELRVxYBHBgYEtZCXjxuD5vPxnmvMGq0jp/dGPKKrN7tfsBh2IY7TlJ15WWwdjRWD3RJbnsygUurTK3xkPkg==", - "dependencies": { - "hosted-git-info": "^7.0.0", - "is-core-module": "^2.8.1", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, "node_modules/read-pkg/node_modules/parse-json": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-7.1.0.tgz", @@ -14460,9 +14546,9 @@ } }, "node_modules/read-pkg/node_modules/type-fest": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.3.1.tgz", - "integrity": "sha512-pphNW/msgOUSkJbH58x8sqpq8uQj6b0ZKGxEsLKMUnGorRcDjrUaLS+39+/ub41JNTwrrMyJcUB8+YZs3mbwqw==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.5.0.tgz", + "integrity": "sha512-diLQivFzddJl4ylL3jxSkEc39Tpw7o1QeEHIPxVwryDK2lpB7Nqhzhuo6v5/Ls08Z0yPSAhsyAWlv1/H0ciNmw==", "engines": { "node": ">=16" }, @@ -14658,9 +14744,9 @@ "dev": true }, "node_modules/resolve": { - "version": "1.22.6", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz", - "integrity": "sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==", + "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.13.0", @@ -14765,15 +14851,15 @@ "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==" }, "node_modules/rimraf": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.1.tgz", - "integrity": "sha512-OfFZdwtd3lZ+XZzYP/6gTACubwFcHdLRqS9UX3UwpU2dnGQYkPFISRwvM3w9IiB2w7bW5qGo/uAwE4SmXXSKvg==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz", + "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==", "dev": true, "dependencies": { - "glob": "^10.2.5" + "glob": "^10.3.7" }, "bin": { - "rimraf": "dist/cjs/src/bin.js" + "rimraf": "dist/esm/bin.mjs" }, "engines": { "node": ">=14" @@ -14792,19 +14878,19 @@ } }, "node_modules/rimraf/node_modules/glob": { - "version": "10.3.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.4.tgz", - "integrity": "sha512-6LFElP3A+i/Q8XQKEvZjkEWEOTgAIALR9AO2rwT8bgPhDd1anmqDJDZ6lLddI4ehxxxR1S5RIqKe1uapMQfYaQ==", + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", "dev": true, "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.0.3", + "jackspeak": "^2.3.5", "minimatch": "^9.0.1", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", "path-scurry": "^1.10.1" }, "bin": { - "glob": "dist/cjs/src/bin.js" + "glob": "dist/esm/bin.mjs" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -15119,18 +15205,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/semantic-release/node_modules/hosted-git-info": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.1.tgz", - "integrity": "sha512-+K84LB1DYwMHoHSgaOY/Jfhw3ucPmSET5v98Ke/HdNSw4a0UktWzyW1mjhjpuxxTqOOsfWT/7iVshHmVZ4IpOA==", - "dev": true, - "dependencies": { - "lru-cache": "^10.0.1" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, "node_modules/semantic-release/node_modules/human-signals": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", @@ -15179,15 +15253,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/semantic-release/node_modules/lru-cache": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz", - "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==", - "dev": true, - "engines": { - "node": "14 || >=16.14" - } - }, "node_modules/semantic-release/node_modules/mimic-fn": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", @@ -15335,9 +15400,9 @@ } }, "node_modules/semantic-release/node_modules/type-fest": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.3.1.tgz", - "integrity": "sha512-pphNW/msgOUSkJbH58x8sqpq8uQj6b0ZKGxEsLKMUnGorRcDjrUaLS+39+/ub41JNTwrrMyJcUB8+YZs3mbwqw==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.5.0.tgz", + "integrity": "sha512-diLQivFzddJl4ylL3jxSkEc39Tpw7o1QeEHIPxVwryDK2lpB7Nqhzhuo6v5/Ls08Z0yPSAhsyAWlv1/H0ciNmw==", "dev": true, "engines": { "node": ">=16" @@ -15414,6 +15479,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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", @@ -15496,6 +15577,21 @@ "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz", "integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==" }, + "node_modules/set-function-length": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", + "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.1", + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/set-function-name": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", @@ -15663,16 +15759,16 @@ } }, "node_modules/sinon": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-16.0.0.tgz", - "integrity": "sha512-B8AaZZm9CT5pqe4l4uWJztfD/mOTa7dL8Qo0W4+s+t74xECOgSZDDQCBjNgIK3+n4kyxQrSTv2V5ul8K25qkiQ==", + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-17.0.0.tgz", + "integrity": "sha512-p4lJiYKBoOEVUxxVIC9H1MM2znG1/c8gud++I2BauJA5hsz7hHsst35eurNWXTusBsIq66FzOQbZ/uMdpvbPIQ==", "dev": true, "dependencies": { "@sinonjs/commons": "^3.0.0", - "@sinonjs/fake-timers": "^10.3.0", + "@sinonjs/fake-timers": "^11.2.2", "@sinonjs/samsam": "^8.0.0", "diff": "^5.1.0", - "nise": "^5.1.4", + "nise": "^5.1.5", "supports-color": "^7.2.0" }, "funding": { @@ -15808,9 +15904,9 @@ } }, "node_modules/sonic-boom": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.3.0.tgz", - "integrity": "sha512-LYxp34KlZ1a2Jb8ZQgFCK3niIHzibdwtwNUWKg0qQRzsDoJ3Gfgkf8KdBTFU3SkejDEIlWwnSnpVdOZIhFMl/g==", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.7.0.tgz", + "integrity": "sha512-IudtNvSqA/ObjN97tfgNmOKyDOs4dNcg4cUUsHDebqsgb8wGBBwb31LIgShNO8fye0dFI52X1+tFoKKI6Rq1Gg==", "dependencies": { "atomic-sleep": "^1.0.0" } @@ -15884,9 +15980,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", - "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==" + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz", + "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==" }, "node_modules/split": { "version": "1.0.1", @@ -15901,26 +15997,11 @@ } }, "node_modules/split2": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", - "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", - "dev": true, - "dependencies": { - "readable-stream": "^3.0.0" - } - }, - "node_modules/split2/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", "engines": { - "node": ">= 6" + "node": ">= 10.x" } }, "node_modules/statuses": { @@ -16356,12 +16437,15 @@ "integrity": "sha512-/0TJD42KDnVwKmDK6jj3xP7E2MG7SHAOG4tyTgyUCRPdHwvkquYNLEQltmdMa3owq3TkddCVcTsoctJI8VQNKA==" }, "node_modules/text-extensions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", - "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz", + "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==", "dev": true, "engines": { - "node": ">=0.10" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/text-table": { @@ -16371,9 +16455,9 @@ "dev": true }, "node_modules/thread-stream": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.4.0.tgz", - "integrity": "sha512-xZYtOtmnA63zj04Q+F9bdEay5r47bvpo1CaNqsKi7TpoJHcotUez8Fkfo2RJWpW91lnnaApdpRbVwCWsy+ifcw==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.4.1.tgz", + "integrity": "sha512-d/Ex2iWd1whipbT681JmTINKw0ZwOUBZm7+Gjs64DHuX34mmw8vJL2bFAaNacaW72zYiTJxSHi5abUuOi5nsfg==", "dependencies": { "real-require": "^0.2.0" } @@ -16408,9 +16492,9 @@ } }, "node_modules/tiny-lru": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-11.0.1.tgz", - "integrity": "sha512-iNgFugVuQgBKrqeO/mpiTTgmBsTP0WL6yeuLfLs/Ctf0pI/ixGqIRm8sDCwMcXGe9WWvt2sGXI5mNqZbValmJg==", + "version": "11.2.3", + "resolved": "https://registry.npmjs.org/tiny-lru/-/tiny-lru-11.2.3.tgz", + "integrity": "sha512-mF9jPTrvN7UHk0bekOk3RlFdFwfyS4CJYVsGc7nInL3pVgUCYj5r9X6GpZBFQgLr0TKJo8Dp+F3oRvYzxU9xiA==", "engines": { "node": ">=12" } @@ -16461,9 +16545,9 @@ } }, "node_modules/toad-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.2.0.tgz", - "integrity": "sha512-Hj5zSqBS6OHbZoQk9IU8VqIr+0JUpwzunnwSlFJhG8aJSInYUMEuzItl3kJsGteTPd1qtflafdRHlRtUazYeqg==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.3.0.tgz", + "integrity": "sha512-3oDzcogWGHZdkwrHyvJVpPjA7oNzY6ENOV3PsWJY9XYPZ6INo94Yd47s5may1U+nleBPwDhrRiTPMIvKaa3MQg==", "engines": { "node": ">=12" } @@ -16837,6 +16921,11 @@ "node": ">=0.10.0" } }, + "node_modules/undici-types": { + "version": "5.25.3", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.25.3.tgz", + "integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==" + }, "node_modules/unique-string": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", @@ -17114,13 +17203,13 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", - "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", + "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", "dev": true, "dependencies": { "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "call-bind": "^1.0.4", "for-each": "^0.3.3", "gopd": "^1.0.1", "has-tostringtag": "^1.0.0" @@ -17247,9 +17336,9 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/ws": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.1.tgz", - "integrity": "sha512-4OOseMUq8AzRBI/7SLMUwO+FEDnguetSk7KMb1sHwvF2w2Wv5Hoj0nlifx8vtGsftE/jWHojPy8sMMzYLJ2G/A==", + "version": "8.14.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", + "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", "dev": true, "engines": { "node": ">=10.0.0" @@ -17301,14 +17390,15 @@ } }, "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", + "dev": true }, "node_modules/yaml": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.2.tgz", - "integrity": "sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.3.tgz", + "integrity": "sha512-zw0VAJxgeZ6+++/su5AFoqBbZbrEakwu+X0M5HmcwUiBL7AzcuPKjj5we4xfQLp78LkEMpD0cOnUhmgOVy3KdQ==", "engines": { "node": ">= 14" } @@ -17332,12 +17422,12 @@ } }, "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs/node_modules/emoji-regex": { @@ -17369,15 +17459,6 @@ "node": ">=8" } }, - "node_modules/yargs/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/package.json b/package.json index 6d42d11..b4d34dc 100644 --- a/package.json +++ b/package.json @@ -40,71 +40,71 @@ "postinstall": "husky install" }, "dependencies": { - "@fastify/cors": "8.3.0", + "@fastify/cors": "8.4.0", "@fastify/helmet": "11.1.1", - "@fastify/multipart": "7.7.3", + "@fastify/multipart": "8.0.0", "@fastify/rate-limit": "8.0.3", - "@fastify/sensible": "5.3.0", - "@fastify/swagger": "8.10.0", - "@fastify/swagger-ui": "1.9.3", - "@prisma/client": "5.3.1", - "@sinclair/typebox": "0.31.15", + "@fastify/sensible": "5.5.0", + "@fastify/swagger": "8.12.0", + "@fastify/swagger-ui": "1.10.1", + "@prisma/client": "5.4.2", + "@sinclair/typebox": "0.31.18", "@thream/socketio-jwt": "3.1.3", - "axios": "1.5.0", + "axios": "1.5.1", "bcryptjs": "2.4.3", "dotenv": "16.3.1", "ejs": "3.1.9", - "fastify": "4.23.2", + "fastify": "4.24.3", "fastify-plugin": "4.5.1", "form-data": "4.0.0", "http-errors": "2.0.0", "jsonwebtoken": "9.0.2", "ms": "2.1.3", - "nodemailer": "6.9.5", + "nodemailer": "6.9.7", "read-pkg": "8.1.0", "socket.io": "4.7.2" }, "devDependencies": { - "@commitlint/cli": "17.7.1", - "@commitlint/config-conventional": "17.7.0", - "@saithodev/semantic-release-backmerge": "3.2.0", + "@commitlint/cli": "18.0.0", + "@commitlint/config-conventional": "18.0.0", + "@saithodev/semantic-release-backmerge": "3.2.1", "@semantic-release/git": "10.0.1", "@swc/cli": "0.1.62", - "@swc/core": "1.3.85", + "@swc/core": "1.3.94", "@tsconfig/strictest": "2.0.2", - "@types/bcryptjs": "2.4.4", - "@types/busboy": "1.5.1", - "@types/ejs": "3.1.2", - "@types/http-errors": "2.0.2", - "@types/jsonwebtoken": "9.0.3", - "@types/ms": "0.7.31", - "@types/node": "20.6.2", - "@types/nodemailer": "6.4.10", - "@types/sinon": "10.0.16", - "@typescript-eslint/eslint-plugin": "6.7.2", - "@typescript-eslint/parser": "6.7.2", + "@types/bcryptjs": "2.4.5", + "@types/busboy": "1.5.2", + "@types/ejs": "3.1.4", + "@types/http-errors": "2.0.3", + "@types/jsonwebtoken": "9.0.4", + "@types/ms": "0.7.33", + "@types/node": "20.8.7", + "@types/nodemailer": "6.4.13", + "@types/sinon": "10.0.20", + "@typescript-eslint/eslint-plugin": "6.9.0", + "@typescript-eslint/parser": "6.9.0", "chokidar": "3.5.3", - "concurrently": "8.2.1", + "concurrently": "8.2.2", "cross-env": "7.0.3", "editorconfig-checker": "5.1.1", - "eslint": "8.49.0", - "eslint-config-conventions": "11.0.1", + "eslint": "8.52.0", + "eslint-config-conventions": "12.0.0", "eslint-config-prettier": "9.0.0", - "eslint-plugin-import": "2.28.1", - "eslint-plugin-prettier": "5.0.0", + "eslint-plugin-import": "2.29.0", + "eslint-plugin-prettier": "5.0.1", "eslint-plugin-promise": "6.1.1", "eslint-plugin-unicorn": "48.0.1", "husky": "8.0.3", - "lint-staged": "14.0.1", + "lint-staged": "15.0.2", "maildev": "2.1.0", "markdownlint-cli2": "0.10.0", "markdownlint-rule-relative-links": "2.1.0", "plop": "4.0.0", "prettier": "3.0.3", - "prisma": "5.3.1", - "rimraf": "5.0.1", + "prisma": "5.4.2", + "rimraf": "5.0.5", "semantic-release": "21.1.1", - "sinon": "16.0.0", + "sinon": "17.0.0", "typescript": "5.2.2" } } diff --git a/plopfile.js b/plopfile.js index d8dc6be..416578b 100644 --- a/plopfile.js +++ b/plopfile.js @@ -1,8 +1,8 @@ -import { serviceGenerator } from './generators/service/index.js' +import { serviceGenerator } from "./generators/service/index.js" export default ( /** @type {import('plop').NodePlopAPI} */ - plop + plop, ) => { - plop.setGenerator('service', serviceGenerator) + plop.setGenerator("service", serviceGenerator) } diff --git a/src/__test__/utils/authenticateUserTest.ts b/src/__test__/utils/authenticateUserTest.ts index cc88da9..2a65399 100644 --- a/src/__test__/utils/authenticateUserTest.ts +++ b/src/__test__/utils/authenticateUserTest.ts @@ -1,35 +1,35 @@ -import type { User } from '@prisma/client' -import sinon from 'sinon' +import type { User } from "@prisma/client" +import sinon from "sinon" -import { refreshTokenExample } from '#src/models/RefreshToken.js' -import type { UserJWT } from '#src/models/User.js' -import { userExample } from '#src/models/User.js' -import { userSettingsExample } from '#src/models/UserSettings.js' +import { refreshTokenExample } from "#src/models/RefreshToken.js" +import type { UserJWT } from "#src/models/User.js" +import { userExample } from "#src/models/User.js" +import { userSettingsExample } from "#src/models/UserSettings.js" import { generateAccessToken, - generateRefreshToken -} from '#src/tools/utils/jwtToken.js' -import prisma from '#src/tools/database/prisma.js' + generateRefreshToken, +} from "#src/tools/utils/jwtToken.js" +import prisma from "#src/tools/database/prisma.js" const userStubValue = { findUnique: async () => { return userExample - } + }, } const userSettingStubValue = { findFirst: async () => { return userSettingsExample - } + }, } const oAuthStubValue = { findMany: async () => { return [] - } + }, } const refreshTokenStubValue = { create: async () => { return refreshTokenExample - } + }, } export const authenticateUserTest = async (): Promise<{ @@ -41,13 +41,13 @@ export const authenticateUserTest = async (): Promise<{ oAuthStubValue: typeof oAuthStubValue refreshTokenStubValue: typeof refreshTokenStubValue }> => { - sinon.stub(prisma, 'user').value(userStubValue) - sinon.stub(prisma, 'userSetting').value(userSettingStubValue) - sinon.stub(prisma, 'oAuth').value(oAuthStubValue) - sinon.stub(prisma, 'refreshToken').value(refreshTokenStubValue) + sinon.stub(prisma, "user").value(userStubValue) + sinon.stub(prisma, "userSetting").value(userSettingStubValue) + sinon.stub(prisma, "oAuth").value(oAuthStubValue) + sinon.stub(prisma, "refreshToken").value(refreshTokenStubValue) const userJWT: UserJWT = { - currentStrategy: 'Local', - id: 1 + currentStrategy: "Local", + id: 1, } const accessToken = generateAccessToken(userJWT) const refreshToken = await generateRefreshToken(userJWT) @@ -58,6 +58,6 @@ export const authenticateUserTest = async (): Promise<{ userStubValue, userSettingStubValue, oAuthStubValue, - refreshTokenStubValue + refreshTokenStubValue, } } diff --git a/src/application.ts b/src/application.ts index 5377dfc..3a14424 100644 --- a/src/application.ts +++ b/src/application.ts @@ -1,75 +1,75 @@ -import dotenv from 'dotenv' -import fastify from 'fastify' -import fastifyCors from '@fastify/cors' -import fastifySwagger from '@fastify/swagger' -import fastifySwaggerUI from '@fastify/swagger-ui' -import fastifyHelmet from '@fastify/helmet' -import fastifyRateLimit from '@fastify/rate-limit' -import fastifySensible from '@fastify/sensible' -import { readPackage } from 'read-pkg' +import dotenv from "dotenv" +import fastify from "fastify" +import fastifyCors from "@fastify/cors" +import fastifySwagger from "@fastify/swagger" +import fastifySwaggerUI from "@fastify/swagger-ui" +import fastifyHelmet from "@fastify/helmet" +import fastifyRateLimit from "@fastify/rate-limit" +import fastifySensible from "@fastify/sensible" +import { readPackage } from "read-pkg" -import { services } from '#src/services/index.js' -import fastifySocketIo from '#src/tools/plugins/socket-io.js' +import { services } from "#src/services/index.js" +import fastifySocketIo from "#src/tools/plugins/socket-io.js" dotenv.config() const packageJSON = await readPackage() export const application = fastify({ - logger: process.env['NODE_ENV'] === 'development', + logger: process.env["NODE_ENV"] === "development", ajv: { customOptions: { - strict: 'log', - keywords: ['kind', 'modifier'], + strict: "log", + keywords: ["kind", "modifier"], formats: { - full: true - } - } - } + full: true, + }, + }, + }, }) await application.register(fastifyCors) await application.register(fastifySensible) await application.register(fastifySocketIo, { cors: { - origin: '*', - methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', + origin: "*", + methods: "GET,HEAD,PUT,PATCH,POST,DELETE", preflightContinue: false, - optionsSuccessStatus: 204 - } + optionsSuccessStatus: 204, + }, }) await application.register(fastifyHelmet) await application.register(fastifyRateLimit, { max: 200, - timeWindow: '1 minute' + timeWindow: "1 minute", }) await application.register(fastifySwagger, { openapi: { info: { title: packageJSON.name, description: packageJSON.description, - version: packageJSON.version + version: packageJSON.version, }, tags: [ - { name: 'users' }, - { name: 'oauth2' }, - { name: 'guilds' }, - { name: 'channels' }, - { name: 'messages' }, - { name: 'members' } + { name: "users" }, + { name: "oauth2" }, + { name: "guilds" }, + { name: "channels" }, + { name: "messages" }, + { name: "members" }, ], components: { securitySchemes: { bearerAuth: { - type: 'http', - scheme: 'bearer', - bearerFormat: 'JWT' - } - } - } + type: "http", + scheme: "bearer", + bearerFormat: "JWT", + }, + }, + }, }, - hideUntagged: true + hideUntagged: true, }) await application.register(fastifySwaggerUI, { - routePrefix: '/documentation', - staticCSP: true + routePrefix: "/documentation", + staticCSP: true, }) await application.register(services) diff --git a/src/index.ts b/src/index.ts index 7257b86..b8994b0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,8 @@ -import { application } from '#src/application.js' -import { HOST, PORT } from '#src/tools/configurations.js' +import { application } from "#src/application.js" +import { HOST, PORT } from "#src/tools/configurations.js" const address = await application.listen({ port: PORT, - host: HOST + host: HOST, }) console.log(`Server listening at ${address}`) diff --git a/src/models/Channel.ts b/src/models/Channel.ts index 3f57bb2..ef30d9f 100644 --- a/src/models/Channel.ts +++ b/src/models/Channel.ts @@ -1,23 +1,23 @@ -import { Type } from '@sinclair/typebox' -import type { Channel } from '@prisma/client' +import { Type } from "@sinclair/typebox" +import type { Channel } from "@prisma/client" -import { date, id } from '#src/models/utils.js' -import { guildExample } from '#src/models/Guild.js' +import { date, id } from "#src/models/utils.js" +import { guildExample } from "#src/models/Guild.js" -export const types = [Type.Literal('text')] +export const types = [Type.Literal("text")] export const channelSchema = { id, name: Type.String({ minLength: 1, maxLength: 20 }), createdAt: date.createdAt, updatedAt: date.updatedAt, - guildId: id + guildId: id, } export const channelExample: Channel = { id: 1, - name: 'general', + name: "general", guildId: guildExample.id, createdAt: new Date(), - updatedAt: new Date() + updatedAt: new Date(), } diff --git a/src/models/Guild.ts b/src/models/Guild.ts index 46bff33..386e876 100644 --- a/src/models/Guild.ts +++ b/src/models/Guild.ts @@ -1,28 +1,28 @@ -import type { Guild } from '@prisma/client' -import { Type } from '@sinclair/typebox' +import type { Guild } from "@prisma/client" +import { Type } from "@sinclair/typebox" -import { date, id } from '#src/models/utils.js' +import { date, id } from "#src/models/utils.js" export const guildSchema = { id, name: Type.String({ minLength: 1, maxLength: 30 }), icon: Type.Union([ - Type.String({ format: 'uri-reference', minLength: 1 }), - Type.Null() + Type.String({ format: "uri-reference", minLength: 1 }), + Type.Null(), ]), description: Type.Union([ Type.String({ minLength: 1, maxLength: 160 }), - Type.Null() + Type.Null(), ]), createdAt: date.createdAt, - updatedAt: date.updatedAt + updatedAt: date.updatedAt, } export const guildExample: Guild = { id: 1, - name: 'GuildExample', - description: 'guild example.', + name: "GuildExample", + description: "guild example.", icon: null, createdAt: new Date(), - updatedAt: new Date() + updatedAt: new Date(), } diff --git a/src/models/Member.ts b/src/models/Member.ts index d780b1f..573c74e 100644 --- a/src/models/Member.ts +++ b/src/models/Member.ts @@ -1,9 +1,9 @@ -import { Type } from '@sinclair/typebox' -import type { Member } from '@prisma/client' +import { Type } from "@sinclair/typebox" +import type { Member } from "@prisma/client" -import { date, id } from '#src/models/utils.js' -import { guildExample } from '#src/models/Guild.js' -import { userExample } from '#src/models/User.js' +import { date, id } from "#src/models/utils.js" +import { guildExample } from "#src/models/Guild.js" +import { userExample } from "#src/models/User.js" export const memberSchema = { id, @@ -11,7 +11,7 @@ export const memberSchema = { createdAt: date.createdAt, updatedAt: date.updatedAt, userId: id, - guildId: id + guildId: id, } export const memberExample: Member = { @@ -20,5 +20,5 @@ export const memberExample: Member = { userId: userExample.id, guildId: guildExample.id, createdAt: new Date(), - updatedAt: new Date() + updatedAt: new Date(), } diff --git a/src/models/Message.ts b/src/models/Message.ts index e43ad24..548d743 100644 --- a/src/models/Message.ts +++ b/src/models/Message.ts @@ -1,34 +1,34 @@ -import type { Message } from '@prisma/client' -import { Type } from '@sinclair/typebox' +import type { Message } from "@prisma/client" +import { Type } from "@sinclair/typebox" -import { date, id } from '#src/models/utils.js' +import { date, id } from "#src/models/utils.js" -export const types = [Type.Literal('text'), Type.Literal('file')] +export const types = [Type.Literal("text"), Type.Literal("file")] export const messageSchema = { id, value: Type.String({ minLength: 1, - maxLength: 20_000 + maxLength: 20_000, }), - type: Type.Union(types, { default: 'text' }), + type: Type.Union(types, { default: "text" }), mimetype: Type.String({ maxLength: 127, - default: 'text/plain' + default: "text/plain", }), createdAt: date.createdAt, updatedAt: date.updatedAt, memberId: id, - channelId: id + channelId: id, } export const messageExample: Message = { id: 1, - value: 'Hello, world!', - type: 'text', - mimetype: 'text/plain', + value: "Hello, world!", + type: "text", + mimetype: "text/plain", createdAt: new Date(), updatedAt: new Date(), memberId: 1, - channelId: 1 + channelId: 1, } diff --git a/src/models/OAuth.ts b/src/models/OAuth.ts index 06c12c8..377f43b 100644 --- a/src/models/OAuth.ts +++ b/src/models/OAuth.ts @@ -1,9 +1,9 @@ -import { Type } from '@sinclair/typebox' +import { Type } from "@sinclair/typebox" -import { date, id } from '#src/models/utils.js' +import { date, id } from "#src/models/utils.js" -export const providers = ['Google', 'GitHub', 'Discord'] as const -export const strategies = [...providers, 'Local'] as const +export const providers = ["Google", "GitHub", "Discord"] as const +export const strategies = [...providers, "Local"] as const export const strategiesTypebox = strategies.map((strategy) => { return Type.Literal(strategy) @@ -21,5 +21,5 @@ export const oauthSchema = { provider: Type.Union([...providersTypebox]), createdAt: date.createdAt, updatedAt: date.updatedAt, - userId: id + userId: id, } diff --git a/src/models/RefreshToken.ts b/src/models/RefreshToken.ts index 276290d..888a49c 100644 --- a/src/models/RefreshToken.ts +++ b/src/models/RefreshToken.ts @@ -1,21 +1,21 @@ -import type { RefreshToken } from '@prisma/client' -import { Type } from '@sinclair/typebox' +import type { RefreshToken } from "@prisma/client" +import { Type } from "@sinclair/typebox" -import { userExample } from '#src/models/User.js' -import { date, id } from '#src/models/utils.js' +import { userExample } from "#src/models/User.js" +import { date, id } from "#src/models/utils.js" export const refreshTokensSchema = { id, - token: Type.String({ format: 'uuid' }), + token: Type.String({ format: "uuid" }), createdAt: date.createdAt, updatedAt: date.updatedAt, - userId: id + userId: id, } export const refreshTokenExample: RefreshToken = { id: 1, userId: userExample.id, - token: 'sometokenUUID', + token: "sometokenUUID", createdAt: new Date(), - updatedAt: new Date() + updatedAt: new Date(), } diff --git a/src/models/User.ts b/src/models/User.ts index 19b12db..2b63bea 100644 --- a/src/models/User.ts +++ b/src/models/User.ts @@ -1,11 +1,11 @@ -import type { User } from '@prisma/client' -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import type { User } from "@prisma/client" +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" -import type { AuthenticationStrategy } from '#src/models/OAuth.js' -import { strategiesTypebox } from '#src/models/OAuth.js' -import { userSettingsSchema } from '#src/models/UserSettings.js' -import { date, id } from '#src/models/utils.js' +import type { AuthenticationStrategy } from "#src/models/OAuth.js" +import { strategiesTypebox } from "#src/models/OAuth.js" +import { userSettingsSchema } from "#src/models/UserSettings.js" +import { date, id } from "#src/models/utils.js" export interface UserJWT { id: number @@ -25,21 +25,21 @@ export interface UserRequest { export const userSchema = { id, name: Type.String({ minLength: 1, maxLength: 30 }), - email: Type.String({ minLength: 1, maxLength: 254, format: 'email' }), + email: Type.String({ minLength: 1, maxLength: 254, format: "email" }), password: Type.String({ minLength: 1 }), - logo: Type.String({ minLength: 1, format: 'uri-reference' }), + logo: Type.String({ minLength: 1, format: "uri-reference" }), status: Type.String({ minLength: 1, maxLength: 50 }), biography: Type.String({ minLength: 1, maxLength: 160 }), website: Type.String({ minLength: 1, maxLength: 255, - format: 'uri' + format: "uri", }), isConfirmed: Type.Boolean({ default: false }), temporaryToken: Type.String(), - temporaryExpirationToken: Type.String({ format: 'date-time' }), + temporaryExpirationToken: Type.String({ format: "date-time" }), createdAt: date.createdAt, - updatedAt: date.updatedAt + updatedAt: date.updatedAt, } export const userPublicWithoutSettingsSchema = { @@ -52,20 +52,20 @@ export const userPublicWithoutSettingsSchema = { website: Type.Union([userSchema.website, Type.Null()]), isConfirmed: userSchema.isConfirmed, createdAt: date.createdAt, - updatedAt: date.updatedAt + updatedAt: date.updatedAt, } export const userPublicSchema = { ...userPublicWithoutSettingsSchema, - settings: Type.Object(userSettingsSchema) + settings: Type.Object(userSettingsSchema), } export const userCurrentSchema = Type.Object({ user: Type.Object({ ...userPublicSchema, currentStrategy: Type.Union([...strategiesTypebox]), - strategies: Type.Array(Type.Union([...strategiesTypebox])) - }) + strategies: Type.Array(Type.Union([...strategiesTypebox])), + }), }) export const bodyUserSchema = Type.Object({ @@ -73,23 +73,23 @@ export const bodyUserSchema = Type.Object({ name: userSchema.name, password: userSchema.password, theme: userSettingsSchema.theme, - language: userSettingsSchema.language + language: userSettingsSchema.language, }) export type BodyUserSchemaType = Static export const userExample: User = { id: 1, - name: 'Divlo', - email: 'contact@divlo.fr', - password: 'somepassword', + name: "Divlo", + email: "contact@divlo.fr", + password: "somepassword", logo: null, status: null, biography: null, website: null, isConfirmed: true, - temporaryToken: 'temporaryUUIDtoken', + temporaryToken: "temporaryUUIDtoken", temporaryExpirationToken: new Date(), createdAt: new Date(), - updatedAt: new Date() + updatedAt: new Date(), } diff --git a/src/models/UserSettings.ts b/src/models/UserSettings.ts index f72d9d2..e6a986b 100644 --- a/src/models/UserSettings.ts +++ b/src/models/UserSettings.ts @@ -1,11 +1,11 @@ -import type { UserSetting } from '@prisma/client' -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import type { UserSetting } from "@prisma/client" +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" -import { date, id } from '#src/models/utils.js' +import { date, id } from "#src/models/utils.js" -export const languages = [Type.Literal('fr'), Type.Literal('en')] -export const themes = [Type.Literal('light'), Type.Literal('dark')] +export const languages = [Type.Literal("fr"), Type.Literal("en")] +export const themes = [Type.Literal("light"), Type.Literal("dark")] export const userSettingsSchema = { id, @@ -15,7 +15,7 @@ export const userSettingsSchema = { isPublicGuilds: Type.Boolean(), createdAt: date.createdAt, updatedAt: date.updatedAt, - userId: id + userId: id, } export type Theme = Static @@ -23,11 +23,11 @@ export type Language = Static export const userSettingsExample: UserSetting = { id: 1, - theme: 'dark', - language: 'en', + theme: "dark", + language: "en", isPublicEmail: false, isPublicGuilds: false, userId: 1, createdAt: new Date(), - updatedAt: new Date() + updatedAt: new Date(), } diff --git a/src/models/utils.ts b/src/models/utils.ts index c66b0df..8541359 100644 --- a/src/models/utils.ts +++ b/src/models/utils.ts @@ -1,51 +1,51 @@ -import { Type } from '@sinclair/typebox' +import { Type } from "@sinclair/typebox" export const date = { createdAt: Type.String({ - format: 'date-time', - description: 'Created date time' + format: "date-time", + description: "Created date time", }), updatedAt: Type.String({ - format: 'date-time', - description: 'Last updated date time' - }) + format: "date-time", + description: "Last updated date time", + }), } -export const id = Type.Integer({ minimum: 1, description: 'Unique identifier' }) +export const id = Type.Integer({ minimum: 1, description: "Unique identifier" }) -export const redirectURI = Type.String({ format: 'uri-reference' }) +export const redirectURI = Type.String({ format: "uri-reference" }) export const fastifyErrorsSchema = { 400: { statusCode: Type.Literal(400), - error: Type.Literal('Bad Request'), - message: Type.String() + error: Type.Literal("Bad Request"), + message: Type.String(), }, 401: { statusCode: Type.Literal(401), - error: Type.Literal('Unauthorized'), - message: Type.Literal('Unauthorized') + error: Type.Literal("Unauthorized"), + message: Type.Literal("Unauthorized"), }, 403: { statusCode: Type.Literal(403), - error: Type.Literal('Forbidden'), - message: Type.Literal('Forbidden') + error: Type.Literal("Forbidden"), + message: Type.Literal("Forbidden"), }, 404: { statusCode: Type.Literal(404), - error: Type.Literal('Not Found'), - message: Type.String() + error: Type.Literal("Not Found"), + message: Type.String(), }, 431: { statusCode: Type.Literal(431), - error: Type.Literal('Request Header Fields Too Large'), - message: Type.String() + error: Type.Literal("Request Header Fields Too Large"), + message: Type.String(), }, 500: { statusCode: Type.Literal(500), - error: Type.Literal('Internal Server Error'), - message: Type.Literal('Something went wrong') - } + error: Type.Literal("Internal Server Error"), + message: Type.Literal("Something went wrong"), + }, } export const fastifyErrors = { @@ -54,5 +54,5 @@ export const fastifyErrors = { 403: Type.Object(fastifyErrorsSchema[403]), 404: Type.Object(fastifyErrorsSchema[404]), 431: Type.Object(fastifyErrorsSchema[431]), - 500: Type.Object(fastifyErrorsSchema[500]) + 500: Type.Object(fastifyErrorsSchema[500]), } diff --git a/src/scripts/generate-jwt-secret.ts b/src/scripts/generate-jwt-secret.ts index e348a38..accd80b 100644 --- a/src/scripts/generate-jwt-secret.ts +++ b/src/scripts/generate-jwt-secret.ts @@ -1,3 +1,3 @@ -import crypto from 'node:crypto' +import crypto from "node:crypto" -console.log(crypto.randomBytes(256).toString('base64')) +console.log(crypto.randomBytes(256).toString("base64")) diff --git a/src/services/channels/[channelId]/__test__/delete.test.ts b/src/services/channels/[channelId]/__test__/delete.test.ts index 3b65595..fd995c0 100644 --- a/src/services/channels/[channelId]/__test__/delete.test.ts +++ b/src/services/channels/[channelId]/__test__/delete.test.ts @@ -1,30 +1,30 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import sinon from 'sinon' +import sinon from "sinon" -import { application } from '#src/application.js' -import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' -import prisma from '#src/tools/database/prisma.js' -import { channelExample } from '#src/models/Channel.js' -import { memberExample } from '#src/models/Member.js' +import { application } from "#src/application.js" +import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" +import prisma from "#src/tools/database/prisma.js" +import { channelExample } from "#src/models/Channel.js" +import { memberExample } from "#src/models/Member.js" -await test('DELETE /channels/[channelId]', async (t) => { +await test("DELETE /channels/[channelId]", async (t) => { t.afterEach(() => { sinon.restore() }) - await t.test('succeeds', async () => { + await t.test("succeeds", async () => { const defaultChannelId = 5 const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'channel').value({ + sinon.stub(prisma, "channel").value({ findUnique: async () => { return channelExample }, findFirst: async () => { return { ...channelExample, - id: defaultChannelId + id: defaultChannelId, } }, count: async () => { @@ -32,19 +32,19 @@ await test('DELETE /channels/[channelId]', async (t) => { }, delete: async () => { return channelExample - } + }, }) - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return memberExample - } + }, }) const response = await application.inject({ - method: 'DELETE', + method: "DELETE", url: `/channels/${channelExample.id}`, headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 200) @@ -54,91 +54,91 @@ await test('DELETE /channels/[channelId]', async (t) => { assert.strictEqual(responseJson.defaultChannelId, defaultChannelId) }) - await t.test('fails if there is only one channel', async () => { + await t.test("fails if there is only one channel", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'channel').value({ + sinon.stub(prisma, "channel").value({ findUnique: async () => { return channelExample }, count: async () => { return 1 - } + }, }) - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return memberExample - } + }, }) const response = await application.inject({ - method: 'DELETE', + method: "DELETE", url: `/channels/${channelExample.id}`, headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) assert.strictEqual(response.statusCode, 400) }) - await t.test('fails if the channel is not found', async () => { + await t.test("fails if the channel is not found", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'channel').value({ + sinon.stub(prisma, "channel").value({ findUnique: async () => { return null - } + }, }) const response = await application.inject({ - method: 'DELETE', + method: "DELETE", url: `/channels/${channelExample.id}`, headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) assert.strictEqual(response.statusCode, 404) }) - await t.test('fails if the member is not found', async () => { + await t.test("fails if the member is not found", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'channel').value({ + sinon.stub(prisma, "channel").value({ findUnique: async () => { return channelExample - } + }, }) - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return null - } + }, }) const response = await application.inject({ - method: 'DELETE', + method: "DELETE", url: `/channels/${channelExample.id}`, headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) assert.strictEqual(response.statusCode, 404) }) - await t.test('fails if the member is not owner', async () => { + await t.test("fails if the member is not owner", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'channel').value({ + sinon.stub(prisma, "channel").value({ findUnique: async () => { return channelExample - } + }, }) - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return { ...memberExample, - isOwner: false + isOwner: false, } - } + }, }) const response = await application.inject({ - method: 'DELETE', + method: "DELETE", url: `/channels/${channelExample.id}`, headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) assert.strictEqual(response.statusCode, 400) }) diff --git a/src/services/channels/[channelId]/__test__/get.test.ts b/src/services/channels/[channelId]/__test__/get.test.ts index a1b0d39..fc4487d 100644 --- a/src/services/channels/[channelId]/__test__/get.test.ts +++ b/src/services/channels/[channelId]/__test__/get.test.ts @@ -1,37 +1,37 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import sinon from 'sinon' +import sinon from "sinon" -import { application } from '#src/application.js' -import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' -import prisma from '#src/tools/database/prisma.js' -import { channelExample } from '#src/models/Channel.js' -import { memberExample } from '#src/models/Member.js' +import { application } from "#src/application.js" +import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" +import prisma from "#src/tools/database/prisma.js" +import { channelExample } from "#src/models/Channel.js" +import { memberExample } from "#src/models/Member.js" -await test('GET /channels/[channelId]', async (t) => { +await test("GET /channels/[channelId]", async (t) => { t.afterEach(() => { sinon.restore() }) - await t.test('succeeds', async () => { + await t.test("succeeds", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'channel').value({ + sinon.stub(prisma, "channel").value({ findUnique: async () => { return channelExample - } + }, }) - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return memberExample - } + }, }) const response = await application.inject({ - method: 'GET', + method: "GET", url: `/channels/${channelExample.id}`, headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 200) @@ -40,58 +40,58 @@ await test('GET /channels/[channelId]', async (t) => { assert.strictEqual(responseJson.channel.guildId, channelExample.guildId) }) - await t.test('fails with not found member', async () => { + await t.test("fails with not found member", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'channel').value({ + sinon.stub(prisma, "channel").value({ findUnique: async () => { return channelExample - } + }, }) - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return null - } + }, }) const response = await application.inject({ - method: 'GET', + method: "GET", url: `/channels/${channelExample.id}`, headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 404) - assert.strictEqual(responseJson.message, 'Channel not found') + assert.strictEqual(responseJson.message, "Channel not found") }) - await t.test('fails with not found channel', async () => { + await t.test("fails with not found channel", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'channel').value({ + sinon.stub(prisma, "channel").value({ findUnique: async () => { return null - } + }, }) - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return memberExample - } + }, }) const response = await application.inject({ - method: 'GET', + method: "GET", url: `/channels/${channelExample.id}`, headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 404) - assert.strictEqual(responseJson.message, 'Channel not found') + assert.strictEqual(responseJson.message, "Channel not found") }) - await t.test('fails with unauthenticated user', async () => { + await t.test("fails with unauthenticated user", async () => { const response = await application.inject({ - method: 'GET', - url: '/channels/1' + method: "GET", + url: "/channels/1", }) assert.strictEqual(response.statusCode, 401) }) diff --git a/src/services/channels/[channelId]/__test__/put.test.ts b/src/services/channels/[channelId]/__test__/put.test.ts index d551d83..139b20d 100644 --- a/src/services/channels/[channelId]/__test__/put.test.ts +++ b/src/services/channels/[channelId]/__test__/put.test.ts @@ -1,53 +1,53 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import sinon from 'sinon' +import sinon from "sinon" -import { application } from '#src/application.js' -import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' -import prisma from '#src/tools/database/prisma.js' -import { channelExample } from '#src/models/Channel.js' -import { memberExample } from '#src/models/Member.js' +import { application } from "#src/application.js" +import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" +import prisma from "#src/tools/database/prisma.js" +import { channelExample } from "#src/models/Channel.js" +import { memberExample } from "#src/models/Member.js" -const newName = 'new channel name' +const newName = "new channel name" -await test('PUT /channels/[channelId]', async (t) => { +await test("PUT /channels/[channelId]", async (t) => { t.afterEach(() => { sinon.restore() }) - await t.test('succeeds', async () => { + await t.test("succeeds", async () => { const defaultChannelId = 5 const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'channel').value({ + sinon.stub(prisma, "channel").value({ findUnique: async () => { return channelExample }, findFirst: async () => { return { ...channelExample, - id: defaultChannelId + id: defaultChannelId, } }, update: async () => { return { ...channelExample, - name: newName + name: newName, } - } + }, }) - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return memberExample - } + }, }) const response = await application.inject({ - method: 'PUT', + method: "PUT", url: `/channels/${channelExample.id}`, headers: { - authorization: `Bearer ${accessToken}` + authorization: `Bearer ${accessToken}`, }, - payload: { name: newName } + payload: { name: newName }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 200) @@ -57,74 +57,74 @@ await test('PUT /channels/[channelId]', async (t) => { assert.strictEqual(responseJson.defaultChannelId, defaultChannelId) }) - await t.test('fails if the channel is not found', async () => { + await t.test("fails if the channel is not found", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'channel').value({ + sinon.stub(prisma, "channel").value({ findUnique: async () => { return null - } + }, }) - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return memberExample - } + }, }) const response = await application.inject({ - method: 'PUT', + method: "PUT", url: `/channels/${channelExample.id}`, headers: { - authorization: `Bearer ${accessToken}` + authorization: `Bearer ${accessToken}`, }, - payload: { name: newName } + payload: { name: newName }, }) assert.strictEqual(response.statusCode, 404) }) - await t.test('fails if the member is not found', async () => { + await t.test("fails if the member is not found", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'channel').value({ + sinon.stub(prisma, "channel").value({ findUnique: async () => { return channelExample - } + }, }) - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return null - } + }, }) const response = await application.inject({ - method: 'PUT', + method: "PUT", url: `/channels/${channelExample.id}`, headers: { - authorization: `Bearer ${accessToken}` + authorization: `Bearer ${accessToken}`, }, - payload: { name: newName } + payload: { name: newName }, }) assert.strictEqual(response.statusCode, 404) }) - await t.test('fails if the member is not owner', async () => { + await t.test("fails if the member is not owner", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'channel').value({ + sinon.stub(prisma, "channel").value({ findUnique: async () => { return channelExample - } + }, }) - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return { ...memberExample, - isOwner: false + isOwner: false, } - } + }, }) const response = await application.inject({ - method: 'PUT', + method: "PUT", url: `/channels/${channelExample.id}`, headers: { - authorization: `Bearer ${accessToken}` + authorization: `Bearer ${accessToken}`, }, - payload: { name: newName } + payload: { name: newName }, }) assert.strictEqual(response.statusCode, 400) }) diff --git a/src/services/channels/[channelId]/delete.ts b/src/services/channels/[channelId]/delete.ts index b32688a..b2a599b 100644 --- a/src/services/channels/[channelId]/delete.ts +++ b/src/services/channels/[channelId]/delete.ts @@ -1,38 +1,38 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import prisma from '#src/tools/database/prisma.js' -import { fastifyErrors } from '#src/models/utils.js' -import authenticateUser from '#src/tools/plugins/authenticateUser.js' -import { channelSchema } from '#src/models/Channel.js' +import prisma from "#src/tools/database/prisma.js" +import { fastifyErrors } from "#src/models/utils.js" +import authenticateUser from "#src/tools/plugins/authenticateUser.js" +import { channelSchema } from "#src/models/Channel.js" const parametersSchema = Type.Object({ - channelId: channelSchema.id + channelId: channelSchema.id, }) type Parameters = Static const deleteServiceSchema: FastifySchema = { - description: 'DELETE a channel with its id.', - tags: ['channels'] as string[], + description: "DELETE a channel with its id.", + tags: ["channels"] as string[], security: [ { - bearerAuth: [] - } + bearerAuth: [], + }, ] as Array<{ [key: string]: [] }>, params: parametersSchema, response: { 200: Type.Object({ ...channelSchema, - defaultChannelId: channelSchema.id + defaultChannelId: channelSchema.id, }), 400: fastifyErrors[400], 401: fastifyErrors[401], 403: fastifyErrors[403], 404: fastifyErrors[404], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const deleteChannelService: FastifyPluginAsync = async (fastify) => { @@ -41,8 +41,8 @@ export const deleteChannelService: FastifyPluginAsync = async (fastify) => { fastify.route<{ Params: Parameters }>({ - method: 'DELETE', - url: '/channels/:channelId', + method: "DELETE", + url: "/channels/:channelId", schema: deleteServiceSchema, handler: async (request, reply) => { if (request.user == null) { @@ -51,51 +51,51 @@ export const deleteChannelService: FastifyPluginAsync = async (fastify) => { const { user, params } = request const { channelId } = params const channelCheck = await prisma.channel.findUnique({ - where: { id: channelId } + where: { id: channelId }, }) if (channelCheck == null) { - throw fastify.httpErrors.notFound('Channel not found') + throw fastify.httpErrors.notFound("Channel not found") } const member = await prisma.member.findFirst({ - where: { guildId: channelCheck.guildId, userId: user.current.id } + where: { guildId: channelCheck.guildId, userId: user.current.id }, }) if (member == null) { - throw fastify.httpErrors.notFound('Member not found') + throw fastify.httpErrors.notFound("Member not found") } if (!member.isOwner) { - throw fastify.httpErrors.badRequest('You should be a member owner') + throw fastify.httpErrors.badRequest("You should be a member owner") } const channelCount = await prisma.channel.count({ - where: { guildId: channelCheck.guildId } + where: { guildId: channelCheck.guildId }, }) if (channelCount <= 1) { throw fastify.httpErrors.badRequest( - 'The guild should have at least one channel' + "The guild should have at least one channel", ) } const channel = await prisma.channel.delete({ - where: { id: channelId } + where: { id: channelId }, }) const defaultChannel = await prisma.channel.findFirst({ - where: { guildId: member.guildId } + where: { guildId: member.guildId }, }) if (defaultChannel == null) { throw fastify.httpErrors.internalServerError() } const item = { ...channel, - defaultChannelId: defaultChannel.id + defaultChannelId: defaultChannel.id, } await fastify.io.emitToMembers({ - event: 'channels', + event: "channels", guildId: member.guildId, payload: { - action: 'delete', - item - } + action: "delete", + item, + }, }) reply.statusCode = 200 return item - } + }, }) } diff --git a/src/services/channels/[channelId]/get.ts b/src/services/channels/[channelId]/get.ts index ab255e2..9dccf45 100644 --- a/src/services/channels/[channelId]/get.ts +++ b/src/services/channels/[channelId]/get.ts @@ -1,37 +1,37 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import prisma from '#src/tools/database/prisma.js' -import { fastifyErrors } from '#src/models/utils.js' -import authenticateUser from '#src/tools/plugins/authenticateUser.js' -import { channelSchema } from '#src/models/Channel.js' +import prisma from "#src/tools/database/prisma.js" +import { fastifyErrors } from "#src/models/utils.js" +import authenticateUser from "#src/tools/plugins/authenticateUser.js" +import { channelSchema } from "#src/models/Channel.js" const parametersSchema = Type.Object({ - channelId: channelSchema.id + channelId: channelSchema.id, }) type Parameters = Static const getServiceSchema: FastifySchema = { - description: 'GET a channel with its id.', - tags: ['channels'] as string[], + description: "GET a channel with its id.", + tags: ["channels"] as string[], security: [ { - bearerAuth: [] - } + bearerAuth: [], + }, ] as Array<{ [key: string]: [] }>, params: parametersSchema, response: { 200: Type.Object({ - channel: Type.Object(channelSchema) + channel: Type.Object(channelSchema), }), 400: fastifyErrors[400], 401: fastifyErrors[401], 403: fastifyErrors[403], 404: fastifyErrors[404], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const getChannelByIdService: FastifyPluginAsync = async (fastify) => { @@ -40,8 +40,8 @@ export const getChannelByIdService: FastifyPluginAsync = async (fastify) => { fastify.route<{ Params: Parameters }>({ - method: 'GET', - url: '/channels/:channelId', + method: "GET", + url: "/channels/:channelId", schema: getServiceSchema, handler: async (request, reply) => { if (request.user == null) { @@ -49,19 +49,19 @@ export const getChannelByIdService: FastifyPluginAsync = async (fastify) => { } const { channelId } = request.params const channel = await prisma.channel.findUnique({ - where: { id: channelId } + where: { id: channelId }, }) if (channel == null) { - throw fastify.httpErrors.notFound('Channel not found') + throw fastify.httpErrors.notFound("Channel not found") } const member = await prisma.member.findFirst({ - where: { guildId: channel.guildId, userId: request.user.current.id } + where: { guildId: channel.guildId, userId: request.user.current.id }, }) if (member == null) { - throw fastify.httpErrors.notFound('Channel not found') + throw fastify.httpErrors.notFound("Channel not found") } reply.statusCode = 200 return { channel } - } + }, }) } diff --git a/src/services/channels/[channelId]/messages/__test__/get.test.ts b/src/services/channels/[channelId]/messages/__test__/get.test.ts index 829e1eb..1df74d7 100644 --- a/src/services/channels/[channelId]/messages/__test__/get.test.ts +++ b/src/services/channels/[channelId]/messages/__test__/get.test.ts @@ -1,47 +1,47 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import sinon from 'sinon' +import sinon from "sinon" -import { application } from '#src/application.js' -import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' -import prisma from '#src/tools/database/prisma.js' -import { channelExample } from '#src/models/Channel.js' -import { memberExample } from '#src/models/Member.js' -import { userExample } from '#src/models/User.js' -import { messageExample } from '#src/models/Message.js' +import { application } from "#src/application.js" +import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" +import prisma from "#src/tools/database/prisma.js" +import { channelExample } from "#src/models/Channel.js" +import { memberExample } from "#src/models/Member.js" +import { userExample } from "#src/models/User.js" +import { messageExample } from "#src/models/Message.js" -await test('GET /channels/[channelId]/messages', async (t) => { +await test("GET /channels/[channelId]/messages", async (t) => { t.afterEach(() => { sinon.restore() }) - await t.test('succeeds', async () => { + await t.test("succeeds", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'channel').value({ + sinon.stub(prisma, "channel").value({ findUnique: async () => { return channelExample - } + }, }) - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return { ...memberExample, - user: userExample + user: userExample, } - } + }, }) - sinon.stub(prisma, 'message').value({ + sinon.stub(prisma, "message").value({ findMany: async () => { return [messageExample] - } + }, }) const response = await application.inject({ - method: 'GET', + method: "GET", url: `/channels/${channelExample.id}/messages`, headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 200) @@ -56,61 +56,61 @@ await test('GET /channels/[channelId]/messages', async (t) => { assert.strictEqual(responseJson[0].member.user.name, userExample.name) }) - await t.test('fails with not found channel', async () => { + await t.test("fails with not found channel", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'channel').value({ + sinon.stub(prisma, "channel").value({ findUnique: async () => { return null - } + }, }) - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return { ...memberExample, - user: userExample + user: userExample, } - } + }, }) const response = await application.inject({ - method: 'GET', + method: "GET", url: `/channels/${channelExample.id}/messages`, headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 404) - assert.strictEqual(responseJson.message, 'Channel not found') + assert.strictEqual(responseJson.message, "Channel not found") }) - await t.test('fails with not found member', async () => { + await t.test("fails with not found member", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'channel').value({ + sinon.stub(prisma, "channel").value({ findUnique: async () => { return channelExample - } + }, }) - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return null - } + }, }) const response = await application.inject({ - method: 'GET', + method: "GET", url: `/channels/${channelExample.id}/messages`, headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 404) - assert.strictEqual(responseJson.message, 'Channel not found') + assert.strictEqual(responseJson.message, "Channel not found") }) - await t.test('fails with unauthenticated user', async () => { + await t.test("fails with unauthenticated user", async () => { const response = await application.inject({ - method: 'GET', - url: `/channels/1/messages` + method: "GET", + url: `/channels/1/messages`, }) assert.strictEqual(response.statusCode, 401) }) diff --git a/src/services/channels/[channelId]/messages/__test__/post.test.ts b/src/services/channels/[channelId]/messages/__test__/post.test.ts index 4e2e891..e06d4d1 100644 --- a/src/services/channels/[channelId]/messages/__test__/post.test.ts +++ b/src/services/channels/[channelId]/messages/__test__/post.test.ts @@ -1,48 +1,48 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import sinon from 'sinon' +import sinon from "sinon" -import { application } from '#src/application.js' -import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' -import prisma from '#src/tools/database/prisma.js' -import { channelExample } from '#src/models/Channel.js' -import { memberExample } from '#src/models/Member.js' -import { userExample } from '#src/models/User.js' -import { messageExample } from '#src/models/Message.js' +import { application } from "#src/application.js" +import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" +import prisma from "#src/tools/database/prisma.js" +import { channelExample } from "#src/models/Channel.js" +import { memberExample } from "#src/models/Member.js" +import { userExample } from "#src/models/User.js" +import { messageExample } from "#src/models/Message.js" -await test('POST /channels/[channelId]/messages', async (t) => { +await test("POST /channels/[channelId]/messages", async (t) => { t.afterEach(() => { sinon.restore() }) - await t.test('succeeds', async () => { + await t.test("succeeds", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'channel').value({ + sinon.stub(prisma, "channel").value({ findUnique: async () => { return channelExample - } + }, }) - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return { ...memberExample, - user: userExample + user: userExample, } - } + }, }) - sinon.stub(prisma, 'message').value({ + sinon.stub(prisma, "message").value({ create: async () => { return messageExample - } + }, }) const response = await application.inject({ - method: 'POST', + method: "POST", url: `/channels/${channelExample.id}/messages`, headers: { - authorization: `Bearer ${accessToken}` + authorization: `Bearer ${accessToken}`, }, - payload: { value: messageExample.value } + payload: { value: messageExample.value }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 201) @@ -56,82 +56,82 @@ await test('POST /channels/[channelId]/messages', async (t) => { assert.strictEqual(responseJson.member.user.name, userExample.name) }) - await t.test('fails with no message value', async () => { + await t.test("fails with no message value", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'channel').value({ + sinon.stub(prisma, "channel").value({ findUnique: async () => { return channelExample - } + }, }) - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return { ...memberExample, - user: userExample + user: userExample, } - } + }, }) const response = await application.inject({ - method: 'POST', + method: "POST", url: `/channels/${channelExample.id}/messages`, headers: { - authorization: `Bearer ${accessToken}` + authorization: `Bearer ${accessToken}`, }, - payload: {} + payload: {}, }) assert.strictEqual(response.statusCode, 400) }) - await t.test('fails with not found channel', async () => { + await t.test("fails with not found channel", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'channel').value({ + sinon.stub(prisma, "channel").value({ findUnique: async () => { return null - } + }, }) - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return { ...memberExample, - user: userExample + user: userExample, } - } + }, }) const response = await application.inject({ - method: 'POST', - url: '/channels/5/messages', + method: "POST", + url: "/channels/5/messages", headers: { - authorization: `Bearer ${accessToken}` + authorization: `Bearer ${accessToken}`, }, - payload: { value: messageExample.value } + payload: { value: messageExample.value }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 404) - assert.strictEqual(responseJson.message, 'Channel not found') + assert.strictEqual(responseJson.message, "Channel not found") }) - await t.test('fails with not found member', async () => { + await t.test("fails with not found member", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'channel').value({ + sinon.stub(prisma, "channel").value({ findUnique: async () => { return channelExample - } + }, }) - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return null - } + }, }) const response = await application.inject({ - method: 'POST', + method: "POST", url: `/channels/${channelExample.id}/messages`, headers: { - authorization: `Bearer ${accessToken}` + authorization: `Bearer ${accessToken}`, }, - payload: { value: messageExample.value } + payload: { value: messageExample.value }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 404) - assert.strictEqual(responseJson.message, 'Channel not found') + assert.strictEqual(responseJson.message, "Channel not found") }) }) diff --git a/src/services/channels/[channelId]/messages/get.ts b/src/services/channels/[channelId]/messages/get.ts index ac9ee19..fb14da1 100644 --- a/src/services/channels/[channelId]/messages/get.ts +++ b/src/services/channels/[channelId]/messages/get.ts @@ -1,34 +1,34 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import prisma from '#src/tools/database/prisma.js' -import { fastifyErrors } from '#src/models/utils.js' -import authenticateUser from '#src/tools/plugins/authenticateUser.js' -import { messageSchema } from '#src/models/Message.js' -import { memberSchema } from '#src/models/Member.js' -import { userPublicWithoutSettingsSchema } from '#src/models/User.js' +import prisma from "#src/tools/database/prisma.js" +import { fastifyErrors } from "#src/models/utils.js" +import authenticateUser from "#src/tools/plugins/authenticateUser.js" +import { messageSchema } from "#src/models/Message.js" +import { memberSchema } from "#src/models/Member.js" +import { userPublicWithoutSettingsSchema } from "#src/models/User.js" import { getPaginationOptions, - queryPaginationObjectSchema -} from '#src/tools/database/pagination.js' -import { channelSchema } from '#src/models/Channel.js' + queryPaginationObjectSchema, +} from "#src/tools/database/pagination.js" +import { channelSchema } from "#src/models/Channel.js" type QuerySchemaType = Static const parametersSchema = Type.Object({ - channelId: channelSchema.id + channelId: channelSchema.id, }) type Parameters = Static const getServiceSchema: FastifySchema = { - description: 'GET all the messages of a channel by its id.', - tags: ['messages'] as string[], + description: "GET all the messages of a channel by its id.", + tags: ["messages"] as string[], security: [ { - bearerAuth: [] - } + bearerAuth: [], + }, ] as Array<{ [key: string]: [] }>, params: parametersSchema, querystring: queryPaginationObjectSchema, @@ -38,20 +38,20 @@ const getServiceSchema: FastifySchema = { ...messageSchema, member: Type.Object({ ...memberSchema, - user: Type.Object(userPublicWithoutSettingsSchema) - }) - }) + user: Type.Object(userPublicWithoutSettingsSchema), + }), + }), ), 400: fastifyErrors[400], 401: fastifyErrors[401], 403: fastifyErrors[403], 404: fastifyErrors[404], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const getMessagesByChannelIdService: FastifyPluginAsync = async ( - fastify + fastify, ) => { await fastify.register(authenticateUser) @@ -59,8 +59,8 @@ export const getMessagesByChannelIdService: FastifyPluginAsync = async ( Params: Parameters Querystring: QuerySchemaType }>({ - method: 'GET', - url: '/channels/:channelId/messages', + method: "GET", + url: "/channels/:channelId/messages", schema: getServiceSchema, handler: async (request, reply) => { if (request.user == null) { @@ -68,21 +68,21 @@ export const getMessagesByChannelIdService: FastifyPluginAsync = async ( } const { channelId } = request.params const channel = await prisma.channel.findUnique({ - where: { id: channelId } + where: { id: channelId }, }) if (channel == null) { - throw fastify.httpErrors.notFound('Channel not found') + throw fastify.httpErrors.notFound("Channel not found") } const memberCheck = await prisma.member.findFirst({ - where: { guildId: channel.guildId, userId: request.user.current.id } + where: { guildId: channel.guildId, userId: request.user.current.id }, }) if (memberCheck == null) { - throw fastify.httpErrors.notFound('Channel not found') + throw fastify.httpErrors.notFound("Channel not found") } const messagesRequest = await prisma.message.findMany({ ...getPaginationOptions(request.query), - orderBy: { createdAt: 'desc' }, - where: { channelId } + orderBy: { createdAt: "desc" }, + where: { channelId }, }) const messages = await Promise.all( messagesRequest.reverse().map(async (message) => { @@ -98,10 +98,10 @@ export const getMessagesByChannelIdService: FastifyPluginAsync = async ( biography: true, website: true, createdAt: true, - updatedAt: true - } - } - } + updatedAt: true, + }, + }, + }, }) return { ...message, @@ -109,14 +109,14 @@ export const getMessagesByChannelIdService: FastifyPluginAsync = async ( ...member, user: { ...member?.user, - email: null - } - } + email: null, + }, + }, } - }) + }), ) reply.statusCode = 200 return messages - } + }, }) } diff --git a/src/services/channels/[channelId]/messages/post.ts b/src/services/channels/[channelId]/messages/post.ts index d542bdd..94464fb 100644 --- a/src/services/channels/[channelId]/messages/post.ts +++ b/src/services/channels/[channelId]/messages/post.ts @@ -1,35 +1,35 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import prisma from '#src/tools/database/prisma.js' -import { fastifyErrors } from '#src/models/utils.js' -import authenticateUser from '#src/tools/plugins/authenticateUser.js' -import { messageSchema } from '#src/models/Message.js' -import { channelSchema } from '#src/models/Channel.js' -import { memberSchema } from '#src/models/Member.js' -import { userPublicWithoutSettingsSchema } from '#src/models/User.js' +import prisma from "#src/tools/database/prisma.js" +import { fastifyErrors } from "#src/models/utils.js" +import authenticateUser from "#src/tools/plugins/authenticateUser.js" +import { messageSchema } from "#src/models/Message.js" +import { channelSchema } from "#src/models/Channel.js" +import { memberSchema } from "#src/models/Member.js" +import { userPublicWithoutSettingsSchema } from "#src/models/User.js" const parametersSchema = Type.Object({ - channelId: channelSchema.id + channelId: channelSchema.id, }) type Parameters = Static const bodyPostServiceSchema = Type.Object({ - value: messageSchema.value + value: messageSchema.value, }) type BodyPostServiceSchemaType = Static const postServiceSchema: FastifySchema = { description: - 'POST a new message (text) in a specific channel using its channelId.', - tags: ['messages'] as string[], + "POST a new message (text) in a specific channel using its channelId.", + tags: ["messages"] as string[], security: [ { - bearerAuth: [] - } + bearerAuth: [], + }, ] as Array<{ [key: string]: [] }>, body: bodyPostServiceSchema, params: parametersSchema, @@ -38,19 +38,19 @@ const postServiceSchema: FastifySchema = { ...messageSchema, member: Type.Object({ ...memberSchema, - user: Type.Object(userPublicWithoutSettingsSchema) - }) + user: Type.Object(userPublicWithoutSettingsSchema), + }), }), 400: fastifyErrors[400], 401: fastifyErrors[401], 403: fastifyErrors[403], 404: fastifyErrors[404], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const postMessageByChannelIdService: FastifyPluginAsync = async ( - fastify + fastify, ) => { await fastify.register(authenticateUser) @@ -58,8 +58,8 @@ export const postMessageByChannelIdService: FastifyPluginAsync = async ( Body: BodyPostServiceSchemaType Params: Parameters }>({ - method: 'POST', - url: '/channels/:channelId/messages', + method: "POST", + url: "/channels/:channelId/messages", schema: postServiceSchema, handler: async (request, reply) => { if (request.user == null) { @@ -67,10 +67,10 @@ export const postMessageByChannelIdService: FastifyPluginAsync = async ( } const { channelId } = request.params const channel = await prisma.channel.findUnique({ - where: { id: channelId } + where: { id: channelId }, }) if (channel == null) { - throw fastify.httpErrors.notFound('Channel not found') + throw fastify.httpErrors.notFound("Channel not found") } const memberCheck = await prisma.member.findFirst({ where: { guildId: channel.guildId, userId: request.user.current.id }, @@ -84,23 +84,23 @@ export const postMessageByChannelIdService: FastifyPluginAsync = async ( biography: true, website: true, createdAt: true, - updatedAt: true - } - } - } + updatedAt: true, + }, + }, + }, }) if (memberCheck == null) { - throw fastify.httpErrors.notFound('Channel not found') + throw fastify.httpErrors.notFound("Channel not found") } const { value } = request.body const message = await prisma.message.create({ data: { value, - type: 'text', - mimetype: 'text/plain', + type: "text", + mimetype: "text/plain", channelId, - memberId: memberCheck.id - } + memberId: memberCheck.id, + }, }) const item = { ...message, @@ -108,17 +108,17 @@ export const postMessageByChannelIdService: FastifyPluginAsync = async ( ...memberCheck, user: { ...memberCheck.user, - email: null - } - } + email: null, + }, + }, } await fastify.io.emitToMembers({ - event: 'messages', + event: "messages", guildId: item.member.guildId, - payload: { action: 'create', item } + payload: { action: "create", item }, }) reply.statusCode = 201 return item - } + }, }) } diff --git a/src/services/channels/[channelId]/messages/uploads/post.ts b/src/services/channels/[channelId]/messages/uploads/post.ts index 2cdf235..619b418 100644 --- a/src/services/channels/[channelId]/messages/uploads/post.ts +++ b/src/services/channels/[channelId]/messages/uploads/post.ts @@ -1,33 +1,33 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' -import fastifyMultipart from '@fastify/multipart' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" +import fastifyMultipart from "@fastify/multipart" -import prisma from '#src/tools/database/prisma.js' -import { fastifyErrors } from '#src/models/utils.js' -import authenticateUser from '#src/tools/plugins/authenticateUser.js' -import { messageSchema } from '#src/models/Message.js' -import { memberSchema } from '#src/models/Member.js' -import { userPublicWithoutSettingsSchema } from '#src/models/User.js' -import { channelSchema } from '#src/models/Channel.js' -import { uploadFile } from '#src/tools/utils/uploadFile.js' +import prisma from "#src/tools/database/prisma.js" +import { fastifyErrors } from "#src/models/utils.js" +import authenticateUser from "#src/tools/plugins/authenticateUser.js" +import { messageSchema } from "#src/models/Message.js" +import { memberSchema } from "#src/models/Member.js" +import { userPublicWithoutSettingsSchema } from "#src/models/User.js" +import { channelSchema } from "#src/models/Channel.js" +import { uploadFile } from "#src/tools/utils/uploadFile.js" const parametersSchema = Type.Object({ - channelId: channelSchema.id + channelId: channelSchema.id, }) type Parameters = Static const postServiceSchema: FastifySchema = { description: - 'POST a new message (file) in a specific channel using its channelId.', - tags: ['messages'] as string[], - consumes: ['multipart/form-data'] as string[], - produces: ['application/json'] as string[], + "POST a new message (file) in a specific channel using its channelId.", + tags: ["messages"] as string[], + consumes: ["multipart/form-data"] as string[], + produces: ["application/json"] as string[], security: [ { - bearerAuth: [] - } + bearerAuth: [], + }, ] as Array<{ [key: string]: [] }>, params: parametersSchema, response: { @@ -35,20 +35,20 @@ const postServiceSchema: FastifySchema = { ...messageSchema, member: Type.Object({ ...memberSchema, - user: Type.Object(userPublicWithoutSettingsSchema) - }) + user: Type.Object(userPublicWithoutSettingsSchema), + }), }), 400: fastifyErrors[400], 401: fastifyErrors[401], 403: fastifyErrors[403], 404: fastifyErrors[404], 431: fastifyErrors[431], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const postMessageUploadsByChannelIdService: FastifyPluginAsync = async ( - fastify + fastify, ) => { await fastify.register(authenticateUser) @@ -57,8 +57,8 @@ export const postMessageUploadsByChannelIdService: FastifyPluginAsync = async ( fastify.route<{ Params: Parameters }>({ - method: 'POST', - url: '/channels/:channelId/messages/uploads', + method: "POST", + url: "/channels/:channelId/messages/uploads", schema: postServiceSchema, handler: async (request, reply) => { if (request.user == null) { @@ -66,10 +66,10 @@ export const postMessageUploadsByChannelIdService: FastifyPluginAsync = async ( } const { channelId } = request.params const channel = await prisma.channel.findUnique({ - where: { id: channelId } + where: { id: channelId }, }) if (channel == null) { - throw fastify.httpErrors.notFound('Channel not found') + throw fastify.httpErrors.notFound("Channel not found") } const memberCheck = await prisma.member.findFirst({ where: { guildId: channel.guildId, userId: request.user.current.id }, @@ -83,27 +83,27 @@ export const postMessageUploadsByChannelIdService: FastifyPluginAsync = async ( biography: true, website: true, createdAt: true, - updatedAt: true - } - } - } + updatedAt: true, + }, + }, + }, }) if (memberCheck == null) { - throw fastify.httpErrors.notFound('Channel not found') + throw fastify.httpErrors.notFound("Channel not found") } const file = await uploadFile({ fastify, request, - folderInUploadsFolder: 'messages' + folderInUploadsFolder: "messages", }) const message = await prisma.message.create({ data: { value: file.pathToStoreInDatabase, - type: 'file', + type: "file", mimetype: file.mimetype, channelId, - memberId: memberCheck.id - } + memberId: memberCheck.id, + }, }) reply.statusCode = 201 const item = { @@ -112,16 +112,16 @@ export const postMessageUploadsByChannelIdService: FastifyPluginAsync = async ( ...memberCheck, user: { ...memberCheck.user, - email: null - } - } + email: null, + }, + }, } await fastify.io.emitToMembers({ - event: 'messages', + event: "messages", guildId: item.member.guildId, - payload: { action: 'create', item } + payload: { action: "create", item }, }) return item - } + }, }) } diff --git a/src/services/channels/[channelId]/put.ts b/src/services/channels/[channelId]/put.ts index cfb3ed4..f960fdd 100644 --- a/src/services/channels/[channelId]/put.ts +++ b/src/services/channels/[channelId]/put.ts @@ -1,45 +1,45 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import prisma from '#src/tools/database/prisma.js' -import { fastifyErrors } from '#src/models/utils.js' -import authenticateUser from '#src/tools/plugins/authenticateUser.js' -import { channelSchema } from '#src/models/Channel.js' +import prisma from "#src/tools/database/prisma.js" +import { fastifyErrors } from "#src/models/utils.js" +import authenticateUser from "#src/tools/plugins/authenticateUser.js" +import { channelSchema } from "#src/models/Channel.js" const bodyPutServiceSchema = Type.Object({ - name: channelSchema.name + name: channelSchema.name, }) type BodyPutServiceSchemaType = Static const parametersSchema = Type.Object({ - channelId: channelSchema.id + channelId: channelSchema.id, }) type Parameters = Static const putServiceSchema: FastifySchema = { - description: 'UPDATE a channel with its id.', - tags: ['channels'] as string[], + description: "UPDATE a channel with its id.", + tags: ["channels"] as string[], security: [ { - bearerAuth: [] - } + bearerAuth: [], + }, ] as Array<{ [key: string]: [] }>, params: parametersSchema, body: bodyPutServiceSchema, response: { 200: Type.Object({ ...channelSchema, - defaultChannelId: channelSchema.id + defaultChannelId: channelSchema.id, }), 400: fastifyErrors[400], 401: fastifyErrors[401], 403: fastifyErrors[403], 404: fastifyErrors[404], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const putChannelService: FastifyPluginAsync = async (fastify) => { @@ -49,8 +49,8 @@ export const putChannelService: FastifyPluginAsync = async (fastify) => { Body: BodyPutServiceSchemaType Params: Parameters }>({ - method: 'PUT', - url: '/channels/:channelId', + method: "PUT", + url: "/channels/:channelId", schema: putServiceSchema, handler: async (request, reply) => { if (request.user == null) { @@ -60,44 +60,44 @@ export const putChannelService: FastifyPluginAsync = async (fastify) => { const { channelId } = params const { name } = body const channelCheck = await prisma.channel.findUnique({ - where: { id: channelId } + where: { id: channelId }, }) if (channelCheck == null) { - throw fastify.httpErrors.notFound('Channel not found') + throw fastify.httpErrors.notFound("Channel not found") } const member = await prisma.member.findFirst({ - where: { guildId: channelCheck.guildId, userId: user.current.id } + where: { guildId: channelCheck.guildId, userId: user.current.id }, }) if (member == null) { - throw fastify.httpErrors.notFound('Member not found') + throw fastify.httpErrors.notFound("Member not found") } if (!member.isOwner) { - throw fastify.httpErrors.badRequest('You should be a member owner') + throw fastify.httpErrors.badRequest("You should be a member owner") } const channel = await prisma.channel.update({ where: { id: channelId }, - data: { name } + data: { name }, }) const defaultChannel = await prisma.channel.findFirst({ - where: { guildId: member.guildId } + where: { guildId: member.guildId }, }) if (defaultChannel == null) { throw fastify.httpErrors.internalServerError() } const item = { ...channel, - defaultChannelId: defaultChannel.id + defaultChannelId: defaultChannel.id, } await fastify.io.emitToMembers({ - event: 'channels', + event: "channels", guildId: member.guildId, payload: { - action: 'update', - item - } + action: "update", + item, + }, }) reply.statusCode = 200 return item - } + }, }) } diff --git a/src/services/channels/index.ts b/src/services/channels/index.ts index 761ae53..62d942e 100644 --- a/src/services/channels/index.ts +++ b/src/services/channels/index.ts @@ -1,11 +1,11 @@ -import type { FastifyPluginAsync } from 'fastify' +import type { FastifyPluginAsync } from "fastify" -import { deleteChannelService } from './[channelId]/delete.js' -import { getChannelByIdService } from './[channelId]/get.js' -import { getMessagesByChannelIdService } from './[channelId]/messages/get.js' -import { postMessageByChannelIdService } from './[channelId]/messages/post.js' -import { postMessageUploadsByChannelIdService } from './[channelId]/messages/uploads/post.js' -import { putChannelService } from './[channelId]/put.js' +import { deleteChannelService } from "./[channelId]/delete.js" +import { getChannelByIdService } from "./[channelId]/get.js" +import { getMessagesByChannelIdService } from "./[channelId]/messages/get.js" +import { postMessageByChannelIdService } from "./[channelId]/messages/post.js" +import { postMessageUploadsByChannelIdService } from "./[channelId]/messages/uploads/post.js" +import { putChannelService } from "./[channelId]/put.js" export const channelsService: FastifyPluginAsync = async (fastify) => { await fastify.register(getChannelByIdService) diff --git a/src/services/guilds/[guildId]/__test__/delete.test.ts b/src/services/guilds/[guildId]/__test__/delete.test.ts index 45eed09..12f3934 100644 --- a/src/services/guilds/[guildId]/__test__/delete.test.ts +++ b/src/services/guilds/[guildId]/__test__/delete.test.ts @@ -1,41 +1,41 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import sinon from 'sinon' +import sinon from "sinon" -import { application } from '#src/application.js' -import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' -import prisma from '#src/tools/database/prisma.js' -import { memberExample } from '#src/models/Member.js' -import { guildExample } from '#src/models/Guild.js' +import { application } from "#src/application.js" +import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" +import prisma from "#src/tools/database/prisma.js" +import { memberExample } from "#src/models/Member.js" +import { guildExample } from "#src/models/Guild.js" -await test('DELETE /guilds/[guildId]', async (t) => { +await test("DELETE /guilds/[guildId]", async (t) => { t.afterEach(() => { sinon.restore() }) - await t.test('succeeds and delete the guild', async () => { + await t.test("succeeds and delete the guild", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return { ...memberExample, isOwner: true, - guild: guildExample + guild: guildExample, } - } + }, }) - sinon.stub(prisma, 'guild').value({ + sinon.stub(prisma, "guild").value({ delete: async () => { return guildExample - } + }, }) const response = await application.inject({ - method: 'DELETE', + method: "DELETE", url: `/guilds/${guildExample.id}`, headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 200) @@ -46,44 +46,44 @@ await test('DELETE /guilds/[guildId]', async (t) => { await t.test("fails if the guild doesn't exist", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return null - } + }, }) const response = await application.inject({ - method: 'DELETE', + method: "DELETE", url: `/guilds/${guildExample.id}`, headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) assert.strictEqual(response.statusCode, 404) }) - await t.test('fails if the user is not the owner', async () => { + await t.test("fails if the user is not the owner", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return { ...memberExample, isOwner: false, - guild: guildExample + guild: guildExample, } - } + }, }) const response = await application.inject({ - method: 'DELETE', + method: "DELETE", url: `/guilds/${guildExample.id}`, headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 400) assert.strictEqual( responseJson.message, - 'You should be an owner of the guild' + "You should be an owner of the guild", ) }) }) diff --git a/src/services/guilds/[guildId]/__test__/get.test.ts b/src/services/guilds/[guildId]/__test__/get.test.ts index 46d0222..e01c003 100644 --- a/src/services/guilds/[guildId]/__test__/get.test.ts +++ b/src/services/guilds/[guildId]/__test__/get.test.ts @@ -1,48 +1,48 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import sinon from 'sinon' +import sinon from "sinon" -import { application } from '#src/application.js' -import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' -import prisma from '#src/tools/database/prisma.js' -import { memberExample } from '#src/models/Member.js' -import { guildExample } from '#src/models/Guild.js' -import { userExample } from '#src/models/User.js' -import { channelExample } from '#src/models/Channel.js' +import { application } from "#src/application.js" +import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" +import prisma from "#src/tools/database/prisma.js" +import { memberExample } from "#src/models/Member.js" +import { guildExample } from "#src/models/Guild.js" +import { userExample } from "#src/models/User.js" +import { channelExample } from "#src/models/Channel.js" const defaultChannelId = 5 -await test('GET /guilds/[guildId]', async (t) => { +await test("GET /guilds/[guildId]", async (t) => { t.afterEach(() => { sinon.restore() }) - await t.test('succeeds', async () => { + await t.test("succeeds", async () => { const { accessToken, user } = await authenticateUserTest() - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return { ...memberExample, guild: guildExample, - user: userExample + user: userExample, } - } + }, }) - sinon.stub(prisma, 'channel').value({ + sinon.stub(prisma, "channel").value({ findFirst: async () => { return { ...channelExample, - id: defaultChannelId + id: defaultChannelId, } - } + }, }) const response = await application.inject({ - method: 'GET', + method: "GET", url: `/guilds/${guildExample.id}`, headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 200) @@ -55,29 +55,29 @@ await test('GET /guilds/[guildId]', async (t) => { assert.strictEqual(responseJson.guild.defaultChannelId, defaultChannelId) }) - await t.test('fails with not found member/guild', async () => { + await t.test("fails with not found member/guild", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return null - } + }, }) const response = await application.inject({ - method: 'GET', - url: '/guilds/1', + method: "GET", + url: "/guilds/1", headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 404) - assert.strictEqual(responseJson.message, 'Member not found') + assert.strictEqual(responseJson.message, "Member not found") }) - await t.test('fails with unauthenticated user', async () => { + await t.test("fails with unauthenticated user", async () => { const response = await application.inject({ - method: 'GET', - url: '/guilds/1' + method: "GET", + url: "/guilds/1", }) assert.strictEqual(response.statusCode, 401) }) diff --git a/src/services/guilds/[guildId]/__test__/put.test.ts b/src/services/guilds/[guildId]/__test__/put.test.ts index 552cdc1..57b001a 100644 --- a/src/services/guilds/[guildId]/__test__/put.test.ts +++ b/src/services/guilds/[guildId]/__test__/put.test.ts @@ -1,62 +1,62 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import sinon from 'sinon' +import sinon from "sinon" -import { application } from '#src/application.js' -import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' -import prisma from '#src/tools/database/prisma.js' -import { memberExample } from '#src/models/Member.js' -import { guildExample } from '#src/models/Guild.js' -import { channelExample } from '#src/models/Channel.js' +import { application } from "#src/application.js" +import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" +import prisma from "#src/tools/database/prisma.js" +import { memberExample } from "#src/models/Member.js" +import { guildExample } from "#src/models/Guild.js" +import { channelExample } from "#src/models/Channel.js" const defaultChannelId = 5 -const newName = 'New guild name' -const newDescription = 'New guild description' +const newName = "New guild name" +const newDescription = "New guild description" -await test('PUT /guilds/[guildId]', async (t) => { +await test("PUT /guilds/[guildId]", async (t) => { t.afterEach(() => { sinon.restore() }) - await t.test('succeeds and edit the guild', async () => { + await t.test("succeeds and edit the guild", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return { ...memberExample, isOwner: true, - guild: guildExample + guild: guildExample, } - } + }, }) - sinon.stub(prisma, 'channel').value({ + sinon.stub(prisma, "channel").value({ findFirst: async () => { return { ...channelExample, - id: defaultChannelId + id: defaultChannelId, } - } + }, }) - sinon.stub(prisma, 'guild').value({ + sinon.stub(prisma, "guild").value({ update: async () => { return { ...guildExample, name: newName, - description: newDescription + description: newDescription, } - } + }, }) const response = await application.inject({ - method: 'PUT', + method: "PUT", url: `/guilds/${guildExample.id}`, headers: { - authorization: `Bearer ${accessToken}` + authorization: `Bearer ${accessToken}`, }, payload: { name: newName, - description: newDescription - } + description: newDescription, + }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 200) @@ -67,52 +67,52 @@ await test('PUT /guilds/[guildId]', async (t) => { await t.test("fails if the guild doesn't exist", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return null - } + }, }) const response = await application.inject({ - method: 'PUT', + method: "PUT", url: `/guilds/${guildExample.id}`, headers: { - authorization: `Bearer ${accessToken}` + authorization: `Bearer ${accessToken}`, }, payload: { name: newName, - description: newDescription - } + description: newDescription, + }, }) assert.strictEqual(response.statusCode, 404) }) - await t.test('fails if the user is not the owner', async () => { + await t.test("fails if the user is not the owner", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return { ...memberExample, isOwner: false, - guild: guildExample + guild: guildExample, } - } + }, }) const response = await application.inject({ - method: 'PUT', + method: "PUT", url: `/guilds/${guildExample.id}`, headers: { - authorization: `Bearer ${accessToken}` + authorization: `Bearer ${accessToken}`, }, payload: { name: newName, - description: newDescription - } + description: newDescription, + }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 400) assert.strictEqual( responseJson.message, - 'You should be an owner of the guild' + "You should be an owner of the guild", ) }) }) diff --git a/src/services/guilds/[guildId]/channels/__test__/get.test.ts b/src/services/guilds/[guildId]/channels/__test__/get.test.ts index 7437174..16c96bb 100644 --- a/src/services/guilds/[guildId]/channels/__test__/get.test.ts +++ b/src/services/guilds/[guildId]/channels/__test__/get.test.ts @@ -1,38 +1,38 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import sinon from 'sinon' +import sinon from "sinon" -import { application } from '#src/application.js' -import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' -import prisma from '#src/tools/database/prisma.js' -import { memberExample } from '#src/models/Member.js' -import { guildExample } from '#src/models/Guild.js' -import { channelExample } from '#src/models/Channel.js' +import { application } from "#src/application.js" +import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" +import prisma from "#src/tools/database/prisma.js" +import { memberExample } from "#src/models/Member.js" +import { guildExample } from "#src/models/Guild.js" +import { channelExample } from "#src/models/Channel.js" -await test('GET /guilds/[guildId]/channels', async (t) => { +await test("GET /guilds/[guildId]/channels", async (t) => { t.afterEach(() => { sinon.restore() }) - await t.test('succeeds', async () => { + await t.test("succeeds", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return memberExample - } + }, }) - sinon.stub(prisma, 'channel').value({ + sinon.stub(prisma, "channel").value({ findMany: async () => { return [channelExample] - } + }, }) const response = await application.inject({ - method: 'GET', + method: "GET", url: `/guilds/${guildExample.id}/channels`, headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 200) @@ -42,29 +42,29 @@ await test('GET /guilds/[guildId]/channels', async (t) => { assert.strictEqual(responseJson[0].guildId, channelExample.guildId) }) - await t.test('fails with not found member/guild', async () => { + await t.test("fails with not found member/guild", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return null - } + }, }) const response = await application.inject({ - method: 'GET', - url: '/guilds/1/channels', + method: "GET", + url: "/guilds/1/channels", headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 404) - assert.strictEqual(responseJson.message, 'Member not found') + assert.strictEqual(responseJson.message, "Member not found") }) - await t.test('fails with unauthenticated user', async () => { + await t.test("fails with unauthenticated user", async () => { const response = await application.inject({ - method: 'GET', - url: '/guilds/1/channels' + method: "GET", + url: "/guilds/1/channels", }) assert.strictEqual(response.statusCode, 401) }) diff --git a/src/services/guilds/[guildId]/channels/__test__/post.test.ts b/src/services/guilds/[guildId]/channels/__test__/post.test.ts index 2d3571a..1e1cb63 100644 --- a/src/services/guilds/[guildId]/channels/__test__/post.test.ts +++ b/src/services/guilds/[guildId]/channels/__test__/post.test.ts @@ -1,47 +1,47 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import sinon from 'sinon' +import sinon from "sinon" -import { application } from '#src/application.js' -import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' -import prisma from '#src/tools/database/prisma.js' -import { memberExample } from '#src/models/Member.js' -import { guildExample } from '#src/models/Guild.js' -import { channelExample } from '#src/models/Channel.js' +import { application } from "#src/application.js" +import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" +import prisma from "#src/tools/database/prisma.js" +import { memberExample } from "#src/models/Member.js" +import { guildExample } from "#src/models/Guild.js" +import { channelExample } from "#src/models/Channel.js" const defaultChannelId = 5 -await test('POST /guilds/[guildId]/channels', async (t) => { +await test("POST /guilds/[guildId]/channels", async (t) => { t.afterEach(() => { sinon.restore() }) - await t.test('succeeds', async () => { + await t.test("succeeds", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return memberExample - } + }, }) - sinon.stub(prisma, 'channel').value({ + sinon.stub(prisma, "channel").value({ findFirst: async () => { return { ...channelExample, - id: defaultChannelId + id: defaultChannelId, } }, create: async () => { return channelExample - } + }, }) const response = await application.inject({ - method: 'POST', + method: "POST", url: `/guilds/${guildExample.id}/channels`, headers: { - authorization: `Bearer ${accessToken}` + authorization: `Bearer ${accessToken}`, }, - payload: { name: channelExample.name } + payload: { name: channelExample.name }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 201) @@ -51,41 +51,41 @@ await test('POST /guilds/[guildId]/channels', async (t) => { assert.strictEqual(responseJson.defaultChannelId, defaultChannelId) }) - await t.test('fails if the member is not found', async () => { + await t.test("fails if the member is not found", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return null - } + }, }) const response = await application.inject({ - method: 'POST', + method: "POST", url: `/guilds/${guildExample.id}/channels`, headers: { - authorization: `Bearer ${accessToken}` + authorization: `Bearer ${accessToken}`, }, - payload: { name: channelExample.name } + payload: { name: channelExample.name }, }) assert.strictEqual(response.statusCode, 404) }) - await t.test('fails if the member is not owner', async () => { + await t.test("fails if the member is not owner", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return { ...memberExample, - isOwner: false + isOwner: false, } - } + }, }) const response = await application.inject({ - method: 'POST', + method: "POST", url: `/guilds/${guildExample.id}/channels`, headers: { - authorization: `Bearer ${accessToken}` + authorization: `Bearer ${accessToken}`, }, - payload: { name: channelExample.name } + payload: { name: channelExample.name }, }) assert.strictEqual(response.statusCode, 400) }) diff --git a/src/services/guilds/[guildId]/channels/get.ts b/src/services/guilds/[guildId]/channels/get.ts index 64b4cc7..9ee14ed 100644 --- a/src/services/guilds/[guildId]/channels/get.ts +++ b/src/services/guilds/[guildId]/channels/get.ts @@ -1,32 +1,32 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import prisma from '#src/tools/database/prisma.js' -import { fastifyErrors } from '#src/models/utils.js' -import authenticateUser from '#src/tools/plugins/authenticateUser.js' -import { guildSchema } from '#src/models/Guild.js' -import { channelSchema } from '#src/models/Channel.js' +import prisma from "#src/tools/database/prisma.js" +import { fastifyErrors } from "#src/models/utils.js" +import authenticateUser from "#src/tools/plugins/authenticateUser.js" +import { guildSchema } from "#src/models/Guild.js" +import { channelSchema } from "#src/models/Channel.js" import { getPaginationOptions, - queryPaginationObjectSchema -} from '#src/tools/database/pagination.js' + queryPaginationObjectSchema, +} from "#src/tools/database/pagination.js" type QuerySchemaType = Static const parametersSchema = Type.Object({ - guildId: guildSchema.id + guildId: guildSchema.id, }) type Parameters = Static const getServiceSchema: FastifySchema = { - description: 'GET all the channels of a guild with its id.', - tags: ['channels'] as string[], + description: "GET all the channels of a guild with its id.", + tags: ["channels"] as string[], security: [ { - bearerAuth: [] - } + bearerAuth: [], + }, ] as Array<{ [key: string]: [] }>, params: parametersSchema, querystring: queryPaginationObjectSchema, @@ -36,12 +36,12 @@ const getServiceSchema: FastifySchema = { 401: fastifyErrors[401], 403: fastifyErrors[403], 404: fastifyErrors[404], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const getChannelsByGuildIdService: FastifyPluginAsync = async ( - fastify + fastify, ) => { await fastify.register(authenticateUser) @@ -49,8 +49,8 @@ export const getChannelsByGuildIdService: FastifyPluginAsync = async ( Params: Parameters Querystring: QuerySchemaType }>({ - method: 'GET', - url: '/guilds/:guildId/channels', + method: "GET", + url: "/guilds/:guildId/channels", schema: getServiceSchema, handler: async (request, reply) => { if (request.user == null) { @@ -58,19 +58,19 @@ export const getChannelsByGuildIdService: FastifyPluginAsync = async ( } const { guildId } = request.params const member = await prisma.member.findFirst({ - where: { guildId, userId: request.user.current.id } + where: { guildId, userId: request.user.current.id }, }) if (member == null) { - throw fastify.httpErrors.notFound('Member not found') + throw fastify.httpErrors.notFound("Member not found") } const channels = await prisma.channel.findMany({ ...getPaginationOptions(request.query), where: { - guildId - } + guildId, + }, }) reply.statusCode = 200 return channels - } + }, }) } diff --git a/src/services/guilds/[guildId]/channels/post.ts b/src/services/guilds/[guildId]/channels/post.ts index 8486e37..4bad474 100644 --- a/src/services/guilds/[guildId]/channels/post.ts +++ b/src/services/guilds/[guildId]/channels/post.ts @@ -1,46 +1,46 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import prisma from '#src/tools/database/prisma.js' -import { fastifyErrors } from '#src/models/utils.js' -import authenticateUser from '#src/tools/plugins/authenticateUser.js' -import { channelSchema } from '#src/models/Channel.js' -import { guildSchema } from '#src/models/Guild.js' +import prisma from "#src/tools/database/prisma.js" +import { fastifyErrors } from "#src/models/utils.js" +import authenticateUser from "#src/tools/plugins/authenticateUser.js" +import { channelSchema } from "#src/models/Channel.js" +import { guildSchema } from "#src/models/Guild.js" const bodyPostServiceSchema = Type.Object({ - name: channelSchema.name + name: channelSchema.name, }) type BodyPostServiceSchemaType = Static const parametersSchema = Type.Object({ - guildId: guildSchema.id + guildId: guildSchema.id, }) type Parameters = Static const postChannelServiceSchema: FastifySchema = { - description: 'Create a channel.', - tags: ['channels'] as string[], + description: "Create a channel.", + tags: ["channels"] as string[], security: [ { - bearerAuth: [] - } + bearerAuth: [], + }, ] as Array<{ [key: string]: [] }>, body: bodyPostServiceSchema, params: parametersSchema, response: { 201: Type.Object({ ...channelSchema, - defaultChannelId: channelSchema.id + defaultChannelId: channelSchema.id, }), 400: fastifyErrors[400], 401: fastifyErrors[401], 403: fastifyErrors[403], 404: fastifyErrors[404], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const postChannelService: FastifyPluginAsync = async (fastify) => { @@ -50,8 +50,8 @@ export const postChannelService: FastifyPluginAsync = async (fastify) => { Body: BodyPostServiceSchemaType Params: Parameters }>({ - method: 'POST', - url: '/guilds/:guildId/channels', + method: "POST", + url: "/guilds/:guildId/channels", schema: postChannelServiceSchema, handler: async (request, reply) => { if (request.user == null) { @@ -61,40 +61,40 @@ export const postChannelService: FastifyPluginAsync = async (fastify) => { const { guildId } = params const { name } = body const member = await prisma.member.findFirst({ - where: { guildId, userId: user.current.id } + where: { guildId, userId: user.current.id }, }) if (member == null) { - throw fastify.httpErrors.notFound('Member not found') + throw fastify.httpErrors.notFound("Member not found") } if (!member.isOwner) { - throw fastify.httpErrors.badRequest('You should be a member owner') + throw fastify.httpErrors.badRequest("You should be a member owner") } const channel = await prisma.channel.create({ data: { name, - guildId - } + guildId, + }, }) const defaultChannel = await prisma.channel.findFirst({ - where: { guildId: member.guildId } + where: { guildId: member.guildId }, }) if (defaultChannel == null) { throw fastify.httpErrors.internalServerError() } const item = { ...channel, - defaultChannelId: defaultChannel.id + defaultChannelId: defaultChannel.id, } await fastify.io.emitToMembers({ - event: 'channels', + event: "channels", guildId, payload: { - action: 'create', - item - } + action: "create", + item, + }, }) reply.statusCode = 201 return item - } + }, }) } diff --git a/src/services/guilds/[guildId]/delete.ts b/src/services/guilds/[guildId]/delete.ts index 905094e..56a4411 100644 --- a/src/services/guilds/[guildId]/delete.ts +++ b/src/services/guilds/[guildId]/delete.ts @@ -1,25 +1,25 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import prisma from '#src/tools/database/prisma.js' -import { fastifyErrors } from '#src/models/utils.js' -import authenticateUser from '#src/tools/plugins/authenticateUser.js' -import { guildSchema } from '#src/models/Guild.js' +import prisma from "#src/tools/database/prisma.js" +import { fastifyErrors } from "#src/models/utils.js" +import authenticateUser from "#src/tools/plugins/authenticateUser.js" +import { guildSchema } from "#src/models/Guild.js" const parametersSchema = Type.Object({ - guildId: guildSchema.id + guildId: guildSchema.id, }) type Parameters = Static const deleteServiceSchema: FastifySchema = { - description: 'DELETE a guild with the guildId.', - tags: ['guilds'] as string[], + description: "DELETE a guild with the guildId.", + tags: ["guilds"] as string[], security: [ { - bearerAuth: [] - } + bearerAuth: [], + }, ] as Array<{ [key: string]: [] }>, params: parametersSchema, response: { @@ -28,8 +28,8 @@ const deleteServiceSchema: FastifySchema = { 401: fastifyErrors[401], 403: fastifyErrors[403], 404: fastifyErrors[404], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const deleteGuildByIdService: FastifyPluginAsync = async (fastify) => { @@ -38,8 +38,8 @@ export const deleteGuildByIdService: FastifyPluginAsync = async (fastify) => { fastify.route<{ Params: Parameters }>({ - method: 'DELETE', - url: '/guilds/:guildId', + method: "DELETE", + url: "/guilds/:guildId", schema: deleteServiceSchema, handler: async (request, reply) => { if (request.user == null) { @@ -49,30 +49,30 @@ export const deleteGuildByIdService: FastifyPluginAsync = async (fastify) => { const member = await prisma.member.findFirst({ where: { guildId, userId: request.user.current.id }, include: { - guild: true - } + guild: true, + }, }) if (member == null || member.guild == null) { - throw fastify.httpErrors.notFound('Member not found') + throw fastify.httpErrors.notFound("Member not found") } if (!member.isOwner) { throw fastify.httpErrors.badRequest( - 'You should be an owner of the guild' + "You should be an owner of the guild", ) } await fastify.io.emitToMembers({ - event: 'guilds', + event: "guilds", guildId: member.guildId, payload: { - action: 'delete', - item: member.guild - } + action: "delete", + item: member.guild, + }, }) const guild = await prisma.guild.delete({ - where: { id: member.guildId } + where: { id: member.guildId }, }) reply.statusCode = 200 return guild - } + }, }) } diff --git a/src/services/guilds/[guildId]/get.ts b/src/services/guilds/[guildId]/get.ts index 9569a2f..6cd8560 100644 --- a/src/services/guilds/[guildId]/get.ts +++ b/src/services/guilds/[guildId]/get.ts @@ -1,59 +1,59 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import prisma from '#src/tools/database/prisma.js' -import { fastifyErrors } from '#src/models/utils.js' -import authenticateUser from '#src/tools/plugins/authenticateUser.js' -import { guildSchema } from '#src/models/Guild.js' -import { memberSchema } from '#src/models/Member.js' -import { userPublicWithoutSettingsSchema } from '#src/models/User.js' -import { channelSchema } from '#src/models/Channel.js' +import prisma from "#src/tools/database/prisma.js" +import { fastifyErrors } from "#src/models/utils.js" +import authenticateUser from "#src/tools/plugins/authenticateUser.js" +import { guildSchema } from "#src/models/Guild.js" +import { memberSchema } from "#src/models/Member.js" +import { userPublicWithoutSettingsSchema } from "#src/models/User.js" +import { channelSchema } from "#src/models/Channel.js" const parametersSchema = Type.Object({ - guildId: guildSchema.id + guildId: guildSchema.id, }) type Parameters = Static const getServiceSchema: FastifySchema = { - description: 'GET a guild member with the guildId.', - tags: ['guilds'] as string[], + description: "GET a guild member with the guildId.", + tags: ["guilds"] as string[], security: [ { - bearerAuth: [] - } + bearerAuth: [], + }, ] as Array<{ [key: string]: [] }>, params: parametersSchema, response: { 200: Type.Object({ guild: Type.Object({ ...guildSchema, - defaultChannelId: channelSchema.id + defaultChannelId: channelSchema.id, }), member: Type.Object({ ...memberSchema, - user: Type.Object(userPublicWithoutSettingsSchema) - }) + user: Type.Object(userPublicWithoutSettingsSchema), + }), }), 400: fastifyErrors[400], 401: fastifyErrors[401], 403: fastifyErrors[403], 404: fastifyErrors[404], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const getGuildMemberByIdService: FastifyPluginAsync = async ( - fastify + fastify, ) => { await fastify.register(authenticateUser) fastify.route<{ Params: Parameters }>({ - method: 'GET', - url: '/guilds/:guildId', + method: "GET", + url: "/guilds/:guildId", schema: getServiceSchema, handler: async (request, reply) => { if (request.user == null) { @@ -72,17 +72,17 @@ export const getGuildMemberByIdService: FastifyPluginAsync = async ( biography: true, website: true, createdAt: true, - updatedAt: true - } + updatedAt: true, + }, }, - guild: true - } + guild: true, + }, }) if (member == null) { - throw fastify.httpErrors.notFound('Member not found') + throw fastify.httpErrors.notFound("Member not found") } const defaultChannel = await prisma.channel.findFirst({ - where: { guildId: member.guildId } + where: { guildId: member.guildId }, }) if (defaultChannel == null) { throw fastify.httpErrors.internalServerError() @@ -90,18 +90,18 @@ export const getGuildMemberByIdService: FastifyPluginAsync = async ( const item = { guild: { ...member.guild, - defaultChannelId: defaultChannel.id + defaultChannelId: defaultChannel.id, }, member: { ...member, user: { ...member.user, - email: null - } - } + email: null, + }, + }, } reply.statusCode = 200 return item - } + }, }) } diff --git a/src/services/guilds/[guildId]/icon/put.ts b/src/services/guilds/[guildId]/icon/put.ts index d07659f..709d6d5 100644 --- a/src/services/guilds/[guildId]/icon/put.ts +++ b/src/services/guilds/[guildId]/icon/put.ts @@ -1,44 +1,44 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' -import fastifyMultipart from '@fastify/multipart' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" +import fastifyMultipart from "@fastify/multipart" -import authenticateUser from '#src/tools/plugins/authenticateUser.js' -import { fastifyErrors } from '#src/models/utils.js' -import prisma from '#src/tools/database/prisma.js' -import { uploadFile } from '#src/tools/utils/uploadFile.js' -import { guildSchema } from '#src/models/Guild.js' -import { channelSchema } from '#src/models/Channel.js' +import authenticateUser from "#src/tools/plugins/authenticateUser.js" +import { fastifyErrors } from "#src/models/utils.js" +import prisma from "#src/tools/database/prisma.js" +import { uploadFile } from "#src/tools/utils/uploadFile.js" +import { guildSchema } from "#src/models/Guild.js" +import { channelSchema } from "#src/models/Channel.js" const parametersSchema = Type.Object({ - guildId: guildSchema.id + guildId: guildSchema.id, }) type Parameters = Static const putServiceSchema: FastifySchema = { - description: 'Edit the icon of the guild with its id', - tags: ['guilds'] as string[], - consumes: ['multipart/form-data'] as string[], - produces: ['application/json'] as string[], + description: "Edit the icon of the guild with its id", + tags: ["guilds"] as string[], + consumes: ["multipart/form-data"] as string[], + produces: ["application/json"] as string[], security: [ { - bearerAuth: [] - } + bearerAuth: [], + }, ] as Array<{ [key: string]: [] }>, params: parametersSchema, response: { 200: Type.Object({ ...guildSchema, - defaultChannelId: channelSchema.id + defaultChannelId: channelSchema.id, }), 400: fastifyErrors[400], 401: fastifyErrors[401], 403: fastifyErrors[403], 404: fastifyErrors[404], 431: fastifyErrors[431], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const putGuildIconById: FastifyPluginAsync = async (fastify) => { @@ -49,8 +49,8 @@ export const putGuildIconById: FastifyPluginAsync = async (fastify) => { fastify.route<{ Params: Parameters }>({ - method: 'PUT', - url: '/guilds/:guildId/icon', + method: "PUT", + url: "/guilds/:guildId/icon", schema: putServiceSchema, handler: async (request, reply) => { if (request.user == null) { @@ -64,14 +64,14 @@ export const putGuildIconById: FastifyPluginAsync = async (fastify) => { const file = await uploadFile({ fastify, request, - folderInUploadsFolder: 'guilds' + folderInUploadsFolder: "guilds", }) await prisma.guild.update({ where: { id: guildId }, - data: { icon: file.pathToStoreInDatabase } + data: { icon: file.pathToStoreInDatabase }, }) const defaultChannel = await prisma.channel.findFirst({ - where: { guildId: guild.id } + where: { guildId: guild.id }, }) if (defaultChannel == null) { throw fastify.httpErrors.internalServerError() @@ -79,15 +79,15 @@ export const putGuildIconById: FastifyPluginAsync = async (fastify) => { const item = { ...guild, icon: file.pathToStoreInDatabase, - defaultChannelId: defaultChannel.id + defaultChannelId: defaultChannel.id, } await fastify.io.emitToMembers({ - event: 'guilds', + event: "guilds", guildId: guild.id, - payload: { action: 'update', item } + payload: { action: "update", item }, }) reply.statusCode = 200 return item - } + }, }) } diff --git a/src/services/guilds/[guildId]/members/__test__/get.test.ts b/src/services/guilds/[guildId]/members/__test__/get.test.ts index c74d5cb..4bb985f 100644 --- a/src/services/guilds/[guildId]/members/__test__/get.test.ts +++ b/src/services/guilds/[guildId]/members/__test__/get.test.ts @@ -1,36 +1,36 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import sinon from 'sinon' +import sinon from "sinon" -import { application } from '#src/application.js' -import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' -import prisma from '#src/tools/database/prisma.js' -import { memberExample } from '#src/models/Member.js' -import { guildExample } from '#src/models/Guild.js' -import { userExample } from '#src/models/User.js' +import { application } from "#src/application.js" +import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" +import prisma from "#src/tools/database/prisma.js" +import { memberExample } from "#src/models/Member.js" +import { guildExample } from "#src/models/Guild.js" +import { userExample } from "#src/models/User.js" -await test('GET /guilds/[guildId]/members', async (t) => { +await test("GET /guilds/[guildId]/members", async (t) => { t.afterEach(() => { sinon.restore() }) - await t.test('succeeds', async () => { + await t.test("succeeds", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return memberExample }, findMany: async () => { return [{ ...memberExample, user: userExample }] - } + }, }) const response = await application.inject({ - method: 'GET', + method: "GET", url: `/guilds/${guildExample.id}/members`, headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 200) @@ -42,29 +42,29 @@ await test('GET /guilds/[guildId]/members', async (t) => { assert.strictEqual(responseJson[0].user.email, null) }) - await t.test('fails with not found member/guild', async () => { + await t.test("fails with not found member/guild", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return null - } + }, }) const response = await application.inject({ - method: 'GET', - url: '/guilds/1/members', + method: "GET", + url: "/guilds/1/members", headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 404) - assert.strictEqual(responseJson.message, 'Member not found') + assert.strictEqual(responseJson.message, "Member not found") }) - await t.test('fails with unauthenticated user', async () => { + await t.test("fails with unauthenticated user", async () => { const response = await application.inject({ - method: 'GET', - url: '/guilds/1/members' + method: "GET", + url: "/guilds/1/members", }) assert.strictEqual(response.statusCode, 401) }) diff --git a/src/services/guilds/[guildId]/members/get.ts b/src/services/guilds/[guildId]/members/get.ts index cf3f2d8..05efe0a 100644 --- a/src/services/guilds/[guildId]/members/get.ts +++ b/src/services/guilds/[guildId]/members/get.ts @@ -1,33 +1,33 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import prisma from '#src/tools/database/prisma.js' -import { fastifyErrors } from '#src/models/utils.js' -import authenticateUser from '#src/tools/plugins/authenticateUser.js' -import { guildSchema } from '#src/models/Guild.js' +import prisma from "#src/tools/database/prisma.js" +import { fastifyErrors } from "#src/models/utils.js" +import authenticateUser from "#src/tools/plugins/authenticateUser.js" +import { guildSchema } from "#src/models/Guild.js" import { getPaginationOptions, - queryPaginationObjectSchema -} from '#src/tools/database/pagination.js' -import { memberSchema } from '#src/models/Member.js' -import { userPublicWithoutSettingsSchema } from '#src/models/User.js' + queryPaginationObjectSchema, +} from "#src/tools/database/pagination.js" +import { memberSchema } from "#src/models/Member.js" +import { userPublicWithoutSettingsSchema } from "#src/models/User.js" type QuerySchemaType = Static const parametersSchema = Type.Object({ - guildId: guildSchema.id + guildId: guildSchema.id, }) type Parameters = Static const getServiceSchema: FastifySchema = { - description: 'GET all the members of a guild with its id.', - tags: ['members'] as string[], + description: "GET all the members of a guild with its id.", + tags: ["members"] as string[], security: [ { - bearerAuth: [] - } + bearerAuth: [], + }, ] as Array<{ [key: string]: [] }>, params: parametersSchema, querystring: queryPaginationObjectSchema, @@ -35,19 +35,19 @@ const getServiceSchema: FastifySchema = { 200: Type.Array( Type.Object({ ...memberSchema, - user: Type.Object(userPublicWithoutSettingsSchema) - }) + user: Type.Object(userPublicWithoutSettingsSchema), + }), ), 400: fastifyErrors[400], 401: fastifyErrors[401], 403: fastifyErrors[403], 404: fastifyErrors[404], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const getMembersByGuildIdService: FastifyPluginAsync = async ( - fastify + fastify, ) => { await fastify.register(authenticateUser) @@ -55,8 +55,8 @@ export const getMembersByGuildIdService: FastifyPluginAsync = async ( Params: Parameters Querystring: QuerySchemaType }>({ - method: 'GET', - url: '/guilds/:guildId/members', + method: "GET", + url: "/guilds/:guildId/members", schema: getServiceSchema, handler: async (request, reply) => { if (request.user == null) { @@ -64,14 +64,14 @@ export const getMembersByGuildIdService: FastifyPluginAsync = async ( } const { guildId } = request.params const memberCheck = await prisma.member.findFirst({ - where: { guildId, userId: request.user.current.id } + where: { guildId, userId: request.user.current.id }, }) if (memberCheck == null) { - throw fastify.httpErrors.notFound('Member not found') + throw fastify.httpErrors.notFound("Member not found") } const membersRequest = await prisma.member.findMany({ ...getPaginationOptions(request.query), - orderBy: { createdAt: 'asc' }, + orderBy: { createdAt: "asc" }, where: { guildId }, include: { user: { @@ -83,22 +83,22 @@ export const getMembersByGuildIdService: FastifyPluginAsync = async ( biography: true, website: true, createdAt: true, - updatedAt: true - } - } - } + updatedAt: true, + }, + }, + }, }) const members = membersRequest.map((member) => { return { ...member, user: { ...member.user, - email: null - } + email: null, + }, } }) reply.statusCode = 200 return members - } + }, }) } diff --git a/src/services/guilds/[guildId]/members/join/__test__/post.test.ts b/src/services/guilds/[guildId]/members/join/__test__/post.test.ts index c5dfe4b..ec399e5 100644 --- a/src/services/guilds/[guildId]/members/join/__test__/post.test.ts +++ b/src/services/guilds/[guildId]/members/join/__test__/post.test.ts @@ -1,49 +1,49 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import sinon from 'sinon' +import sinon from "sinon" -import { application } from '#src/application.js' -import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' -import prisma from '#src/tools/database/prisma.js' -import { memberExample } from '#src/models/Member.js' -import { guildExample } from '#src/models/Guild.js' -import { userExample } from '#src/models/User.js' -import { channelExample } from '#src/models/Channel.js' +import { application } from "#src/application.js" +import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" +import prisma from "#src/tools/database/prisma.js" +import { memberExample } from "#src/models/Member.js" +import { guildExample } from "#src/models/Guild.js" +import { userExample } from "#src/models/User.js" +import { channelExample } from "#src/models/Channel.js" const defaultChannelId = 5 -await test('POST /guilds/[guildId]/members/join', async (t) => { +await test("POST /guilds/[guildId]/members/join", async (t) => { t.afterEach(() => { sinon.restore() }) - await t.test('succeeds', async () => { + await t.test("succeeds", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return null }, create: async () => { return { ...memberExample, user: userExample } - } + }, }) - sinon.stub(prisma, 'channel').value({ + sinon.stub(prisma, "channel").value({ findFirst: async () => { return channelExample - } + }, }) - sinon.stub(prisma, 'guild').value({ + sinon.stub(prisma, "guild").value({ findUnique: async () => { return guildExample - } + }, }) const response = await application.inject({ - method: 'POST', + method: "POST", url: `/guilds/${guildExample.id}/members/join`, headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 201) @@ -56,59 +56,59 @@ await test('POST /guilds/[guildId]/members/join', async (t) => { assert.strictEqual(responseJson.guild.defaultChannelId, channelExample.id) }) - await t.test('fails if the guild is not found', async () => { + await t.test("fails if the guild is not found", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return null - } + }, }) - sinon.stub(prisma, 'channel').value({ + sinon.stub(prisma, "channel").value({ findFirst: async () => { return null - } + }, }) - sinon.stub(prisma, 'guild').value({ + sinon.stub(prisma, "guild").value({ findUnique: async () => { return null - } + }, }) const response = await application.inject({ - method: 'POST', + method: "POST", url: `/guilds/${guildExample.id}/members/join`, headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) assert.strictEqual(response.statusCode, 404) }) - await t.test('fails if the user is already in the guild', async () => { + await t.test("fails if the user is already in the guild", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return memberExample - } + }, }) - sinon.stub(prisma, 'channel').value({ + sinon.stub(prisma, "channel").value({ findFirst: async () => { return { ...channelExample, - id: defaultChannelId + id: defaultChannelId, } - } + }, }) - sinon.stub(prisma, 'guild').value({ + sinon.stub(prisma, "guild").value({ findUnique: async () => { return guildExample - } + }, }) const response = await application.inject({ - method: 'POST', + method: "POST", url: `/guilds/${guildExample.id}/members/join`, headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 400) diff --git a/src/services/guilds/[guildId]/members/join/post.ts b/src/services/guilds/[guildId]/members/join/post.ts index e3045a9..6eeaf64 100644 --- a/src/services/guilds/[guildId]/members/join/post.ts +++ b/src/services/guilds/[guildId]/members/join/post.ts @@ -1,28 +1,28 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import prisma from '#src/tools/database/prisma.js' -import { fastifyErrors, fastifyErrorsSchema, id } from '#src/models/utils.js' -import authenticateUser from '#src/tools/plugins/authenticateUser.js' -import { guildSchema } from '#src/models/Guild.js' -import { memberSchema } from '#src/models/Member.js' -import { userPublicWithoutSettingsSchema } from '#src/models/User.js' -import { channelSchema } from '#src/models/Channel.js' +import prisma from "#src/tools/database/prisma.js" +import { fastifyErrors, fastifyErrorsSchema, id } from "#src/models/utils.js" +import authenticateUser from "#src/tools/plugins/authenticateUser.js" +import { guildSchema } from "#src/models/Guild.js" +import { memberSchema } from "#src/models/Member.js" +import { userPublicWithoutSettingsSchema } from "#src/models/User.js" +import { channelSchema } from "#src/models/Channel.js" const parametersSchema = Type.Object({ - guildId: guildSchema.id + guildId: guildSchema.id, }) type Parameters = Static const postServiceSchema: FastifySchema = { - description: 'Join a guild (create a member).', - tags: ['members'] as string[], + description: "Join a guild (create a member).", + tags: ["members"] as string[], security: [ { - bearerAuth: [] - } + bearerAuth: [], + }, ] as Array<{ [key: string]: [] }>, params: parametersSchema, response: { @@ -30,19 +30,19 @@ const postServiceSchema: FastifySchema = { ...memberSchema, guild: Type.Object({ ...guildSchema, - defaultChannelId: id + defaultChannelId: id, }), - user: Type.Object(userPublicWithoutSettingsSchema) + user: Type.Object(userPublicWithoutSettingsSchema), }), 400: Type.Object({ ...fastifyErrorsSchema[400], - defaultChannelId: channelSchema.id + defaultChannelId: channelSchema.id, }), 401: fastifyErrors[401], 403: fastifyErrors[403], 404: fastifyErrors[404], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const postMemberService: FastifyPluginAsync = async (fastify) => { @@ -51,8 +51,8 @@ export const postMemberService: FastifyPluginAsync = async (fastify) => { fastify.route<{ Params: Parameters }>({ - method: 'POST', - url: '/guilds/:guildId/members/join', + method: "POST", + url: "/guilds/:guildId/members/join", schema: postServiceSchema, handler: async (request, reply) => { if (request.user == null) { @@ -62,16 +62,16 @@ export const postMemberService: FastifyPluginAsync = async (fastify) => { const { guildId } = params const guild = await prisma.guild.findUnique({ where: { - id: guildId - } + id: guildId, + }, }) if (guild == null) { - throw fastify.httpErrors.notFound('Guild not found') + throw fastify.httpErrors.notFound("Guild not found") } const defaultChannel = await prisma.channel.findFirst({ where: { - guildId - } + guildId, + }, }) if (defaultChannel == null) { throw fastify.httpErrors.internalServerError() @@ -79,22 +79,22 @@ export const postMemberService: FastifyPluginAsync = async (fastify) => { const memberCheck = await prisma.member.findFirst({ where: { userId: user.current.id, - guildId: guild.id - } + guildId: guild.id, + }, }) if (memberCheck != null) { throw fastify.httpErrors.createError( 400, - 'You are already in the guild', + "You are already in the guild", { - defaultChannelId: defaultChannel.id - } + defaultChannelId: defaultChannel.id, + }, ) } const member = await prisma.member.create({ data: { guildId, - userId: user.current.id + userId: user.current.id, }, include: { user: { @@ -106,33 +106,33 @@ export const postMemberService: FastifyPluginAsync = async (fastify) => { biography: true, website: true, createdAt: true, - updatedAt: true - } - } - } + updatedAt: true, + }, + }, + }, }) const item = { ...member, user: { ...member.user, - email: null + email: null, }, guild: { ...guild, - defaultChannelId: defaultChannel.id - } + defaultChannelId: defaultChannel.id, + }, } await fastify.io.emitToMembers({ - event: 'members', + event: "members", guildId, payload: { - action: 'create', - item - } + action: "create", + item, + }, }) reply.statusCode = 201 return item - } + }, }) } diff --git a/src/services/guilds/[guildId]/members/leave/__test__/delete.test.ts b/src/services/guilds/[guildId]/members/leave/__test__/delete.test.ts index 7f35831..8743246 100644 --- a/src/services/guilds/[guildId]/members/leave/__test__/delete.test.ts +++ b/src/services/guilds/[guildId]/members/leave/__test__/delete.test.ts @@ -1,39 +1,39 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import sinon from 'sinon' +import sinon from "sinon" -import { application } from '#src/application.js' -import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' -import prisma from '#src/tools/database/prisma.js' -import { memberExample } from '#src/models/Member.js' -import { guildExample } from '#src/models/Guild.js' +import { application } from "#src/application.js" +import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" +import prisma from "#src/tools/database/prisma.js" +import { memberExample } from "#src/models/Member.js" +import { guildExample } from "#src/models/Guild.js" -await test('DELETE /guilds/[guildId]/members/leave', async (t) => { +await test("DELETE /guilds/[guildId]/members/leave", async (t) => { t.afterEach(() => { sinon.restore() }) - await t.test('succeeds', async () => { + await t.test("succeeds", async () => { const { accessToken } = await authenticateUserTest() const member = { ...memberExample, - isOwner: false + isOwner: false, } - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return member }, delete: async () => { return member - } + }, }) const response = await application.inject({ - method: 'DELETE', + method: "DELETE", url: `/guilds/${guildExample.id}/members/leave`, headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 200) @@ -42,40 +42,40 @@ await test('DELETE /guilds/[guildId]/members/leave', async (t) => { assert.strictEqual(responseJson.userId, member.userId) }) - await t.test('fails if the member is not found', async () => { + await t.test("fails if the member is not found", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return null - } + }, }) const response = await application.inject({ - method: 'DELETE', + method: "DELETE", url: `/guilds/${guildExample.id}/members/leave`, headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) assert.strictEqual(response.statusCode, 404) }) - await t.test('fails if the member is owner', async () => { + await t.test("fails if the member is owner", async () => { const { accessToken } = await authenticateUserTest() const member = { ...memberExample, - isOwner: true + isOwner: true, } - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return member - } + }, }) const response = await application.inject({ - method: 'DELETE', + method: "DELETE", url: `/guilds/${guildExample.id}/members/leave`, headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) assert.strictEqual(response.statusCode, 400) }) diff --git a/src/services/guilds/[guildId]/members/leave/delete.ts b/src/services/guilds/[guildId]/members/leave/delete.ts index f235a84..37e56e3 100644 --- a/src/services/guilds/[guildId]/members/leave/delete.ts +++ b/src/services/guilds/[guildId]/members/leave/delete.ts @@ -1,26 +1,26 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import prisma from '#src/tools/database/prisma.js' -import { fastifyErrors } from '#src/models/utils.js' -import authenticateUser from '#src/tools/plugins/authenticateUser.js' -import { guildSchema } from '#src/models/Guild.js' -import { memberSchema } from '#src/models/Member.js' +import prisma from "#src/tools/database/prisma.js" +import { fastifyErrors } from "#src/models/utils.js" +import authenticateUser from "#src/tools/plugins/authenticateUser.js" +import { guildSchema } from "#src/models/Guild.js" +import { memberSchema } from "#src/models/Member.js" const parametersSchema = Type.Object({ - guildId: guildSchema.id + guildId: guildSchema.id, }) type Parameters = Static const deleteServiceSchema: FastifySchema = { - description: 'Leave a guild (delete a member).', - tags: ['members'] as string[], + description: "Leave a guild (delete a member).", + tags: ["members"] as string[], security: [ { - bearerAuth: [] - } + bearerAuth: [], + }, ] as Array<{ [key: string]: [] }>, params: parametersSchema, response: { @@ -29,8 +29,8 @@ const deleteServiceSchema: FastifySchema = { 401: fastifyErrors[401], 403: fastifyErrors[403], 404: fastifyErrors[404], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const deleteMemberService: FastifyPluginAsync = async (fastify) => { @@ -39,8 +39,8 @@ export const deleteMemberService: FastifyPluginAsync = async (fastify) => { fastify.route<{ Params: Parameters }>({ - method: 'DELETE', - url: '/guilds/:guildId/members/leave', + method: "DELETE", + url: "/guilds/:guildId/members/leave", schema: deleteServiceSchema, handler: async (request, reply) => { if (request.user == null) { @@ -49,27 +49,27 @@ export const deleteMemberService: FastifyPluginAsync = async (fastify) => { const { user, params } = request const { guildId } = params const member = await prisma.member.findFirst({ - where: { guildId, userId: user.current.id } + where: { guildId, userId: user.current.id }, }) if (member == null) { - throw fastify.httpErrors.notFound('Member not found') + throw fastify.httpErrors.notFound("Member not found") } if (member.isOwner) { throw fastify.httpErrors.badRequest( - "The member owner can't leave the guild (you can delete it instead)" + "The member owner can't leave the guild (you can delete it instead)", ) } await prisma.member.delete({ where: { id: member.id } }) await fastify.io.emitToMembers({ - event: 'members', + event: "members", guildId, payload: { - action: 'delete', - item: member - } + action: "delete", + item: member, + }, }) reply.statusCode = 200 return member - } + }, }) } diff --git a/src/services/guilds/[guildId]/put.ts b/src/services/guilds/[guildId]/put.ts index b670201..2aea435 100644 --- a/src/services/guilds/[guildId]/put.ts +++ b/src/services/guilds/[guildId]/put.ts @@ -1,48 +1,48 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import prisma from '#src/tools/database/prisma.js' -import { fastifyErrors } from '#src/models/utils.js' -import authenticateUser from '#src/tools/plugins/authenticateUser.js' -import { guildSchema } from '#src/models/Guild.js' -import { parseStringNullish } from '#src/tools/utils/parseStringNullish.js' -import { channelSchema } from '#src/models/Channel.js' +import prisma from "#src/tools/database/prisma.js" +import { fastifyErrors } from "#src/models/utils.js" +import authenticateUser from "#src/tools/plugins/authenticateUser.js" +import { guildSchema } from "#src/models/Guild.js" +import { parseStringNullish } from "#src/tools/utils/parseStringNullish.js" +import { channelSchema } from "#src/models/Channel.js" const parametersSchema = Type.Object({ - guildId: guildSchema.id + guildId: guildSchema.id, }) type Parameters = Static const bodyPutServiceSchema = Type.Object({ name: Type.Optional(guildSchema.name), - description: Type.Optional(guildSchema.description) + description: Type.Optional(guildSchema.description), }) type BodyPutServiceSchemaType = Static const putServiceSchema: FastifySchema = { - description: 'Update a guild with the guildId.', - tags: ['guilds'] as string[], + description: "Update a guild with the guildId.", + tags: ["guilds"] as string[], security: [ { - bearerAuth: [] - } + bearerAuth: [], + }, ] as Array<{ [key: string]: [] }>, body: bodyPutServiceSchema, params: parametersSchema, response: { 200: Type.Object({ ...guildSchema, - defaultChannelId: channelSchema.id + defaultChannelId: channelSchema.id, }), 400: fastifyErrors[400], 401: fastifyErrors[401], 403: fastifyErrors[403], 404: fastifyErrors[404], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const putGuildByIdService: FastifyPluginAsync = async (fastify) => { @@ -52,8 +52,8 @@ export const putGuildByIdService: FastifyPluginAsync = async (fastify) => { Body: BodyPutServiceSchemaType Params: Parameters }>({ - method: 'PUT', - url: '/guilds/:guildId', + method: "PUT", + url: "/guilds/:guildId", schema: putServiceSchema, handler: async (request, reply) => { if (request.user == null) { @@ -64,41 +64,44 @@ export const putGuildByIdService: FastifyPluginAsync = async (fastify) => { const member = await prisma.member.findFirst({ where: { guildId, userId: request.user.current.id }, include: { - guild: true - } + guild: true, + }, }) if (member == null || member.guild == null) { - throw fastify.httpErrors.notFound('Member not found') + throw fastify.httpErrors.notFound("Member not found") } if (!member.isOwner) { throw fastify.httpErrors.badRequest( - 'You should be an owner of the guild' + "You should be an owner of the guild", ) } const guild = await prisma.guild.update({ where: { id: guildId }, data: { name: name ?? member.guild.name, - description: parseStringNullish(member.guild.description, description) - } + description: parseStringNullish( + member.guild.description, + description, + ), + }, }) const defaultChannel = await prisma.channel.findFirst({ - where: { guildId: guild.id } + where: { guildId: guild.id }, }) if (defaultChannel == null) { throw fastify.httpErrors.internalServerError() } const item = { ...guild, - defaultChannelId: defaultChannel.id + defaultChannelId: defaultChannel.id, } await fastify.io.emitToMembers({ - event: 'guilds', + event: "guilds", guildId: guild.id, - payload: { action: 'update', item } + payload: { action: "update", item }, }) reply.statusCode = 200 return item - } + }, }) } diff --git a/src/services/guilds/__test__/get.test.ts b/src/services/guilds/__test__/get.test.ts index 968ce08..61cc99e 100644 --- a/src/services/guilds/__test__/get.test.ts +++ b/src/services/guilds/__test__/get.test.ts @@ -1,43 +1,43 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import sinon from 'sinon' +import sinon from "sinon" -import { application } from '#src/application.js' -import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' -import prisma from '#src/tools/database/prisma.js' -import { memberExample } from '#src/models/Member.js' -import { guildExample } from '#src/models/Guild.js' -import { channelExample } from '#src/models/Channel.js' +import { application } from "#src/application.js" +import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" +import prisma from "#src/tools/database/prisma.js" +import { memberExample } from "#src/models/Member.js" +import { guildExample } from "#src/models/Guild.js" +import { channelExample } from "#src/models/Channel.js" -await test('GET /guilds', async (t) => { +await test("GET /guilds", async (t) => { t.afterEach(() => { sinon.restore() }) - await t.test('succeeds', async () => { + await t.test("succeeds", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'guild').value({ + sinon.stub(prisma, "guild").value({ findUnique: async () => { return guildExample - } + }, }) - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findMany: async () => { return [memberExample] - } + }, }) - sinon.stub(prisma, 'channel').value({ + sinon.stub(prisma, "channel").value({ findFirst: async () => { return channelExample - } + }, }) const response = await application.inject({ - method: 'GET', - url: '/guilds', + method: "GET", + url: "/guilds", headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 200) diff --git a/src/services/guilds/__test__/post.test.ts b/src/services/guilds/__test__/post.test.ts index f34f348..ca9fc11 100644 --- a/src/services/guilds/__test__/post.test.ts +++ b/src/services/guilds/__test__/post.test.ts @@ -1,54 +1,54 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import sinon from 'sinon' +import sinon from "sinon" -import { application } from '#src/application.js' -import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' -import prisma from '#src/tools/database/prisma.js' -import { memberExample } from '#src/models/Member.js' -import { guildExample } from '#src/models/Guild.js' -import { channelExample } from '#src/models/Channel.js' -import { userExample } from '#src/models/User.js' +import { application } from "#src/application.js" +import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" +import prisma from "#src/tools/database/prisma.js" +import { memberExample } from "#src/models/Member.js" +import { guildExample } from "#src/models/Guild.js" +import { channelExample } from "#src/models/Channel.js" +import { userExample } from "#src/models/User.js" -await test('POST /guilds', async (t) => { +await test("POST /guilds", async (t) => { t.afterEach(() => { sinon.restore() }) - await t.test('succeeds', async () => { + await t.test("succeeds", async () => { const { accessToken, user } = await authenticateUserTest() - sinon.stub(prisma, 'guild').value({ + sinon.stub(prisma, "guild").value({ create: async () => { return guildExample - } + }, }) - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ create: async () => { return memberExample }, findUnique: async () => { return { ...memberExample, - ...userExample + ...userExample, } - } + }, }) - sinon.stub(prisma, 'channel').value({ + sinon.stub(prisma, "channel").value({ create: async () => { return channelExample - } + }, }) const response = await application.inject({ - method: 'POST', - url: '/guilds', + method: "POST", + url: "/guilds", headers: { - authorization: `Bearer ${accessToken}` + authorization: `Bearer ${accessToken}`, }, payload: { name: guildExample.name, - description: guildExample.description - } + description: guildExample.description, + }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 201) @@ -61,21 +61,21 @@ await test('POST /guilds', async (t) => { assert.strictEqual(responseJson.guild.members[0].guildId, guildExample.id) assert.strictEqual( responseJson.guild.members[0].isOwner, - memberExample.isOwner + memberExample.isOwner, ) assert.strictEqual(responseJson.guild.channels.length, 1) assert.strictEqual(responseJson.guild.channels[0].id, channelExample.id) assert.strictEqual(responseJson.guild.channels[0].guildId, guildExample.id) }) - await t.test('fails with empty name and description', async () => { + await t.test("fails with empty name and description", async () => { const { accessToken } = await authenticateUserTest() const response = await application.inject({ - method: 'POST', - url: '/guilds', + method: "POST", + url: "/guilds", headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) assert.strictEqual(response.statusCode, 400) }) diff --git a/src/services/guilds/get.ts b/src/services/guilds/get.ts index d1c9e51..31abe71 100644 --- a/src/services/guilds/get.ts +++ b/src/services/guilds/get.ts @@ -1,39 +1,39 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import prisma from '#src/tools/database/prisma.js' -import { fastifyErrors, id } from '#src/models/utils.js' -import authenticateUser from '#src/tools/plugins/authenticateUser.js' -import { guildSchema } from '#src/models/Guild.js' +import prisma from "#src/tools/database/prisma.js" +import { fastifyErrors, id } from "#src/models/utils.js" +import authenticateUser from "#src/tools/plugins/authenticateUser.js" +import { guildSchema } from "#src/models/Guild.js" import { getPaginationOptions, - queryPaginationObjectSchema -} from '#src/tools/database/pagination.js' + queryPaginationObjectSchema, +} from "#src/tools/database/pagination.js" type QuerySchemaType = Static const getServiceSchema: FastifySchema = { - description: 'GET all the guilds of an user.', - tags: ['guilds'] as string[], + description: "GET all the guilds of an user.", + tags: ["guilds"] as string[], security: [ { - bearerAuth: [] - } + bearerAuth: [], + }, ] as Array<{ [key: string]: [] }>, querystring: queryPaginationObjectSchema, response: { 200: Type.Array( Type.Object({ ...guildSchema, - defaultChannelId: id - }) + defaultChannelId: id, + }), ), 400: fastifyErrors[400], 401: fastifyErrors[401], 403: fastifyErrors[403], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const getGuilds: FastifyPluginAsync = async (fastify) => { @@ -42,8 +42,8 @@ export const getGuilds: FastifyPluginAsync = async (fastify) => { fastify.route<{ Querystring: QuerySchemaType }>({ - method: 'GET', - url: '/guilds', + method: "GET", + url: "/guilds", schema: getServiceSchema, handler: async (request, reply) => { if (request.user == null) { @@ -52,29 +52,29 @@ export const getGuilds: FastifyPluginAsync = async (fastify) => { const membersRequest = await prisma.member.findMany({ ...getPaginationOptions(request.query), where: { - userId: request.user.current.id - } + userId: request.user.current.id, + }, }) const guilds = await Promise.all( membersRequest.map(async (member) => { const channel = await prisma.channel.findFirst({ where: { - guildId: member.guildId - } + guildId: member.guildId, + }, }) const guild = await prisma.guild.findUnique({ where: { - id: member.guildId - } + id: member.guildId, + }, }) return { ...guild, - defaultChannelId: channel?.id + defaultChannelId: channel?.id, } - }) + }), ) reply.statusCode = 200 return guilds - } + }, }) } diff --git a/src/services/guilds/index.ts b/src/services/guilds/index.ts index e0c54d5..200f3ed 100644 --- a/src/services/guilds/index.ts +++ b/src/services/guilds/index.ts @@ -1,17 +1,17 @@ -import type { FastifyPluginAsync } from 'fastify' +import type { FastifyPluginAsync } from "fastify" -import { getGuilds } from './get.js' -import { postGuilds } from './post.js' -import { getGuildsPublic } from './public/get.js' -import { getChannelsByGuildIdService } from './[guildId]/channels/get.js' -import { postChannelService } from './[guildId]/channels/post.js' -import { deleteGuildByIdService } from './[guildId]/delete.js' -import { getGuildMemberByIdService } from './[guildId]/get.js' -import { putGuildIconById } from './[guildId]/icon/put.js' -import { getMembersByGuildIdService } from './[guildId]/members/get.js' -import { postMemberService } from './[guildId]/members/join/post.js' -import { deleteMemberService } from './[guildId]/members/leave/delete.js' -import { putGuildByIdService } from './[guildId]/put.js' +import { getGuilds } from "./get.js" +import { postGuilds } from "./post.js" +import { getGuildsPublic } from "./public/get.js" +import { getChannelsByGuildIdService } from "./[guildId]/channels/get.js" +import { postChannelService } from "./[guildId]/channels/post.js" +import { deleteGuildByIdService } from "./[guildId]/delete.js" +import { getGuildMemberByIdService } from "./[guildId]/get.js" +import { putGuildIconById } from "./[guildId]/icon/put.js" +import { getMembersByGuildIdService } from "./[guildId]/members/get.js" +import { postMemberService } from "./[guildId]/members/join/post.js" +import { deleteMemberService } from "./[guildId]/members/leave/delete.js" +import { putGuildByIdService } from "./[guildId]/put.js" export const guildsService: FastifyPluginAsync = async (fastify) => { await fastify.register(postGuilds) diff --git a/src/services/guilds/post.ts b/src/services/guilds/post.ts index 0faa943..1d80195 100644 --- a/src/services/guilds/post.ts +++ b/src/services/guilds/post.ts @@ -1,30 +1,30 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import prisma from '#src/tools/database/prisma.js' -import { fastifyErrors } from '#src/models/utils.js' -import authenticateUser from '#src/tools/plugins/authenticateUser.js' -import { guildSchema } from '#src/models/Guild.js' -import { channelSchema } from '#src/models/Channel.js' -import { memberSchema } from '#src/models/Member.js' -import { userPublicWithoutSettingsSchema } from '#src/models/User.js' -import { parseStringNullish } from '#src/tools/utils/parseStringNullish.js' +import prisma from "#src/tools/database/prisma.js" +import { fastifyErrors } from "#src/models/utils.js" +import authenticateUser from "#src/tools/plugins/authenticateUser.js" +import { guildSchema } from "#src/models/Guild.js" +import { channelSchema } from "#src/models/Channel.js" +import { memberSchema } from "#src/models/Member.js" +import { userPublicWithoutSettingsSchema } from "#src/models/User.js" +import { parseStringNullish } from "#src/tools/utils/parseStringNullish.js" const bodyPostServiceSchema = Type.Object({ name: guildSchema.name, - description: guildSchema.description + description: guildSchema.description, }) type BodyPostServiceSchemaType = Static const postServiceSchema: FastifySchema = { - description: 'Create a guild.', - tags: ['guilds'] as string[], + description: "Create a guild.", + tags: ["guilds"] as string[], security: [ { - bearerAuth: [] - } + bearerAuth: [], + }, ] as Array<{ [key: string]: [] }>, body: bodyPostServiceSchema, response: { @@ -35,16 +35,16 @@ const postServiceSchema: FastifySchema = { members: Type.Array( Type.Object({ ...memberSchema, - user: Type.Object(userPublicWithoutSettingsSchema) - }) - ) - }) + user: Type.Object(userPublicWithoutSettingsSchema), + }), + ), + }), }), 400: fastifyErrors[400], 401: fastifyErrors[401], 403: fastifyErrors[403], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const postGuilds: FastifyPluginAsync = async (fastify) => { @@ -53,8 +53,8 @@ export const postGuilds: FastifyPluginAsync = async (fastify) => { fastify.route<{ Body: BodyPostServiceSchemaType }>({ - method: 'POST', - url: '/guilds', + method: "POST", + url: "/guilds", schema: postServiceSchema, handler: async (request, reply) => { if (request.user == null) { @@ -62,37 +62,37 @@ export const postGuilds: FastifyPluginAsync = async (fastify) => { } const { name, description } = request.body const guild = await prisma.guild.create({ - data: { name, description: parseStringNullish(description) } + data: { name, description: parseStringNullish(description) }, }) const channel = await prisma.channel.create({ - data: { name: 'general', guildId: guild.id } + data: { name: "general", guildId: guild.id }, }) const memberCreated = await prisma.member.create({ data: { userId: request.user.current.id, isOwner: true, - guildId: guild.id - } + guildId: guild.id, + }, }) const members = await Promise.all( [memberCreated].map(async (member) => { const user = await prisma.user.findUnique({ - where: { id: member?.userId } + where: { id: member?.userId }, }) return { ...member, - user + user, } - }) + }), ) reply.statusCode = 201 return { guild: { ...guild, channels: [channel], - members - } + members, + }, } - } + }, }) } diff --git a/src/services/guilds/public/__test__/get.test.ts b/src/services/guilds/public/__test__/get.test.ts index 1b971db..1ab4359 100644 --- a/src/services/guilds/public/__test__/get.test.ts +++ b/src/services/guilds/public/__test__/get.test.ts @@ -1,36 +1,36 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import sinon from 'sinon' +import sinon from "sinon" -import { application } from '#src/application.js' -import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' -import prisma from '#src/tools/database/prisma.js' -import { guildExample } from '#src/models/Guild.js' +import { application } from "#src/application.js" +import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" +import prisma from "#src/tools/database/prisma.js" +import { guildExample } from "#src/models/Guild.js" -await test('GET /guilds/public', async (t) => { +await test("GET /guilds/public", async (t) => { t.afterEach(() => { sinon.restore() }) - await t.test('succeeds', async () => { + await t.test("succeeds", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'guild').value({ + sinon.stub(prisma, "guild").value({ findMany: async () => { return [guildExample] - } + }, }) - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ count: async () => { return 2 - } + }, }) const response = await application.inject({ - method: 'GET', - url: '/guilds/public', + method: "GET", + url: "/guilds/public", headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 200) diff --git a/src/services/guilds/public/get.ts b/src/services/guilds/public/get.ts index 3b705c8..5d8284b 100644 --- a/src/services/guilds/public/get.ts +++ b/src/services/guilds/public/get.ts @@ -1,45 +1,45 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import prisma from '#src/tools/database/prisma.js' -import { fastifyErrors } from '#src/models/utils.js' -import authenticateUser from '#src/tools/plugins/authenticateUser.js' -import { guildSchema } from '#src/models/Guild.js' +import prisma from "#src/tools/database/prisma.js" +import { fastifyErrors } from "#src/models/utils.js" +import authenticateUser from "#src/tools/plugins/authenticateUser.js" +import { guildSchema } from "#src/models/Guild.js" import { getPaginationOptions, - queryPaginationSchema -} from '#src/tools/database/pagination.js' + queryPaginationSchema, +} from "#src/tools/database/pagination.js" const querySchema = Type.Object({ search: Type.Optional(Type.String()), - ...queryPaginationSchema + ...queryPaginationSchema, }) export type QuerySchemaType = Static const getServiceSchema: FastifySchema = { description: - 'GET all the public guilds (ordered by descending members count).', - tags: ['guilds'] as string[], + "GET all the public guilds (ordered by descending members count).", + tags: ["guilds"] as string[], security: [ { - bearerAuth: [] - } + bearerAuth: [], + }, ] as Array<{ [key: string]: [] }>, querystring: querySchema, response: { 200: Type.Array( Type.Object({ ...guildSchema, - membersCount: Type.Integer() - }) + membersCount: Type.Integer(), + }), ), 400: fastifyErrors[400], 401: fastifyErrors[401], 403: fastifyErrors[403], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const getGuildsPublic: FastifyPluginAsync = async (fastify) => { @@ -48,8 +48,8 @@ export const getGuildsPublic: FastifyPluginAsync = async (fastify) => { fastify.route<{ Querystring: QuerySchemaType }>({ - method: 'GET', - url: '/guilds/public', + method: "GET", + url: "/guilds/public", schema: getServiceSchema, handler: async (request, reply) => { if (request.user == null) { @@ -59,28 +59,28 @@ export const getGuildsPublic: FastifyPluginAsync = async (fastify) => { ...getPaginationOptions(request.query), orderBy: { members: { - _count: 'desc' - } + _count: "desc", + }, }, ...(request.query.search != null && { where: { - name: { contains: request.query.search } - } - }) + name: { contains: request.query.search }, + }, + }), }) const guilds = await Promise.all( guildsRequest.map(async (guild) => { const membersCount = await prisma.member.count({ - where: { guildId: guild.id } + where: { guildId: guild.id }, }) return { ...guild, - membersCount + membersCount, } - }) + }), ) reply.statusCode = 200 return guilds - } + }, }) } diff --git a/src/services/index.ts b/src/services/index.ts index a226c1d..c222b61 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -1,9 +1,9 @@ -import type { FastifyPluginAsync } from 'fastify' +import type { FastifyPluginAsync } from "fastify" -import { usersService } from './users/index.js' -import { guildsService } from './guilds/index.js' -import { channelsService } from './channels/index.js' -import { messagesService } from './messages/index.js' +import { usersService } from "./users/index.js" +import { guildsService } from "./guilds/index.js" +import { channelsService } from "./channels/index.js" +import { messagesService } from "./messages/index.js" export const services: FastifyPluginAsync = async (fastify) => { await fastify.register(channelsService) diff --git a/src/services/messages/[messageId]/__test__/delete.test.ts b/src/services/messages/[messageId]/__test__/delete.test.ts index 0af1a15..630157a 100644 --- a/src/services/messages/[messageId]/__test__/delete.test.ts +++ b/src/services/messages/[messageId]/__test__/delete.test.ts @@ -1,48 +1,48 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import sinon from 'sinon' +import sinon from "sinon" -import { application } from '#src/application.js' -import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' -import prisma from '#src/tools/database/prisma.js' -import { messageExample } from '#src/models/Message.js' -import { memberExample } from '#src/models/Member.js' -import { userExample } from '#src/models/User.js' -import { channelExample } from '#src/models/Channel.js' +import { application } from "#src/application.js" +import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" +import prisma from "#src/tools/database/prisma.js" +import { messageExample } from "#src/models/Message.js" +import { memberExample } from "#src/models/Member.js" +import { userExample } from "#src/models/User.js" +import { channelExample } from "#src/models/Channel.js" -await test('DELETE /messsages/[messageId]', async (t) => { +await test("DELETE /messsages/[messageId]", async (t) => { t.afterEach(() => { sinon.restore() }) - await t.test('succeeds', async () => { + await t.test("succeeds", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'message').value({ + sinon.stub(prisma, "message").value({ findFirst: async () => { return { ...messageExample, - channel: channelExample + channel: channelExample, } }, delete: async () => { return messageExample - } + }, }) - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return { ...memberExample, - user: userExample + user: userExample, } - } + }, }) const response = await application.inject({ - method: 'DELETE', + method: "DELETE", url: `/messages/${messageExample.id}`, headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 200) @@ -56,73 +56,73 @@ await test('DELETE /messsages/[messageId]', async (t) => { assert.strictEqual(responseJson.member.user.name, userExample.name) }) - await t.test('fails if the message is not found', async () => { + await t.test("fails if the message is not found", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'message').value({ + sinon.stub(prisma, "message").value({ findFirst: async () => { return null - } + }, }) const response = await application.inject({ - method: 'DELETE', + method: "DELETE", url: `/messages/${messageExample.id}`, headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) assert.strictEqual(response.statusCode, 404) }) - await t.test('fails if the member is not found', async () => { + await t.test("fails if the member is not found", async () => { const { accessToken } = await authenticateUserTest() - sinon.stub(prisma, 'message').value({ + sinon.stub(prisma, "message").value({ findFirst: async () => { return { ...messageExample, - channel: channelExample + channel: channelExample, } - } + }, }) - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return null - } + }, }) const response = await application.inject({ - method: 'DELETE', + method: "DELETE", url: `/messages/${messageExample.id}`, headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) assert.strictEqual(response.statusCode, 404) }) - await t.test('fails if the member is not owner of the message', async () => { + await t.test("fails if the member is not owner of the message", async () => { const { accessToken } = await authenticateUserTest() const randomUserIdOwnerOfMessage = 14 - sinon.stub(prisma, 'message').value({ + sinon.stub(prisma, "message").value({ findFirst: async () => { return { ...messageExample, - channel: channelExample + channel: channelExample, } - } + }, }) - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return { ...memberExample, - userId: randomUserIdOwnerOfMessage + userId: randomUserIdOwnerOfMessage, } - } + }, }) const response = await application.inject({ - method: 'DELETE', + method: "DELETE", url: `/messages/${messageExample.id}`, headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) assert.strictEqual(response.statusCode, 400) }) diff --git a/src/services/messages/[messageId]/__test__/put.test.ts b/src/services/messages/[messageId]/__test__/put.test.ts index fe2db7e..d69c09e 100644 --- a/src/services/messages/[messageId]/__test__/put.test.ts +++ b/src/services/messages/[messageId]/__test__/put.test.ts @@ -1,53 +1,53 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import sinon from 'sinon' +import sinon from "sinon" -import { application } from '#src/application.js' -import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' -import prisma from '#src/tools/database/prisma.js' -import { messageExample } from '#src/models/Message.js' -import { memberExample } from '#src/models/Member.js' -import { userExample } from '#src/models/User.js' -import { channelExample } from '#src/models/Channel.js' +import { application } from "#src/application.js" +import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" +import prisma from "#src/tools/database/prisma.js" +import { messageExample } from "#src/models/Message.js" +import { memberExample } from "#src/models/Member.js" +import { userExample } from "#src/models/User.js" +import { channelExample } from "#src/models/Channel.js" -await test('PUT /messsages/[messageId]', async (t) => { +await test("PUT /messsages/[messageId]", async (t) => { t.afterEach(() => { sinon.restore() }) - await t.test('succeeds', async () => { + await t.test("succeeds", async () => { const { accessToken } = await authenticateUserTest() - const newValue = 'some message' - sinon.stub(prisma, 'message').value({ + const newValue = "some message" + sinon.stub(prisma, "message").value({ findFirst: async () => { return { ...messageExample, - channel: channelExample + channel: channelExample, } }, update: async () => { return { ...messageExample, - value: newValue + value: newValue, } - } + }, }) - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return { ...memberExample, - user: userExample + user: userExample, } - } + }, }) const response = await application.inject({ - method: 'PUT', + method: "PUT", url: `/messages/${messageExample.id}`, headers: { - authorization: `Bearer ${accessToken}` + authorization: `Bearer ${accessToken}`, }, - payload: { value: newValue } + payload: { value: newValue }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 200) @@ -61,83 +61,83 @@ await test('PUT /messsages/[messageId]', async (t) => { assert.strictEqual(responseJson.member.user.name, userExample.name) }) - await t.test('fails if the message is not found', async () => { + await t.test("fails if the message is not found", async () => { const { accessToken } = await authenticateUserTest() - const newValue = 'some message' - sinon.stub(prisma, 'message').value({ + const newValue = "some message" + sinon.stub(prisma, "message").value({ findFirst: async () => { return null - } + }, }) const response = await application.inject({ - method: 'PUT', + method: "PUT", url: `/messages/${messageExample.id}`, headers: { - authorization: `Bearer ${accessToken}` + authorization: `Bearer ${accessToken}`, }, - payload: { value: newValue } + payload: { value: newValue }, }) assert.strictEqual(response.statusCode, 404) }) - await t.test('fails if the member is not found', async () => { + await t.test("fails if the member is not found", async () => { const { accessToken } = await authenticateUserTest() - const newValue = 'some message' - sinon.stub(prisma, 'message').value({ + const newValue = "some message" + sinon.stub(prisma, "message").value({ findFirst: async () => { return { ...messageExample, - channel: channelExample + channel: channelExample, } - } + }, }) - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return null - } + }, }) const response = await application.inject({ - method: 'PUT', + method: "PUT", url: `/messages/${messageExample.id}`, headers: { - authorization: `Bearer ${accessToken}` + authorization: `Bearer ${accessToken}`, }, - payload: { value: newValue } + payload: { value: newValue }, }) assert.strictEqual(response.statusCode, 404) }) await t.test( - 'fails if the member is not the owner of the message', + "fails if the member is not the owner of the message", async () => { const { accessToken } = await authenticateUserTest() - const newValue = 'some message' + const newValue = "some message" const randomUserIdOwnerOfMessage = 14 - sinon.stub(prisma, 'message').value({ + sinon.stub(prisma, "message").value({ findFirst: async () => { return { ...messageExample, - channel: channelExample + channel: channelExample, } - } + }, }) - sinon.stub(prisma, 'member').value({ + sinon.stub(prisma, "member").value({ findFirst: async () => { return { ...memberExample, - userId: randomUserIdOwnerOfMessage + userId: randomUserIdOwnerOfMessage, } - } + }, }) const response = await application.inject({ - method: 'PUT', + method: "PUT", url: `/messages/${messageExample.id}`, headers: { - authorization: `Bearer ${accessToken}` + authorization: `Bearer ${accessToken}`, }, - payload: { value: newValue } + payload: { value: newValue }, }) assert.strictEqual(response.statusCode, 400) - } + }, ) }) diff --git a/src/services/messages/[messageId]/delete.ts b/src/services/messages/[messageId]/delete.ts index 8f13e86..6029f32 100644 --- a/src/services/messages/[messageId]/delete.ts +++ b/src/services/messages/[messageId]/delete.ts @@ -1,27 +1,27 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import prisma from '#src/tools/database/prisma.js' -import { fastifyErrors } from '#src/models/utils.js' -import authenticateUser from '#src/tools/plugins/authenticateUser.js' -import { messageSchema } from '#src/models/Message.js' -import { memberSchema } from '#src/models/Member.js' -import { userPublicWithoutSettingsSchema } from '#src/models/User.js' +import prisma from "#src/tools/database/prisma.js" +import { fastifyErrors } from "#src/models/utils.js" +import authenticateUser from "#src/tools/plugins/authenticateUser.js" +import { messageSchema } from "#src/models/Message.js" +import { memberSchema } from "#src/models/Member.js" +import { userPublicWithoutSettingsSchema } from "#src/models/User.js" const parametersSchema = Type.Object({ - messageId: messageSchema.id + messageId: messageSchema.id, }) type Parameters = Static const putServiceSchema: FastifySchema = { - description: 'UPDATE a message with its id.', - tags: ['messages'] as string[], + description: "UPDATE a message with its id.", + tags: ["messages"] as string[], security: [ { - bearerAuth: [] - } + bearerAuth: [], + }, ] as Array<{ [key: string]: [] }>, params: parametersSchema, response: { @@ -29,15 +29,15 @@ const putServiceSchema: FastifySchema = { ...messageSchema, member: Type.Object({ ...memberSchema, - user: Type.Object(userPublicWithoutSettingsSchema) - }) + user: Type.Object(userPublicWithoutSettingsSchema), + }), }), 400: fastifyErrors[400], 401: fastifyErrors[401], 403: fastifyErrors[403], 404: fastifyErrors[404], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const deleteMessageService: FastifyPluginAsync = async (fastify) => { @@ -46,8 +46,8 @@ export const deleteMessageService: FastifyPluginAsync = async (fastify) => { fastify.route<{ Params: Parameters }>({ - method: 'DELETE', - url: '/messages/:messageId', + method: "DELETE", + url: "/messages/:messageId", schema: putServiceSchema, handler: async (request, reply) => { if (request.user == null) { @@ -58,16 +58,16 @@ export const deleteMessageService: FastifyPluginAsync = async (fastify) => { const messageCheck = await prisma.message.findFirst({ where: { id: messageId }, include: { - channel: true - } + channel: true, + }, }) if (messageCheck == null || messageCheck.channel == null) { - throw fastify.httpErrors.notFound('Message not found') + throw fastify.httpErrors.notFound("Message not found") } const member = await prisma.member.findFirst({ where: { guildId: messageCheck.channel.guildId, - userId: user.current.id + userId: user.current.id, }, include: { user: { @@ -79,23 +79,23 @@ export const deleteMessageService: FastifyPluginAsync = async (fastify) => { biography: true, website: true, createdAt: true, - updatedAt: true - } - } - } + updatedAt: true, + }, + }, + }, }) if (member == null) { - throw fastify.httpErrors.notFound('Member not found') + throw fastify.httpErrors.notFound("Member not found") } if (member.userId !== user.current.id) { throw fastify.httpErrors.badRequest( - 'You should be the owner of the message' + "You should be the owner of the message", ) } const message = await prisma.message.delete({ where: { - id: messageCheck.id - } + id: messageCheck.id, + }, }) const item = { ...message, @@ -103,17 +103,17 @@ export const deleteMessageService: FastifyPluginAsync = async (fastify) => { ...member, user: { ...member.user, - email: null - } - } + email: null, + }, + }, } await fastify.io.emitToMembers({ - event: 'messages', + event: "messages", guildId: item.member.guildId, - payload: { action: 'delete', item } + payload: { action: "delete", item }, }) reply.statusCode = 200 return item - } + }, }) } diff --git a/src/services/messages/[messageId]/put.ts b/src/services/messages/[messageId]/put.ts index ee4b890..e6d015a 100644 --- a/src/services/messages/[messageId]/put.ts +++ b/src/services/messages/[messageId]/put.ts @@ -1,33 +1,33 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import prisma from '#src/tools/database/prisma.js' -import { fastifyErrors } from '#src/models/utils.js' -import authenticateUser from '#src/tools/plugins/authenticateUser.js' -import { messageSchema } from '#src/models/Message.js' -import { memberSchema } from '#src/models/Member.js' -import { userPublicWithoutSettingsSchema } from '#src/models/User.js' +import prisma from "#src/tools/database/prisma.js" +import { fastifyErrors } from "#src/models/utils.js" +import authenticateUser from "#src/tools/plugins/authenticateUser.js" +import { messageSchema } from "#src/models/Message.js" +import { memberSchema } from "#src/models/Member.js" +import { userPublicWithoutSettingsSchema } from "#src/models/User.js" const bodyPutServiceSchema = Type.Object({ - value: messageSchema.value + value: messageSchema.value, }) type BodyPutServiceSchemaType = Static const parametersSchema = Type.Object({ - messageId: messageSchema.id + messageId: messageSchema.id, }) type Parameters = Static const putServiceSchema: FastifySchema = { - description: 'UPDATE a message with its id.', - tags: ['messages'] as string[], + description: "UPDATE a message with its id.", + tags: ["messages"] as string[], security: [ { - bearerAuth: [] - } + bearerAuth: [], + }, ] as Array<{ [key: string]: [] }>, body: bodyPutServiceSchema, params: parametersSchema, @@ -36,15 +36,15 @@ const putServiceSchema: FastifySchema = { ...messageSchema, member: Type.Object({ ...memberSchema, - user: Type.Object(userPublicWithoutSettingsSchema) - }) + user: Type.Object(userPublicWithoutSettingsSchema), + }), }), 400: fastifyErrors[400], 401: fastifyErrors[401], 403: fastifyErrors[403], 404: fastifyErrors[404], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const putMessageService: FastifyPluginAsync = async (fastify) => { @@ -54,8 +54,8 @@ export const putMessageService: FastifyPluginAsync = async (fastify) => { Body: BodyPutServiceSchemaType Params: Parameters }>({ - method: 'PUT', - url: '/messages/:messageId', + method: "PUT", + url: "/messages/:messageId", schema: putServiceSchema, handler: async (request, reply) => { if (request.user == null) { @@ -65,18 +65,18 @@ export const putMessageService: FastifyPluginAsync = async (fastify) => { const { messageId } = params const { value } = body const messageCheck = await prisma.message.findFirst({ - where: { id: messageId, type: 'text' }, + where: { id: messageId, type: "text" }, include: { - channel: true - } + channel: true, + }, }) if (messageCheck == null || messageCheck.channel == null) { - throw fastify.httpErrors.notFound('Message not found') + throw fastify.httpErrors.notFound("Message not found") } const member = await prisma.member.findFirst({ where: { guildId: messageCheck.channel.guildId, - userId: user.current.id + userId: user.current.id, }, include: { user: { @@ -88,26 +88,26 @@ export const putMessageService: FastifyPluginAsync = async (fastify) => { biography: true, website: true, createdAt: true, - updatedAt: true - } - } - } + updatedAt: true, + }, + }, + }, }) if (member == null) { - throw fastify.httpErrors.notFound('Member not found') + throw fastify.httpErrors.notFound("Member not found") } if (member.userId !== user.current.id) { throw fastify.httpErrors.badRequest( - 'You should be the owner of the message' + "You should be the owner of the message", ) } const message = await prisma.message.update({ where: { - id: messageCheck.id + id: messageCheck.id, }, data: { - value - } + value, + }, }) const item = { ...message, @@ -115,17 +115,17 @@ export const putMessageService: FastifyPluginAsync = async (fastify) => { ...member, user: { ...member.user, - email: null - } - } + email: null, + }, + }, } await fastify.io.emitToMembers({ - event: 'messages', + event: "messages", guildId: item.member.guildId, - payload: { action: 'update', item } + payload: { action: "update", item }, }) reply.statusCode = 200 return item - } + }, }) } diff --git a/src/services/messages/index.ts b/src/services/messages/index.ts index c501116..e9384ee 100644 --- a/src/services/messages/index.ts +++ b/src/services/messages/index.ts @@ -1,7 +1,7 @@ -import type { FastifyPluginAsync } from 'fastify' +import type { FastifyPluginAsync } from "fastify" -import { deleteMessageService } from './[messageId]/delete.js' -import { putMessageService } from './[messageId]/put.js' +import { deleteMessageService } from "./[messageId]/delete.js" +import { putMessageService } from "./[messageId]/put.js" export const messagesService: FastifyPluginAsync = async (fastify) => { await fastify.register(putMessageService) diff --git a/src/services/users/[userId]/__test__/get.test.ts b/src/services/users/[userId]/__test__/get.test.ts index 49bc3e3..18bb9ff 100644 --- a/src/services/users/[userId]/__test__/get.test.ts +++ b/src/services/users/[userId]/__test__/get.test.ts @@ -1,37 +1,37 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import sinon from 'sinon' +import sinon from "sinon" -import { application } from '#src/application.js' -import prisma from '#src/tools/database/prisma.js' -import { userExample } from '#src/models/User.js' -import { userSettingsExample } from '#src/models/UserSettings.js' +import { application } from "#src/application.js" +import prisma from "#src/tools/database/prisma.js" +import { userExample } from "#src/models/User.js" +import { userSettingsExample } from "#src/models/UserSettings.js" -await test('GET /users/[userId]', async (t) => { +await test("GET /users/[userId]", async (t) => { t.afterEach(() => { sinon.restore() }) - await t.test('succeeds', async () => { - sinon.stub(prisma, 'guild').value({ + await t.test("succeeds", async () => { + sinon.stub(prisma, "guild").value({ findMany: async () => { return [] - } + }, }) - sinon.stub(prisma, 'user').value({ + sinon.stub(prisma, "user").value({ findUnique: async () => { return userExample - } + }, }) - sinon.stub(prisma, 'userSetting').value({ + sinon.stub(prisma, "userSetting").value({ findFirst: async () => { return userSettingsExample - } + }, }) const response = await application.inject({ - method: 'GET', - url: `/users/${userExample.id}` + method: "GET", + url: `/users/${userExample.id}`, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 200) @@ -39,18 +39,18 @@ await test('GET /users/[userId]', async (t) => { assert.strictEqual(responseJson.user.name, userExample.name) }) - await t.test('fails with not found user', async () => { - sinon.stub(prisma, 'userSetting').value({ + await t.test("fails with not found user", async () => { + sinon.stub(prisma, "userSetting").value({ findFirst: async () => { return null - } + }, }) const response = await application.inject({ - method: 'GET', - url: `/users/1` + method: "GET", + url: `/users/1`, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 404) - assert.strictEqual(responseJson.message, 'User not found') + assert.strictEqual(responseJson.message, "User not found") }) }) diff --git a/src/services/users/[userId]/get.ts b/src/services/users/[userId]/get.ts index 84d0df6..7d3b625 100644 --- a/src/services/users/[userId]/get.ts +++ b/src/services/users/[userId]/get.ts @@ -1,51 +1,51 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import prisma from '#src/tools/database/prisma.js' -import { fastifyErrors } from '#src/models/utils.js' -import { userPublicSchema } from '#src/models/User.js' -import { guildSchema } from '#src/models/Guild.js' +import prisma from "#src/tools/database/prisma.js" +import { fastifyErrors } from "#src/models/utils.js" +import { userPublicSchema } from "#src/models/User.js" +import { guildSchema } from "#src/models/Guild.js" const parametersGetUserSchema = Type.Object({ - userId: userPublicSchema.id + userId: userPublicSchema.id, }) type ParametersGetUser = Static const getServiceSchema: FastifySchema = { - description: 'GET the public user informations with its id', - tags: ['users'] as string[], + description: "GET the public user informations with its id", + tags: ["users"] as string[], params: parametersGetUserSchema, response: { 200: Type.Object({ user: Type.Object(userPublicSchema), - guilds: Type.Array(Type.Object(guildSchema)) + guilds: Type.Array(Type.Object(guildSchema)), }), 400: fastifyErrors[400], 404: fastifyErrors[404], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const getUserById: FastifyPluginAsync = async (fastify) => { await fastify.route<{ Params: ParametersGetUser }>({ - method: 'GET', - url: '/users/:userId', + method: "GET", + url: "/users/:userId", schema: getServiceSchema, handler: async (request, reply) => { const { userId } = request.params const settings = await prisma.userSetting.findFirst({ - where: { userId } + where: { userId }, }) if (settings == null) { - throw fastify.httpErrors.notFound('User not found') + throw fastify.httpErrors.notFound("User not found") } const user = await prisma.user.findUnique({ where: { - id: userId + id: userId, }, select: { id: true, @@ -57,18 +57,18 @@ export const getUserById: FastifyPluginAsync = async (fastify) => { biography: true, website: true, createdAt: true, - updatedAt: true - } + updatedAt: true, + }, }) if (user == null) { - throw fastify.httpErrors.notFound('User not found') + throw fastify.httpErrors.notFound("User not found") } reply.statusCode = 200 return { user: { ...user, email: user.email ?? null, - settings + settings, }, guilds: !settings.isPublicGuilds ? [] @@ -77,12 +77,12 @@ export const getUserById: FastifyPluginAsync = async (fastify) => { where: { members: { some: { - userId - } - } - } - }) + userId, + }, + }, + }, + }), } - } + }, }) } diff --git a/src/services/users/confirm-email/__test__/get.test.ts b/src/services/users/confirm-email/__test__/get.test.ts index 17966d5..54b7165 100644 --- a/src/services/users/confirm-email/__test__/get.test.ts +++ b/src/services/users/confirm-email/__test__/get.test.ts @@ -1,51 +1,51 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import sinon from 'sinon' +import sinon from "sinon" -import { application } from '#src/application.js' -import prisma from '#src/tools/database/prisma.js' -import { userExample } from '#src/models/User.js' +import { application } from "#src/application.js" +import prisma from "#src/tools/database/prisma.js" +import { userExample } from "#src/models/User.js" -await test('GET /users/confirm-email', async (t) => { +await test("GET /users/confirm-email", async (t) => { t.afterEach(() => { sinon.restore() }) - await t.test('succeeds', async () => { - sinon.stub(prisma, 'user').value({ + await t.test("succeeds", async () => { + sinon.stub(prisma, "user").value({ findFirst: async () => { return userExample }, update: async () => { return { ...userExample, isConfirmed: true, temporaryToken: null } - } + }, }) const response = await application.inject({ - method: 'GET', - url: '/users/confirm-email', + method: "GET", + url: "/users/confirm-email", query: { - temporaryToken: userExample.temporaryToken ?? '' - } + temporaryToken: userExample.temporaryToken ?? "", + }, }) assert.strictEqual(response.statusCode, 200) }) - await t.test('should fails with invalid `temporaryToken`', async () => { - sinon.stub(prisma, 'user').value({ + await t.test("should fails with invalid `temporaryToken`", async () => { + sinon.stub(prisma, "user").value({ findFirst: async () => { return null }, update: async () => { return { ...userExample, isConfirmed: true, temporaryToken: null } - } + }, }) const response = await application.inject({ - method: 'GET', - url: '/users/confirm-email', + method: "GET", + url: "/users/confirm-email", query: { - temporaryToken: userExample.temporaryToken ?? '' - } + temporaryToken: userExample.temporaryToken ?? "", + }, }) assert.strictEqual(response.statusCode, 403) }) diff --git a/src/services/users/confirm-email/get.ts b/src/services/users/confirm-email/get.ts index a1f239e..0a92f68 100644 --- a/src/services/users/confirm-email/get.ts +++ b/src/services/users/confirm-email/get.ts @@ -1,44 +1,44 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import prisma from '#src/tools/database/prisma.js' -import { fastifyErrors } from '#src/models/utils.js' -import { userSchema } from '#src/models/User.js' +import prisma from "#src/tools/database/prisma.js" +import { fastifyErrors } from "#src/models/utils.js" +import { userSchema } from "#src/models/User.js" const queryGetConfirmEmailSchema = Type.Object({ - redirectURI: Type.Optional(Type.String({ format: 'uri-reference' })), - temporaryToken: userSchema.temporaryToken + redirectURI: Type.Optional(Type.String({ format: "uri-reference" })), + temporaryToken: userSchema.temporaryToken, }) type QueryGetConfirmEmailSchemaType = Static const getConfirmEmailSchema: FastifySchema = { - description: 'Confirm the account of the user.', - tags: ['users'] as string[], + description: "Confirm the account of the user.", + tags: ["users"] as string[], querystring: queryGetConfirmEmailSchema, response: { 200: Type.String(), 400: fastifyErrors[400], 403: fastifyErrors[403], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const getConfirmEmail: FastifyPluginAsync = async (fastify) => { await fastify.route<{ Querystring: QueryGetConfirmEmailSchemaType }>({ - method: 'GET', - url: '/users/confirm-email', + method: "GET", + url: "/users/confirm-email", schema: getConfirmEmailSchema, handler: async (request, reply) => { const { redirectURI, temporaryToken } = request.query const user = await prisma.user.findFirst({ where: { temporaryToken, - isConfirmed: false - } + isConfirmed: false, + }, }) if (user == null) { throw fastify.httpErrors.forbidden() @@ -47,14 +47,14 @@ export const getConfirmEmail: FastifyPluginAsync = async (fastify) => { where: { id: user.id }, data: { temporaryToken: null, - isConfirmed: true - } + isConfirmed: true, + }, }) if (redirectURI == null) { reply.statusCode = 200 - return 'Success, your email has been confirmed, you can now signin!' + return "Success, your email has been confirmed, you can now signin!" } return await reply.redirect(redirectURI) - } + }, }) } diff --git a/src/services/users/current/__test__/get.test.ts b/src/services/users/current/__test__/get.test.ts index 61b73c4..9651a01 100644 --- a/src/services/users/current/__test__/get.test.ts +++ b/src/services/users/current/__test__/get.test.ts @@ -1,35 +1,35 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import sinon from 'sinon' +import sinon from "sinon" -import { application } from '#src/application.js' -import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' +import { application } from "#src/application.js" +import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" -await test('GET /users/current', async (t) => { +await test("GET /users/current", async (t) => { t.afterEach(() => { sinon.restore() }) - await t.test('succeeds', async () => { + await t.test("succeeds", async () => { const { accessToken, user } = await authenticateUserTest() const response = await application.inject({ - method: 'GET', - url: '/users/current', + method: "GET", + url: "/users/current", headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 200) assert.strictEqual(responseJson.user.name, user.name) - assert.deepStrictEqual(responseJson.user.strategies, ['Local']) + assert.deepStrictEqual(responseJson.user.strategies, ["Local"]) }) - await t.test('fails with unauthenticated user', async () => { + await t.test("fails with unauthenticated user", async () => { const response = await application.inject({ - method: 'GET', - url: '/users/current' + method: "GET", + url: "/users/current", }) assert.strictEqual(response.statusCode, 401) }) diff --git a/src/services/users/current/__test__/put.test.ts b/src/services/users/current/__test__/put.test.ts index 6c1f864..c3914b5 100644 --- a/src/services/users/current/__test__/put.test.ts +++ b/src/services/users/current/__test__/put.test.ts @@ -1,21 +1,21 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import sinon from 'sinon' +import sinon from "sinon" -import { application } from '#src/application.js' -import prisma from '#src/tools/database/prisma.js' -import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' +import { application } from "#src/application.js" +import prisma from "#src/tools/database/prisma.js" +import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" -await test('PUT /users/current', async (t) => { +await test("PUT /users/current", async (t) => { t.afterEach(() => { sinon.restore() }) - await t.test('succeeds with valid accessToken and valid name', async () => { - const newName = 'John Doe' + await t.test("succeeds with valid accessToken and valid name", async () => { + const newName = "John Doe" const { accessToken, user, userStubValue } = await authenticateUserTest() - sinon.stub(prisma, 'user').value({ + sinon.stub(prisma, "user").value({ ...userStubValue, findFirst: async () => { return null @@ -23,29 +23,29 @@ await test('PUT /users/current', async (t) => { update: async () => { return { ...user, - name: newName + name: newName, } - } + }, }) const response = await application.inject({ - method: 'PUT', - url: '/users/current', + method: "PUT", + url: "/users/current", headers: { - authorization: `Bearer ${accessToken}` + authorization: `Bearer ${accessToken}`, }, payload: { - name: newName - } + name: newName, + }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 200) assert.strictEqual(responseJson.user.name, newName) }) - await t.test('succeeds and only update the status', async () => { - const newStatus = '👀 Working on secret projects...' + await t.test("succeeds and only update the status", async () => { + const newStatus = "👀 Working on secret projects..." const { accessToken, user, userStubValue } = await authenticateUserTest() - sinon.stub(prisma, 'user').value({ + sinon.stub(prisma, "user").value({ ...userStubValue, findFirst: async () => { return null @@ -53,19 +53,19 @@ await test('PUT /users/current', async (t) => { update: async () => { return { ...user, - status: newStatus + status: newStatus, } - } + }, }) const response = await application.inject({ - method: 'PUT', - url: '/users/current', + method: "PUT", + url: "/users/current", headers: { - authorization: `Bearer ${accessToken}` + authorization: `Bearer ${accessToken}`, }, payload: { - status: newStatus - } + status: newStatus, + }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 200) @@ -73,48 +73,48 @@ await test('PUT /users/current', async (t) => { assert.strictEqual(responseJson.user.status, newStatus) }) - await t.test('fails with name already used', async () => { - const newName = 'John Doe' + await t.test("fails with name already used", async () => { + const newName = "John Doe" const { accessToken, user, userStubValue } = await authenticateUserTest() - sinon.stub(prisma, 'user').value({ + sinon.stub(prisma, "user").value({ ...userStubValue, findFirst: async () => { return user - } + }, }) const response = await application.inject({ - method: 'PUT', - url: '/users/current', + method: "PUT", + url: "/users/current", headers: { - authorization: `Bearer ${accessToken}` + authorization: `Bearer ${accessToken}`, }, payload: { - name: newName - } + name: newName, + }, }) assert.strictEqual(response.statusCode, 400) }) - await t.test('fails with invalid website url', async () => { - const newWebsite = 'invalid website url' + await t.test("fails with invalid website url", async () => { + const newWebsite = "invalid website url" const { accessToken } = await authenticateUserTest() const response = await application.inject({ - method: 'PUT', - url: '/users/current', + method: "PUT", + url: "/users/current", headers: { - authorization: `Bearer ${accessToken}` + authorization: `Bearer ${accessToken}`, }, payload: { - website: newWebsite - } + website: newWebsite, + }, }) assert.strictEqual(response.statusCode, 400) }) - await t.test('succeeds with valid website url', async () => { - const newWebsite = 'https://somerandomwebsite.com' + await t.test("succeeds with valid website url", async () => { + const newWebsite = "https://somerandomwebsite.com" const { accessToken, user, userStubValue } = await authenticateUserTest() - sinon.stub(prisma, 'user').value({ + sinon.stub(prisma, "user").value({ ...userStubValue, findFirst: async () => { return null @@ -122,19 +122,19 @@ await test('PUT /users/current', async (t) => { update: async () => { return { ...user, - website: newWebsite + website: newWebsite, } - } + }, }) const response = await application.inject({ - method: 'PUT', - url: '/users/current', + method: "PUT", + url: "/users/current", headers: { - authorization: `Bearer ${accessToken}` + authorization: `Bearer ${accessToken}`, }, payload: { - website: newWebsite - } + website: newWebsite, + }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 200) diff --git a/src/services/users/current/get.ts b/src/services/users/current/get.ts index d66f5a1..19fae39 100644 --- a/src/services/users/current/get.ts +++ b/src/services/users/current/get.ts @@ -1,33 +1,33 @@ -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import prisma from '#src/tools/database/prisma.js' -import { fastifyErrors } from '#src/models/utils.js' -import authenticateUser from '#src/tools/plugins/authenticateUser.js' -import { userCurrentSchema } from '#src/models/User.js' +import prisma from "#src/tools/database/prisma.js" +import { fastifyErrors } from "#src/models/utils.js" +import authenticateUser from "#src/tools/plugins/authenticateUser.js" +import { userCurrentSchema } from "#src/models/User.js" const getCurrentUserSchema: FastifySchema = { - description: 'GET the current connected user', - tags: ['users'] as string[], + description: "GET the current connected user", + tags: ["users"] as string[], security: [ { - bearerAuth: [] - } + bearerAuth: [], + }, ] as Array<{ [key: string]: [] }>, response: { 200: userCurrentSchema, 400: fastifyErrors[400], 401: fastifyErrors[401], 403: fastifyErrors[403], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const getCurrentUser: FastifyPluginAsync = async (fastify) => { await fastify.register(authenticateUser) fastify.route({ - method: 'GET', - url: '/users/current', + method: "GET", + url: "/users/current", schema: getCurrentUserSchema, handler: async (request, reply) => { if (request.user == null) { @@ -35,16 +35,16 @@ export const getCurrentUser: FastifyPluginAsync = async (fastify) => { } const { user } = request const settings = await prisma.userSetting.findFirst({ - where: { userId: user.current.id } + where: { userId: user.current.id }, }) const OAuths = await prisma.oAuth.findMany({ - where: { userId: user.current.id } + where: { userId: user.current.id }, }) const strategies = OAuths.map((oauth) => { return oauth.provider }) if (user.current.password != null) { - strategies.push('Local') + strategies.push("Local") } reply.statusCode = 200 return { @@ -52,9 +52,9 @@ export const getCurrentUser: FastifyPluginAsync = async (fastify) => { ...user.current, settings, currentStrategy: user.currentStrategy, - strategies - } + strategies, + }, } - } + }, }) } diff --git a/src/services/users/current/logo/put.ts b/src/services/users/current/logo/put.ts index 58f744c..1b92f90 100644 --- a/src/services/users/current/logo/put.ts +++ b/src/services/users/current/logo/put.ts @@ -1,34 +1,34 @@ -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' -import fastifyMultipart from '@fastify/multipart' +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" +import fastifyMultipart from "@fastify/multipart" -import authenticateUser from '#src/tools/plugins/authenticateUser.js' -import { fastifyErrors } from '#src/models/utils.js' -import prisma from '#src/tools/database/prisma.js' -import { uploadFile } from '#src/tools/utils/uploadFile.js' +import authenticateUser from "#src/tools/plugins/authenticateUser.js" +import { fastifyErrors } from "#src/models/utils.js" +import prisma from "#src/tools/database/prisma.js" +import { uploadFile } from "#src/tools/utils/uploadFile.js" const putServiceSchema: FastifySchema = { - description: 'Edit the current connected user logo', - tags: ['users'] as string[], - consumes: ['multipart/form-data'] as string[], - produces: ['application/json'] as string[], + description: "Edit the current connected user logo", + tags: ["users"] as string[], + consumes: ["multipart/form-data"] as string[], + produces: ["application/json"] as string[], security: [ { - bearerAuth: [] - } + bearerAuth: [], + }, ] as Array<{ [key: string]: [] }>, response: { 200: Type.Object({ user: Type.Object({ - logo: Type.String() - }) + logo: Type.String(), + }), }), 400: fastifyErrors[400], 401: fastifyErrors[401], 403: fastifyErrors[403], 431: fastifyErrors[431], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const putCurrentUserLogo: FastifyPluginAsync = async (fastify) => { @@ -37,8 +37,8 @@ export const putCurrentUserLogo: FastifyPluginAsync = async (fastify) => { await fastify.register(fastifyMultipart) fastify.route({ - method: 'PUT', - url: '/users/current/logo', + method: "PUT", + url: "/users/current/logo", schema: putServiceSchema, handler: async (request, reply) => { if (request.user == null) { @@ -47,18 +47,18 @@ export const putCurrentUserLogo: FastifyPluginAsync = async (fastify) => { const file = await uploadFile({ fastify, request, - folderInUploadsFolder: 'users' + folderInUploadsFolder: "users", }) await prisma.user.update({ where: { id: request.user.current.id }, - data: { logo: file.pathToStoreInDatabase } + data: { logo: file.pathToStoreInDatabase }, }) reply.statusCode = 200 return { user: { - logo: file.pathToStoreInDatabase - } + logo: file.pathToStoreInDatabase, + }, } - } + }, }) } diff --git a/src/services/users/current/put.ts b/src/services/users/current/put.ts index b15dfcb..780de48 100644 --- a/src/services/users/current/put.ts +++ b/src/services/users/current/put.ts @@ -1,41 +1,41 @@ -import { randomUUID } from 'node:crypto' +import { randomUUID } from "node:crypto" -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import prisma from '#src/tools/database/prisma.js' -import { fastifyErrors } from '#src/models/utils.js' -import authenticateUser from '#src/tools/plugins/authenticateUser.js' -import { userCurrentSchema, userSchema } from '#src/models/User.js' -import { sendEmail } from '#src/tools/email/sendEmail.js' -import { API_URL } from '#src/tools/configurations.js' -import type { Language, Theme } from '#src/models/UserSettings.js' -import { parseStringNullish } from '#src/tools/utils/parseStringNullish.js' +import prisma from "#src/tools/database/prisma.js" +import { fastifyErrors } from "#src/models/utils.js" +import authenticateUser from "#src/tools/plugins/authenticateUser.js" +import { userCurrentSchema, userSchema } from "#src/models/User.js" +import { sendEmail } from "#src/tools/email/sendEmail.js" +import { API_URL } from "#src/tools/configurations.js" +import type { Language, Theme } from "#src/models/UserSettings.js" +import { parseStringNullish } from "#src/tools/utils/parseStringNullish.js" const bodyPutServiceSchema = Type.Object({ name: Type.Optional(userSchema.name), email: Type.Optional(Type.Union([userSchema.email, Type.Null()])), status: Type.Optional(Type.Union([userSchema.status, Type.Null()])), biography: Type.Optional(Type.Union([userSchema.biography, Type.Null()])), - website: Type.Optional(Type.Union([userSchema.website, Type.Null()])) + website: Type.Optional(Type.Union([userSchema.website, Type.Null()])), }) type BodyPutServiceSchemaType = Static const queryPutCurrentUserSchema = Type.Object({ - redirectURI: Type.Optional(Type.String({ format: 'uri-reference' })) + redirectURI: Type.Optional(Type.String({ format: "uri-reference" })), }) type QueryPutCurrentUserSchemaType = Static const putServiceSchema: FastifySchema = { - description: 'Edit the current connected user information', - tags: ['users'] as string[], + description: "Edit the current connected user information", + tags: ["users"] as string[], security: [ { - bearerAuth: [] - } + bearerAuth: [], + }, ] as Array<{ [key: string]: [] }>, body: bodyPutServiceSchema, querystring: queryPutCurrentUserSchema, @@ -44,8 +44,8 @@ const putServiceSchema: FastifySchema = { 400: fastifyErrors[400], 401: fastifyErrors[401], 403: fastifyErrors[403], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const putCurrentUser: FastifyPluginAsync = async (fastify) => { @@ -55,8 +55,8 @@ export const putCurrentUser: FastifyPluginAsync = async (fastify) => { Body: BodyPutServiceSchemaType Querystring: QueryPutCurrentUserSchemaType }>({ - method: 'PUT', - url: '/users/current', + method: "PUT", + url: "/users/current", schema: putServiceSchema, handler: async (request, reply) => { if (request.user == null) { @@ -68,59 +68,59 @@ export const putCurrentUser: FastifyPluginAsync = async (fastify) => { where: { OR: [ ...(email != null ? [{ email }] : [{}]), - ...(name != null ? [{ name }] : [{}]) + ...(name != null ? [{ name }] : [{}]), ], - AND: [{ id: { not: request.user.current.id } }] - } + AND: [{ id: { not: request.user.current.id } }], + }, }) if (userValidation != null) { throw fastify.httpErrors.badRequest( - 'body.email or body.name already taken.' + "body.email or body.name already taken.", ) } const settings = await prisma.userSetting.findFirst({ - where: { userId: request.user.current.id } + where: { userId: request.user.current.id }, }) if (settings == null) { throw fastify.httpErrors.internalServerError() } const OAuths = await prisma.oAuth.findMany({ - where: { userId: request.user.current.id } + where: { userId: request.user.current.id }, }) const strategies = OAuths.map((oauth) => { return oauth.provider }) if (request.user.current.password != null) { - strategies.push('Local') + strategies.push("Local") } - if (email === null && strategies.includes('Local')) { + if (email === null && strategies.includes("Local")) { throw fastify.httpErrors.badRequest( - 'You must have an email to sign in.' + "You must have an email to sign in.", ) } if (email != null && email !== request.user.current.email) { await prisma.refreshToken.deleteMany({ where: { - userId: request.user.current.id - } + userId: request.user.current.id, + }, }) const temporaryToken = randomUUID() const redirectQuery = - redirectURI != null ? `&redirectURI=${redirectURI}` : '' + redirectURI != null ? `&redirectURI=${redirectURI}` : "" await sendEmail({ - type: 'confirm-email', + type: "confirm-email", email, url: `${API_URL}/users/confirm-email?temporaryToken=${temporaryToken}${redirectQuery}`, language: settings.language as Language, - theme: settings.theme as Theme + theme: settings.theme as Theme, }) await prisma.user.update({ where: { id: request.user.current.id }, data: { email, temporaryToken, - isConfirmed: false - } + isConfirmed: false, + }, }) } const user = await prisma.user.update({ @@ -130,20 +130,20 @@ export const putCurrentUser: FastifyPluginAsync = async (fastify) => { status: parseStringNullish(request.user.current.status, status), biography: parseStringNullish( request.user.current.biography, - biography + biography, ), - website: parseStringNullish(request.user.current.website, website) - } + website: parseStringNullish(request.user.current.website, website), + }, }) await fastify.io.emitToAuthorizedUsers({ - event: 'users', + event: "users", isAuthorizedCallback: () => { return true }, payload: { - action: 'update', - item: user - } + action: "update", + item: user, + }, }) reply.statusCode = 200 return { @@ -151,9 +151,9 @@ export const putCurrentUser: FastifyPluginAsync = async (fastify) => { ...user, settings, currentStrategy: request.user.currentStrategy, - strategies - } + strategies, + }, } - } + }, }) } diff --git a/src/services/users/current/settings/__test__/put.test.ts b/src/services/users/current/settings/__test__/put.test.ts index c5d8ff9..883c033 100644 --- a/src/services/users/current/settings/__test__/put.test.ts +++ b/src/services/users/current/settings/__test__/put.test.ts @@ -1,29 +1,29 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import sinon from 'sinon' +import sinon from "sinon" -import { application } from '#src/application.js' -import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' -import prisma from '#src/tools/database/prisma.js' -import { userSettingsExample } from '#src/models/UserSettings.js' +import { application } from "#src/application.js" +import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" +import prisma from "#src/tools/database/prisma.js" +import { userSettingsExample } from "#src/models/UserSettings.js" -await test('PUT /users/current/settings', async (t) => { +await test("PUT /users/current/settings", async (t) => { t.afterEach(() => { sinon.restore() }) await t.test( - 'succeeds and edit the theme, language, isPublicEmail and isPublicGuilds', + "succeeds and edit the theme, language, isPublicEmail and isPublicGuilds", async () => { const newSettings = { - theme: 'light', - language: 'fr', + theme: "light", + language: "fr", isPublicEmail: true, - isPublicGuilds: true + isPublicGuilds: true, } const { accessToken, userSettingStubValue } = await authenticateUserTest() - sinon.stub(prisma, 'userSetting').value({ + sinon.stub(prisma, "userSetting").value({ ...userSettingStubValue, findFirst: async () => { return userSettingsExample @@ -31,17 +31,17 @@ await test('PUT /users/current/settings', async (t) => { update: async () => { return { ...userSettingsExample, - ...newSettings + ...newSettings, } - } + }, }) const response = await application.inject({ - method: 'PUT', - url: '/users/current/settings', + method: "PUT", + url: "/users/current/settings", headers: { - authorization: `Bearer ${accessToken}` + authorization: `Bearer ${accessToken}`, }, - payload: newSettings + payload: newSettings, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 200) @@ -49,21 +49,21 @@ await test('PUT /users/current/settings', async (t) => { assert.strictEqual(responseJson.settings.language, newSettings.language) assert.strictEqual( responseJson.settings.isPublicEmail, - newSettings.isPublicEmail + newSettings.isPublicEmail, ) assert.strictEqual( responseJson.settings.isPublicGuilds, - newSettings.isPublicGuilds + newSettings.isPublicGuilds, ) - } + }, ) - await t.test('fails with invalid language', async () => { + await t.test("fails with invalid language", async () => { const newSettings = { - language: 'somerandomlanguage' + language: "somerandomlanguage", } const { accessToken, userSettingStubValue } = await authenticateUserTest() - sinon.stub(prisma, 'userSetting').value({ + sinon.stub(prisma, "userSetting").value({ ...userSettingStubValue, findFirst: async () => { return userSettingsExample @@ -71,17 +71,17 @@ await test('PUT /users/current/settings', async (t) => { update: async () => { return { ...userSettingsExample, - ...newSettings + ...newSettings, } - } + }, }) const response = await application.inject({ - method: 'PUT', - url: '/users/current/settings', + method: "PUT", + url: "/users/current/settings", headers: { - authorization: `Bearer ${accessToken}` + authorization: `Bearer ${accessToken}`, }, - payload: newSettings + payload: newSettings, }) assert.strictEqual(response.statusCode, 400) }) diff --git a/src/services/users/current/settings/put.ts b/src/services/users/current/settings/put.ts index 53bce39..7cccd90 100644 --- a/src/services/users/current/settings/put.ts +++ b/src/services/users/current/settings/put.ts @@ -1,39 +1,39 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import prisma from '#src/tools/database/prisma.js' -import { fastifyErrors } from '#src/models/utils.js' -import authenticateUser from '#src/tools/plugins/authenticateUser.js' -import { userSettingsSchema } from '#src/models/UserSettings.js' +import prisma from "#src/tools/database/prisma.js" +import { fastifyErrors } from "#src/models/utils.js" +import authenticateUser from "#src/tools/plugins/authenticateUser.js" +import { userSettingsSchema } from "#src/models/UserSettings.js" const bodyPutServiceSchema = Type.Object({ theme: Type.Optional(userSettingsSchema.theme), language: Type.Optional(userSettingsSchema.language), isPublicEmail: Type.Optional(userSettingsSchema.isPublicEmail), - isPublicGuilds: Type.Optional(userSettingsSchema.isPublicGuilds) + isPublicGuilds: Type.Optional(userSettingsSchema.isPublicGuilds), }) type BodyPutServiceSchemaType = Static const putServiceSchema: FastifySchema = { - description: 'Edit the current connected user settings', - tags: ['users'] as string[], + description: "Edit the current connected user settings", + tags: ["users"] as string[], security: [ { - bearerAuth: [] - } + bearerAuth: [], + }, ] as Array<{ [key: string]: [] }>, body: bodyPutServiceSchema, response: { 200: Type.Object({ - settings: Type.Object(userSettingsSchema) + settings: Type.Object(userSettingsSchema), }), 400: fastifyErrors[400], 401: fastifyErrors[401], 403: fastifyErrors[403], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const putCurrentUserSettings: FastifyPluginAsync = async (fastify) => { @@ -42,8 +42,8 @@ export const putCurrentUserSettings: FastifyPluginAsync = async (fastify) => { fastify.route<{ Body: BodyPutServiceSchemaType }>({ - method: 'PUT', - url: '/users/current/settings', + method: "PUT", + url: "/users/current/settings", schema: putServiceSchema, handler: async (request, reply) => { if (request.user == null) { @@ -51,7 +51,7 @@ export const putCurrentUserSettings: FastifyPluginAsync = async (fastify) => { } const { theme, language, isPublicEmail, isPublicGuilds } = request.body const settings = await prisma.userSetting.findFirst({ - where: { userId: request.user.current.id } + where: { userId: request.user.current.id }, }) if (settings == null) { throw fastify.httpErrors.internalServerError() @@ -62,11 +62,11 @@ export const putCurrentUserSettings: FastifyPluginAsync = async (fastify) => { theme: theme ?? settings.theme, language: language ?? settings.language, isPublicEmail: isPublicEmail ?? settings.isPublicEmail, - isPublicGuilds: isPublicGuilds ?? settings.isPublicGuilds - } + isPublicGuilds: isPublicGuilds ?? settings.isPublicGuilds, + }, }) reply.statusCode = 200 return { settings: newSettings } - } + }, }) } diff --git a/src/services/users/index.ts b/src/services/users/index.ts index 1ad1505..f3edebe 100644 --- a/src/services/users/index.ts +++ b/src/services/users/index.ts @@ -1,31 +1,31 @@ -import type { FastifyPluginAsync } from 'fastify' +import type { FastifyPluginAsync } from "fastify" -import { postSignupUser } from './signup/post.js' -import { getConfirmEmail } from './confirm-email/get.js' -import { postSigninUser } from './signin/post.js' -import { postSignoutUser } from './signout/post.js' -import { deleteSignoutUser } from './signout/delete.js' -import { postRefreshTokenUser } from './refresh-token/post.js' -import { putResetPasswordUser } from './reset-password/put.js' -import { postResetPasswordUser } from './reset-password/post.js' -import { getCurrentUser } from './current/get.js' -import { putCurrentUser } from './current/put.js' -import { putCurrentUserSettings } from './current/settings/put.js' -import { getUserById } from './[userId]/get.js' -import { putCurrentUserLogo } from './current/logo/put.js' -import { getSigninDiscordOAuth2Service } from './oauth2/discord/signin/get.js' -import { getCallbackDiscordOAuth2Service } from './oauth2/discord/callback/get.js' -import { getSigninGoogleOAuth2Service } from './oauth2/google/signin/get.js' -import { getCallbackGoogleOAuth2Service } from './oauth2/google/callback/get.js' -import { getSigninGitHubOAuth2Service } from './oauth2/github/signin/get.js' -import { getCallbackGitHubOAuth2Service } from './oauth2/github/callback/get.js' -import { deleteProviderService } from './oauth2/[provider]/delete.js' -import { getCallbackAddStrategyDiscordOAuth2Service } from './oauth2/discord/callback-add-strategy/get.js' -import { getAddStrategyDiscordOAuth2Service } from './oauth2/discord/add-strategy/get.js' -import { getAddStrategyGitHubOAuth2Service } from './oauth2/github/add-strategy/get.js' -import { getCallbackAddStrategyGitHubOAuth2Service } from './oauth2/github/callback-add-strategy/get.js' -import { getCallbackAddStrategyGoogleOAuth2Service } from './oauth2/google/callback-add-strategy/get.js' -import { getAddStrategyGoogleOAuth2Service } from './oauth2/google/add-strategy/get.js' +import { postSignupUser } from "./signup/post.js" +import { getConfirmEmail } from "./confirm-email/get.js" +import { postSigninUser } from "./signin/post.js" +import { postSignoutUser } from "./signout/post.js" +import { deleteSignoutUser } from "./signout/delete.js" +import { postRefreshTokenUser } from "./refresh-token/post.js" +import { putResetPasswordUser } from "./reset-password/put.js" +import { postResetPasswordUser } from "./reset-password/post.js" +import { getCurrentUser } from "./current/get.js" +import { putCurrentUser } from "./current/put.js" +import { putCurrentUserSettings } from "./current/settings/put.js" +import { getUserById } from "./[userId]/get.js" +import { putCurrentUserLogo } from "./current/logo/put.js" +import { getSigninDiscordOAuth2Service } from "./oauth2/discord/signin/get.js" +import { getCallbackDiscordOAuth2Service } from "./oauth2/discord/callback/get.js" +import { getSigninGoogleOAuth2Service } from "./oauth2/google/signin/get.js" +import { getCallbackGoogleOAuth2Service } from "./oauth2/google/callback/get.js" +import { getSigninGitHubOAuth2Service } from "./oauth2/github/signin/get.js" +import { getCallbackGitHubOAuth2Service } from "./oauth2/github/callback/get.js" +import { deleteProviderService } from "./oauth2/[provider]/delete.js" +import { getCallbackAddStrategyDiscordOAuth2Service } from "./oauth2/discord/callback-add-strategy/get.js" +import { getAddStrategyDiscordOAuth2Service } from "./oauth2/discord/add-strategy/get.js" +import { getAddStrategyGitHubOAuth2Service } from "./oauth2/github/add-strategy/get.js" +import { getCallbackAddStrategyGitHubOAuth2Service } from "./oauth2/github/callback-add-strategy/get.js" +import { getCallbackAddStrategyGoogleOAuth2Service } from "./oauth2/google/callback-add-strategy/get.js" +import { getAddStrategyGoogleOAuth2Service } from "./oauth2/google/add-strategy/get.js" export const usersService: FastifyPluginAsync = async (fastify) => { await fastify.register(postSignupUser) diff --git a/src/services/users/oauth2/[provider]/delete.ts b/src/services/users/oauth2/[provider]/delete.ts index 516701a..07e99e5 100644 --- a/src/services/users/oauth2/[provider]/delete.ts +++ b/src/services/users/oauth2/[provider]/delete.ts @@ -1,25 +1,25 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import prisma from '#src/tools/database/prisma.js' -import { fastifyErrors } from '#src/models/utils.js' -import authenticateUser from '#src/tools/plugins/authenticateUser.js' -import { oauthSchema } from '#src/models/OAuth.js' +import prisma from "#src/tools/database/prisma.js" +import { fastifyErrors } from "#src/models/utils.js" +import authenticateUser from "#src/tools/plugins/authenticateUser.js" +import { oauthSchema } from "#src/models/OAuth.js" const parametersSchema = Type.Object({ - provider: oauthSchema.provider + provider: oauthSchema.provider, }) type Parameters = Static const deleteServiceSchema: FastifySchema = { - description: 'DELETE a provider to authenticate with for a user.', - tags: ['oauth2'] as string[], + description: "DELETE a provider to authenticate with for a user.", + tags: ["oauth2"] as string[], security: [ { - bearerAuth: [] - } + bearerAuth: [], + }, ] as Array<{ [key: string]: [] }>, params: parametersSchema, response: { @@ -28,8 +28,8 @@ const deleteServiceSchema: FastifySchema = { 401: fastifyErrors[401], 403: fastifyErrors[403], 404: fastifyErrors[404], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const deleteProviderService: FastifyPluginAsync = async (fastify) => { @@ -38,8 +38,8 @@ export const deleteProviderService: FastifyPluginAsync = async (fastify) => { fastify.route<{ Params: Parameters }>({ - method: 'DELETE', - url: '/users/oauth2/:provider', + method: "DELETE", + url: "/users/oauth2/:provider", schema: deleteServiceSchema, handler: async (request, reply) => { if (request.user == null) { @@ -48,31 +48,31 @@ export const deleteProviderService: FastifyPluginAsync = async (fastify) => { const { user, params } = request const { provider } = params const OAuths = await prisma.oAuth.findMany({ - where: { userId: user.current.id } + where: { userId: user.current.id }, }) const strategies = OAuths.map((oauth) => { return oauth.provider }) if (user.current.password != null) { - strategies.push('Local') + strategies.push("Local") } const oauthProvider = OAuths.find((oauth) => { return oauth.provider === provider }) if (oauthProvider == null) { - throw fastify.httpErrors.notFound('You are not using this provider') + throw fastify.httpErrors.notFound("You are not using this provider") } const hasOthersWayToAuthenticate = strategies.length >= 2 if (!hasOthersWayToAuthenticate) { throw fastify.httpErrors.badRequest( - "You can't delete your only way to authenticate" + "You can't delete your only way to authenticate", ) } const oauthProviderDelete = await prisma.oAuth.delete({ - where: { id: oauthProvider.id } + where: { id: oauthProvider.id }, }) reply.statusCode = 200 return oauthProviderDelete - } + }, }) } diff --git a/src/services/users/oauth2/discord/__utils__/utils.ts b/src/services/users/oauth2/discord/__utils__/utils.ts index 0990da2..f408162 100644 --- a/src/services/users/oauth2/discord/__utils__/utils.ts +++ b/src/services/users/oauth2/discord/__utils__/utils.ts @@ -1,15 +1,15 @@ -import querystring from 'node:querystring' +import querystring from "node:querystring" -import axios from 'axios' +import axios from "axios" -import { OAuthStrategy } from '#src/tools/utils/OAuthStrategy.js' +import { OAuthStrategy } from "#src/tools/utils/OAuthStrategy.js" -export const DISCORD_PROVIDER = 'Discord' -export const DISCORD_BASE_URL = 'https://discord.com/api/v10' +export const DISCORD_PROVIDER = "Discord" +export const DISCORD_BASE_URL = "https://discord.com/api/v10" export const DISCORD_CLIENT_ID = - process.env['DISCORD_CLIENT_ID'] ?? 'DISCORD_CLIENT_ID' + process.env["DISCORD_CLIENT_ID"] ?? "DISCORD_CLIENT_ID" export const DISCORD_CLIENT_SECRET = - process.env['DISCORD_CLIENT_SECRET'] ?? 'DISCORD_CLIENT_SECRET' + process.env["DISCORD_CLIENT_SECRET"] ?? "DISCORD_CLIENT_SECRET" export const discordStrategy = new OAuthStrategy(DISCORD_PROVIDER) export interface DiscordUser { @@ -30,31 +30,31 @@ export interface DiscordTokens { export const getDiscordUserData = async ( code: string, - redirectURI: string + redirectURI: string, ): Promise => { const { data: tokens } = await axios.post( `${DISCORD_BASE_URL}/oauth2/token`, querystring.stringify({ client_id: DISCORD_CLIENT_ID, client_secret: DISCORD_CLIENT_SECRET, - grant_type: 'authorization_code', + grant_type: "authorization_code", code, redirect_uri: redirectURI, - scope: 'identify' + scope: "identify", }), { headers: { - 'Content-Type': 'application/x-www-form-urlencoded' - } - } + "Content-Type": "application/x-www-form-urlencoded", + }, + }, ) const { data: discordUser } = await axios.get( `${DISCORD_BASE_URL}/users/@me`, { headers: { - Authorization: `${tokens.token_type} ${tokens.access_token}` - } - } + Authorization: `${tokens.token_type} ${tokens.access_token}`, + }, + }, ) return discordUser } diff --git a/src/services/users/oauth2/discord/add-strategy/get.ts b/src/services/users/oauth2/discord/add-strategy/get.ts index 9030706..7f9e8b4 100644 --- a/src/services/users/oauth2/discord/add-strategy/get.ts +++ b/src/services/users/oauth2/discord/add-strategy/get.ts @@ -1,44 +1,44 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import { API_URL } from '#src/tools/configurations.js' -import { fastifyErrors } from '#src/models/utils.js' -import { DISCORD_BASE_URL, DISCORD_CLIENT_ID } from '../__utils__/utils.js' -import authenticateUser from '#src/tools/plugins/authenticateUser.js' +import { API_URL } from "#src/tools/configurations.js" +import { fastifyErrors } from "#src/models/utils.js" +import { DISCORD_BASE_URL, DISCORD_CLIENT_ID } from "../__utils__/utils.js" +import authenticateUser from "#src/tools/plugins/authenticateUser.js" const querySchema = Type.Object({ - redirectURI: Type.String({ format: 'uri-reference' }) + redirectURI: Type.String({ format: "uri-reference" }), }) type QuerySchemaType = Static const getServiceSchema: FastifySchema = { - description: 'Discord OAuth2 - add-strategy', - tags: ['oauth2'] as string[], + description: "Discord OAuth2 - add-strategy", + tags: ["oauth2"] as string[], security: [ { - bearerAuth: [] - } + bearerAuth: [], + }, ] as Array<{ [key: string]: [] }>, querystring: querySchema, response: { 200: Type.String(), 400: fastifyErrors[400], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const getAddStrategyDiscordOAuth2Service: FastifyPluginAsync = async ( - fastify + fastify, ) => { await fastify.register(authenticateUser) await fastify.route<{ Querystring: QuerySchemaType }>({ - method: 'GET', - url: '/users/oauth2/discord/add-strategy', + method: "GET", + url: "/users/oauth2/discord/add-strategy", schema: getServiceSchema, handler: async (request, reply) => { if (request.user == null) { @@ -49,6 +49,6 @@ export const getAddStrategyDiscordOAuth2Service: FastifyPluginAsync = async ( const url = `${DISCORD_BASE_URL}/oauth2/authorize?client_id=${DISCORD_CLIENT_ID}&scope=identify&response_type=code&state=${request.user.accessToken}&redirect_uri=${redirectCallback}` reply.statusCode = 200 return url - } + }, }) } diff --git a/src/services/users/oauth2/discord/callback-add-strategy/get.ts b/src/services/users/oauth2/discord/callback-add-strategy/get.ts index b5f9a9d..3c1cc69 100644 --- a/src/services/users/oauth2/discord/callback-add-strategy/get.ts +++ b/src/services/users/oauth2/discord/callback-add-strategy/get.ts @@ -1,30 +1,30 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import { API_URL } from '#src/tools/configurations.js' -import { fastifyErrors } from '#src/models/utils.js' -import { discordStrategy, getDiscordUserData } from '../__utils__/utils.js' -import { buildQueryURL } from '#src/tools/utils/buildQueryURL.js' -import { getUserWithBearerToken } from '#src/tools/plugins/authenticateUser.js' +import { API_URL } from "#src/tools/configurations.js" +import { fastifyErrors } from "#src/models/utils.js" +import { discordStrategy, getDiscordUserData } from "../__utils__/utils.js" +import { buildQueryURL } from "#src/tools/utils/buildQueryURL.js" +import { getUserWithBearerToken } from "#src/tools/plugins/authenticateUser.js" const querySchema = Type.Object({ code: Type.String(), state: Type.String(), - redirectURI: Type.String({ format: 'uri-reference' }) + redirectURI: Type.String({ format: "uri-reference" }), }) type QuerySchemaType = Static const getServiceSchema: FastifySchema = { - description: 'Discord OAuth2 - callback-add-strategy', - tags: ['oauth2'] as string[], + description: "Discord OAuth2 - callback-add-strategy", + tags: ["oauth2"] as string[], querystring: querySchema, response: { 200: Type.String(), 400: fastifyErrors[400], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const getCallbackAddStrategyDiscordOAuth2Service: FastifyPluginAsync = @@ -32,26 +32,26 @@ export const getCallbackAddStrategyDiscordOAuth2Service: FastifyPluginAsync = await fastify.route<{ Querystring: QuerySchemaType }>({ - method: 'GET', - url: '/users/oauth2/discord/callback-add-strategy', + method: "GET", + url: "/users/oauth2/discord/callback-add-strategy", schema: getServiceSchema, handler: async (request, reply) => { const { redirectURI, code, state: accessToken } = request.query const userRequest = await getUserWithBearerToken( - `Bearer ${accessToken}` + `Bearer ${accessToken}`, ) const discordUser = await getDiscordUserData( code, - `${API_URL}/users/oauth2/discord/callback-add-strategy?redirectURI=${redirectURI}` + `${API_URL}/users/oauth2/discord/callback-add-strategy?redirectURI=${redirectURI}`, ) const message = await discordStrategy.callbackAddStrategy( { name: discordUser.username, - id: discordUser.id + id: discordUser.id, }, - userRequest + userRequest, ) return await reply.redirect(buildQueryURL(redirectURI, { message })) - } + }, }) } diff --git a/src/services/users/oauth2/discord/callback/get.ts b/src/services/users/oauth2/discord/callback/get.ts index 39351f2..a485c3b 100644 --- a/src/services/users/oauth2/discord/callback/get.ts +++ b/src/services/users/oauth2/discord/callback/get.ts @@ -1,50 +1,50 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import { API_URL } from '#src/tools/configurations.js' -import { fastifyErrors } from '#src/models/utils.js' -import { discordStrategy, getDiscordUserData } from '../__utils__/utils.js' -import { buildQueryURL } from '#src/tools/utils/buildQueryURL.js' +import { API_URL } from "#src/tools/configurations.js" +import { fastifyErrors } from "#src/models/utils.js" +import { discordStrategy, getDiscordUserData } from "../__utils__/utils.js" +import { buildQueryURL } from "#src/tools/utils/buildQueryURL.js" const querySchema = Type.Object({ code: Type.String(), - redirectURI: Type.String({ format: 'uri-reference' }) + redirectURI: Type.String({ format: "uri-reference" }), }) type QuerySchemaType = Static const getServiceSchema: FastifySchema = { - description: 'Discord OAuth2 - callback', - tags: ['oauth2'] as string[], + description: "Discord OAuth2 - callback", + tags: ["oauth2"] as string[], querystring: querySchema, response: { 200: Type.String(), 400: fastifyErrors[400], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const getCallbackDiscordOAuth2Service: FastifyPluginAsync = async ( - fastify + fastify, ) => { await fastify.route<{ Querystring: QuerySchemaType }>({ - method: 'GET', - url: '/users/oauth2/discord/callback', + method: "GET", + url: "/users/oauth2/discord/callback", schema: getServiceSchema, handler: async (request, reply) => { const { redirectURI, code } = request.query const discordUser = await getDiscordUserData( code, - `${API_URL}/users/oauth2/discord/callback?redirectURI=${redirectURI}` + `${API_URL}/users/oauth2/discord/callback?redirectURI=${redirectURI}`, ) const responseJWT = await discordStrategy.callbackSignin({ name: discordUser.username, - id: discordUser.id + id: discordUser.id, }) return await reply.redirect(buildQueryURL(redirectURI, responseJWT)) - } + }, }) } diff --git a/src/services/users/oauth2/discord/signin/get.ts b/src/services/users/oauth2/discord/signin/get.ts index c61ef97..f48f31c 100644 --- a/src/services/users/oauth2/discord/signin/get.ts +++ b/src/services/users/oauth2/discord/signin/get.ts @@ -1,36 +1,36 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import { API_URL } from '#src/tools/configurations.js' -import { fastifyErrors } from '#src/models/utils.js' -import { DISCORD_BASE_URL, DISCORD_CLIENT_ID } from '../__utils__/utils.js' +import { API_URL } from "#src/tools/configurations.js" +import { fastifyErrors } from "#src/models/utils.js" +import { DISCORD_BASE_URL, DISCORD_CLIENT_ID } from "../__utils__/utils.js" const querySchema = Type.Object({ - redirectURI: Type.String({ format: 'uri-reference' }) + redirectURI: Type.String({ format: "uri-reference" }), }) type QuerySchemaType = Static const getServiceSchema: FastifySchema = { - description: 'Discord OAuth2 - signin', - tags: ['oauth2'] as string[], + description: "Discord OAuth2 - signin", + tags: ["oauth2"] as string[], querystring: querySchema, response: { 200: Type.String(), 400: fastifyErrors[400], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const getSigninDiscordOAuth2Service: FastifyPluginAsync = async ( - fastify + fastify, ) => { await fastify.route<{ Querystring: QuerySchemaType }>({ - method: 'GET', - url: '/users/oauth2/discord/signin', + method: "GET", + url: "/users/oauth2/discord/signin", schema: getServiceSchema, handler: async (request, reply) => { const { redirectURI } = request.query @@ -38,6 +38,6 @@ export const getSigninDiscordOAuth2Service: FastifyPluginAsync = async ( const url = `${DISCORD_BASE_URL}/oauth2/authorize?client_id=${DISCORD_CLIENT_ID}&scope=identify&response_type=code&redirect_uri=${redirectCallback}` reply.statusCode = 200 return url - } + }, }) } diff --git a/src/services/users/oauth2/github/__utils__/utils.ts b/src/services/users/oauth2/github/__utils__/utils.ts index 3032676..a0680e8 100644 --- a/src/services/users/oauth2/github/__utils__/utils.ts +++ b/src/services/users/oauth2/github/__utils__/utils.ts @@ -1,16 +1,16 @@ -import querystring from 'node:querystring' +import querystring from "node:querystring" -import axios from 'axios' +import axios from "axios" -import { OAuthStrategy } from '#src/tools/utils/OAuthStrategy.js' +import { OAuthStrategy } from "#src/tools/utils/OAuthStrategy.js" -export const GITHUB_PROVIDER = 'GitHub' -export const GITHUB_BASE_URL = 'https://github.com' -export const GITHUB_API_BASE_URL = 'https://api.github.com' +export const GITHUB_PROVIDER = "GitHub" +export const GITHUB_BASE_URL = "https://github.com" +export const GITHUB_API_BASE_URL = "https://api.github.com" export const GITHUB_CLIENT_ID = - process.env['GITHUB_CLIENT_ID'] ?? 'GITHUB_CLIENT_ID' + process.env["GITHUB_CLIENT_ID"] ?? "GITHUB_CLIENT_ID" export const GITHUB_CLIENT_SECRET = - process.env['GITHUB_CLIENT_SECRET'] ?? 'GITHUB_CLIENT_SECRET' + process.env["GITHUB_CLIENT_SECRET"] ?? "GITHUB_CLIENT_SECRET" export const githubStrategy = new OAuthStrategy(GITHUB_PROVIDER) export interface GitHubUser { @@ -28,7 +28,7 @@ export interface GitHubTokens { export const getGitHubUserData = async ( code: string, - redirectURI: string + redirectURI: string, ): Promise => { const { data: token } = await axios.post( `${GITHUB_BASE_URL}/login/oauth/access_token`, @@ -36,22 +36,22 @@ export const getGitHubUserData = async ( client_id: GITHUB_CLIENT_ID, client_secret: GITHUB_CLIENT_SECRET, code, - redirect_uri: redirectURI + redirect_uri: redirectURI, }), { headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - Accept: 'application/json' - } - } + "Content-Type": "application/x-www-form-urlencoded", + Accept: "application/json", + }, + }, ) const { data: githubUser } = await axios.get( `${GITHUB_API_BASE_URL}/user`, { headers: { - Authorization: `token ${token.access_token}` - } - } + Authorization: `token ${token.access_token}`, + }, + }, ) return githubUser } diff --git a/src/services/users/oauth2/github/add-strategy/get.ts b/src/services/users/oauth2/github/add-strategy/get.ts index 75edc0d..cfafec7 100644 --- a/src/services/users/oauth2/github/add-strategy/get.ts +++ b/src/services/users/oauth2/github/add-strategy/get.ts @@ -1,44 +1,44 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import { API_URL } from '#src/tools/configurations.js' -import { fastifyErrors } from '#src/models/utils.js' -import { GITHUB_BASE_URL, GITHUB_CLIENT_ID } from '../__utils__/utils.js' -import authenticateUser from '#src/tools/plugins/authenticateUser.js' +import { API_URL } from "#src/tools/configurations.js" +import { fastifyErrors } from "#src/models/utils.js" +import { GITHUB_BASE_URL, GITHUB_CLIENT_ID } from "../__utils__/utils.js" +import authenticateUser from "#src/tools/plugins/authenticateUser.js" const querySchema = Type.Object({ - redirectURI: Type.String({ format: 'uri-reference' }) + redirectURI: Type.String({ format: "uri-reference" }), }) type QuerySchemaType = Static const getServiceSchema: FastifySchema = { - description: 'GitHub OAuth2 - add-strategy', - tags: ['oauth2'] as string[], + description: "GitHub OAuth2 - add-strategy", + tags: ["oauth2"] as string[], security: [ { - bearerAuth: [] - } + bearerAuth: [], + }, ] as Array<{ [key: string]: [] }>, querystring: querySchema, response: { 200: Type.String(), 400: fastifyErrors[400], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const getAddStrategyGitHubOAuth2Service: FastifyPluginAsync = async ( - fastify + fastify, ) => { await fastify.register(authenticateUser) await fastify.route<{ Querystring: QuerySchemaType }>({ - method: 'GET', - url: '/users/oauth2/github/add-strategy', + method: "GET", + url: "/users/oauth2/github/add-strategy", schema: getServiceSchema, handler: async (request, reply) => { if (request.user == null) { @@ -49,6 +49,6 @@ export const getAddStrategyGitHubOAuth2Service: FastifyPluginAsync = async ( const url = `${GITHUB_BASE_URL}/login/oauth/authorize?client_id=${GITHUB_CLIENT_ID}&state=${request.user.accessToken}&redirect_uri=${redirectCallback}` reply.statusCode = 200 return url - } + }, }) } diff --git a/src/services/users/oauth2/github/callback-add-strategy/get.ts b/src/services/users/oauth2/github/callback-add-strategy/get.ts index ebd9fb3..f37f34f 100644 --- a/src/services/users/oauth2/github/callback-add-strategy/get.ts +++ b/src/services/users/oauth2/github/callback-add-strategy/get.ts @@ -1,30 +1,30 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import { API_URL } from '#src/tools/configurations.js' -import { fastifyErrors } from '#src/models/utils.js' -import { githubStrategy, getGitHubUserData } from '../__utils__/utils.js' -import { buildQueryURL } from '#src/tools/utils/buildQueryURL.js' -import { getUserWithBearerToken } from '#src/tools/plugins/authenticateUser.js' +import { API_URL } from "#src/tools/configurations.js" +import { fastifyErrors } from "#src/models/utils.js" +import { githubStrategy, getGitHubUserData } from "../__utils__/utils.js" +import { buildQueryURL } from "#src/tools/utils/buildQueryURL.js" +import { getUserWithBearerToken } from "#src/tools/plugins/authenticateUser.js" const querySchema = Type.Object({ code: Type.String(), state: Type.String(), - redirectURI: Type.String({ format: 'uri-reference' }) + redirectURI: Type.String({ format: "uri-reference" }), }) type QuerySchemaType = Static const getServiceSchema: FastifySchema = { - description: 'GitHub OAuth2 - callback-add-strategy', - tags: ['oauth2'] as string[], + description: "GitHub OAuth2 - callback-add-strategy", + tags: ["oauth2"] as string[], querystring: querySchema, response: { 200: Type.String(), 400: fastifyErrors[400], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const getCallbackAddStrategyGitHubOAuth2Service: FastifyPluginAsync = @@ -32,26 +32,26 @@ export const getCallbackAddStrategyGitHubOAuth2Service: FastifyPluginAsync = await fastify.route<{ Querystring: QuerySchemaType }>({ - method: 'GET', - url: '/users/oauth2/github/callback-add-strategy', + method: "GET", + url: "/users/oauth2/github/callback-add-strategy", schema: getServiceSchema, handler: async (request, reply) => { const { redirectURI, code, state: accessToken } = request.query const userRequest = await getUserWithBearerToken( - `Bearer ${accessToken}` + `Bearer ${accessToken}`, ) const githubUser = await getGitHubUserData( code, - `${API_URL}/users/oauth2/github/callback-add-strategy?redirectURI=${redirectURI}` + `${API_URL}/users/oauth2/github/callback-add-strategy?redirectURI=${redirectURI}`, ) const message = await githubStrategy.callbackAddStrategy( { name: githubUser.name, - id: githubUser.id + id: githubUser.id, }, - userRequest + userRequest, ) return await reply.redirect(buildQueryURL(redirectURI, { message })) - } + }, }) } diff --git a/src/services/users/oauth2/github/callback/get.ts b/src/services/users/oauth2/github/callback/get.ts index a14f9d9..cfeca7b 100644 --- a/src/services/users/oauth2/github/callback/get.ts +++ b/src/services/users/oauth2/github/callback/get.ts @@ -1,50 +1,50 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import { API_URL } from '#src/tools/configurations.js' -import { fastifyErrors } from '#src/models/utils.js' -import { githubStrategy, getGitHubUserData } from '../__utils__/utils.js' -import { buildQueryURL } from '#src/tools/utils/buildQueryURL.js' +import { API_URL } from "#src/tools/configurations.js" +import { fastifyErrors } from "#src/models/utils.js" +import { githubStrategy, getGitHubUserData } from "../__utils__/utils.js" +import { buildQueryURL } from "#src/tools/utils/buildQueryURL.js" const querySchema = Type.Object({ code: Type.String(), - redirectURI: Type.String({ format: 'uri-reference' }) + redirectURI: Type.String({ format: "uri-reference" }), }) type QuerySchemaType = Static const getServiceSchema: FastifySchema = { - description: 'GitHub OAuth2 - callback', - tags: ['oauth2'] as string[], + description: "GitHub OAuth2 - callback", + tags: ["oauth2"] as string[], querystring: querySchema, response: { 200: Type.String(), 400: fastifyErrors[400], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const getCallbackGitHubOAuth2Service: FastifyPluginAsync = async ( - fastify + fastify, ) => { await fastify.route<{ Querystring: QuerySchemaType }>({ - method: 'GET', - url: '/users/oauth2/github/callback', + method: "GET", + url: "/users/oauth2/github/callback", schema: getServiceSchema, handler: async (request, reply) => { const { redirectURI, code } = request.query const githubUser = await getGitHubUserData( code, - `${API_URL}/users/oauth2/github/callback?redirectURI=${redirectURI}` + `${API_URL}/users/oauth2/github/callback?redirectURI=${redirectURI}`, ) const responseJWT = await githubStrategy.callbackSignin({ name: githubUser.name, - id: githubUser.id + id: githubUser.id, }) return await reply.redirect(buildQueryURL(redirectURI, responseJWT)) - } + }, }) } diff --git a/src/services/users/oauth2/github/signin/get.ts b/src/services/users/oauth2/github/signin/get.ts index 783a5ef..3f5bf97 100644 --- a/src/services/users/oauth2/github/signin/get.ts +++ b/src/services/users/oauth2/github/signin/get.ts @@ -1,36 +1,36 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import { API_URL } from '#src/tools/configurations.js' -import { fastifyErrors } from '#src/models/utils.js' -import { GITHUB_BASE_URL, GITHUB_CLIENT_ID } from '../__utils__/utils.js' +import { API_URL } from "#src/tools/configurations.js" +import { fastifyErrors } from "#src/models/utils.js" +import { GITHUB_BASE_URL, GITHUB_CLIENT_ID } from "../__utils__/utils.js" const querySchema = Type.Object({ - redirectURI: Type.String({ format: 'uri-reference' }) + redirectURI: Type.String({ format: "uri-reference" }), }) type QuerySchemaType = Static const getServiceSchema: FastifySchema = { - description: 'GitHub OAuth2 - signin', - tags: ['oauth2'] as string[], + description: "GitHub OAuth2 - signin", + tags: ["oauth2"] as string[], querystring: querySchema, response: { 200: Type.String(), 400: fastifyErrors[400], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const getSigninGitHubOAuth2Service: FastifyPluginAsync = async ( - fastify + fastify, ) => { await fastify.route<{ Querystring: QuerySchemaType }>({ - method: 'GET', - url: '/users/oauth2/github/signin', + method: "GET", + url: "/users/oauth2/github/signin", schema: getServiceSchema, handler: async (request, reply) => { const { redirectURI } = request.query @@ -38,6 +38,6 @@ export const getSigninGitHubOAuth2Service: FastifyPluginAsync = async ( const url = `${GITHUB_BASE_URL}/login/oauth/authorize?client_id=${GITHUB_CLIENT_ID}&redirect_uri=${redirectCallback}` reply.statusCode = 200 return url - } + }, }) } diff --git a/src/services/users/oauth2/google/__utils__/utils.ts b/src/services/users/oauth2/google/__utils__/utils.ts index ba4e78f..d3a63cd 100644 --- a/src/services/users/oauth2/google/__utils__/utils.ts +++ b/src/services/users/oauth2/google/__utils__/utils.ts @@ -1,18 +1,18 @@ -import querystring from 'node:querystring' +import querystring from "node:querystring" -import axios from 'axios' +import axios from "axios" -import { OAuthStrategy } from '#src/tools/utils/OAuthStrategy.js' +import { OAuthStrategy } from "#src/tools/utils/OAuthStrategy.js" -export const GOOGLE_PROVIDER = 'Google' -export const GOOGLE_BASE_URL = 'https://accounts.google.com/o/oauth2/v2/auth' -export const GOOGLE_OAUTH2_TOKEN = 'https://oauth2.googleapis.com/token' +export const GOOGLE_PROVIDER = "Google" +export const GOOGLE_BASE_URL = "https://accounts.google.com/o/oauth2/v2/auth" +export const GOOGLE_OAUTH2_TOKEN = "https://oauth2.googleapis.com/token" export const GOOGLE_USERINFO = - 'https://www.googleapis.com/oauth2/v1/userinfo?alt=json' + "https://www.googleapis.com/oauth2/v1/userinfo?alt=json" export const GOOGLE_CLIENT_ID = - process.env['GOOGLE_CLIENT_ID'] ?? 'GOOGLE_CLIENT_ID' + process.env["GOOGLE_CLIENT_ID"] ?? "GOOGLE_CLIENT_ID" export const GOOGLE_CLIENT_SECRET = - process.env['GOOGLE_CLIENT_SECRET'] ?? 'GOOGLE_CLIENT_SECRET' + process.env["GOOGLE_CLIENT_SECRET"] ?? "GOOGLE_CLIENT_SECRET" export const googleStrategy = new OAuthStrategy(GOOGLE_PROVIDER) export interface GoogleUser { @@ -34,7 +34,7 @@ export interface GoogleTokens { export const getGoogleUserData = async ( code: string, - redirectURI: string + redirectURI: string, ): Promise => { const { data: token } = await axios.post( GOOGLE_OAUTH2_TOKEN, @@ -43,17 +43,17 @@ export const getGoogleUserData = async ( client_secret: GOOGLE_CLIENT_SECRET, code, redirect_uri: redirectURI, - grant_type: 'authorization_code' + grant_type: "authorization_code", }), { headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - Accept: 'application/json' - } - } + "Content-Type": "application/x-www-form-urlencoded", + Accept: "application/json", + }, + }, ) const { data: googleUser } = await axios.get( - `${GOOGLE_USERINFO}&access_token=${token.access_token}` + `${GOOGLE_USERINFO}&access_token=${token.access_token}`, ) return googleUser } diff --git a/src/services/users/oauth2/google/add-strategy/get.ts b/src/services/users/oauth2/google/add-strategy/get.ts index c8294b0..837308d 100644 --- a/src/services/users/oauth2/google/add-strategy/get.ts +++ b/src/services/users/oauth2/google/add-strategy/get.ts @@ -1,44 +1,44 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import { API_URL } from '#src/tools/configurations.js' -import { fastifyErrors } from '#src/models/utils.js' -import { GOOGLE_BASE_URL, GOOGLE_CLIENT_ID } from '../__utils__/utils.js' -import authenticateUser from '#src/tools/plugins/authenticateUser.js' +import { API_URL } from "#src/tools/configurations.js" +import { fastifyErrors } from "#src/models/utils.js" +import { GOOGLE_BASE_URL, GOOGLE_CLIENT_ID } from "../__utils__/utils.js" +import authenticateUser from "#src/tools/plugins/authenticateUser.js" const querySchema = Type.Object({ - redirectURI: Type.String({ format: 'uri-reference' }) + redirectURI: Type.String({ format: "uri-reference" }), }) type QuerySchemaType = Static const getServiceSchema: FastifySchema = { - description: 'Google OAuth2 - add-strategy', - tags: ['oauth2'] as string[], + description: "Google OAuth2 - add-strategy", + tags: ["oauth2"] as string[], security: [ { - bearerAuth: [] - } + bearerAuth: [], + }, ] as Array<{ [key: string]: [] }>, querystring: querySchema, response: { 200: Type.String(), 400: fastifyErrors[400], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const getAddStrategyGoogleOAuth2Service: FastifyPluginAsync = async ( - fastify + fastify, ) => { await fastify.register(authenticateUser) await fastify.route<{ Querystring: QuerySchemaType }>({ - method: 'GET', - url: '/users/oauth2/google/add-strategy', + method: "GET", + url: "/users/oauth2/google/add-strategy", schema: getServiceSchema, handler: async (request, reply) => { if (request.user == null) { @@ -49,6 +49,6 @@ export const getAddStrategyGoogleOAuth2Service: FastifyPluginAsync = async ( const url = `${GOOGLE_BASE_URL}?client_id=${GOOGLE_CLIENT_ID}&state=${request.user.accessToken}&redirect_uri=${redirectCallback}&response_type=code&scope=profile&access_type=online` reply.statusCode = 200 return url - } + }, }) } diff --git a/src/services/users/oauth2/google/callback-add-strategy/get.ts b/src/services/users/oauth2/google/callback-add-strategy/get.ts index ba87be5..a1fe043 100644 --- a/src/services/users/oauth2/google/callback-add-strategy/get.ts +++ b/src/services/users/oauth2/google/callback-add-strategy/get.ts @@ -1,30 +1,30 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import { API_URL } from '#src/tools/configurations.js' -import { fastifyErrors } from '#src/models/utils.js' -import { googleStrategy, getGoogleUserData } from '../__utils__/utils.js' -import { buildQueryURL } from '#src/tools/utils/buildQueryURL.js' -import { getUserWithBearerToken } from '#src/tools/plugins/authenticateUser.js' +import { API_URL } from "#src/tools/configurations.js" +import { fastifyErrors } from "#src/models/utils.js" +import { googleStrategy, getGoogleUserData } from "../__utils__/utils.js" +import { buildQueryURL } from "#src/tools/utils/buildQueryURL.js" +import { getUserWithBearerToken } from "#src/tools/plugins/authenticateUser.js" const querySchema = Type.Object({ code: Type.String(), state: Type.String(), - redirectURI: Type.String({ format: 'uri-reference' }) + redirectURI: Type.String({ format: "uri-reference" }), }) type QuerySchemaType = Static const getServiceSchema: FastifySchema = { - description: 'Google OAuth2 - callback-add-strategy', - tags: ['oauth2'] as string[], + description: "Google OAuth2 - callback-add-strategy", + tags: ["oauth2"] as string[], querystring: querySchema, response: { 200: Type.String(), 400: fastifyErrors[400], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const getCallbackAddStrategyGoogleOAuth2Service: FastifyPluginAsync = @@ -32,26 +32,26 @@ export const getCallbackAddStrategyGoogleOAuth2Service: FastifyPluginAsync = await fastify.route<{ Querystring: QuerySchemaType }>({ - method: 'GET', - url: '/users/oauth2/google/callback-add-strategy', + method: "GET", + url: "/users/oauth2/google/callback-add-strategy", schema: getServiceSchema, handler: async (request, reply) => { const { redirectURI, code, state: accessToken } = request.query const userRequest = await getUserWithBearerToken( - `Bearer ${accessToken}` + `Bearer ${accessToken}`, ) const googleUser = await getGoogleUserData( code, - `${API_URL}/users/oauth2/google/callback-add-strategy?redirectURI=${redirectURI}` + `${API_URL}/users/oauth2/google/callback-add-strategy?redirectURI=${redirectURI}`, ) const message = await googleStrategy.callbackAddStrategy( { name: googleUser.name, - id: googleUser.id + id: googleUser.id, }, - userRequest + userRequest, ) return await reply.redirect(buildQueryURL(redirectURI, { message })) - } + }, }) } diff --git a/src/services/users/oauth2/google/callback/get.ts b/src/services/users/oauth2/google/callback/get.ts index 188f52a..2fe9f60 100644 --- a/src/services/users/oauth2/google/callback/get.ts +++ b/src/services/users/oauth2/google/callback/get.ts @@ -1,50 +1,50 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import { API_URL } from '#src/tools/configurations.js' -import { fastifyErrors } from '#src/models/utils.js' -import { googleStrategy, getGoogleUserData } from '../__utils__/utils.js' -import { buildQueryURL } from '#src/tools/utils/buildQueryURL.js' +import { API_URL } from "#src/tools/configurations.js" +import { fastifyErrors } from "#src/models/utils.js" +import { googleStrategy, getGoogleUserData } from "../__utils__/utils.js" +import { buildQueryURL } from "#src/tools/utils/buildQueryURL.js" const querySchema = Type.Object({ code: Type.String(), - redirectURI: Type.String({ format: 'uri-reference' }) + redirectURI: Type.String({ format: "uri-reference" }), }) type QuerySchemaType = Static const getServiceSchema: FastifySchema = { - description: 'Google OAuth2 - callback', - tags: ['oauth2'] as string[], + description: "Google OAuth2 - callback", + tags: ["oauth2"] as string[], querystring: querySchema, response: { 200: Type.String(), 400: fastifyErrors[400], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const getCallbackGoogleOAuth2Service: FastifyPluginAsync = async ( - fastify + fastify, ) => { await fastify.route<{ Querystring: QuerySchemaType }>({ - method: 'GET', - url: '/users/oauth2/google/callback', + method: "GET", + url: "/users/oauth2/google/callback", schema: getServiceSchema, handler: async (request, reply) => { const { redirectURI, code } = request.query const googleUser = await getGoogleUserData( code, - `${API_URL}/users/oauth2/google/callback?redirectURI=${redirectURI}` + `${API_URL}/users/oauth2/google/callback?redirectURI=${redirectURI}`, ) const responseJWT = await googleStrategy.callbackSignin({ name: googleUser.name, - id: googleUser.id + id: googleUser.id, }) return await reply.redirect(buildQueryURL(redirectURI, responseJWT)) - } + }, }) } diff --git a/src/services/users/oauth2/google/signin/get.ts b/src/services/users/oauth2/google/signin/get.ts index 483f989..a759c35 100644 --- a/src/services/users/oauth2/google/signin/get.ts +++ b/src/services/users/oauth2/google/signin/get.ts @@ -1,36 +1,36 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import { API_URL } from '#src/tools/configurations.js' -import { fastifyErrors } from '#src/models/utils.js' -import { GOOGLE_BASE_URL, GOOGLE_CLIENT_ID } from '../__utils__/utils.js' +import { API_URL } from "#src/tools/configurations.js" +import { fastifyErrors } from "#src/models/utils.js" +import { GOOGLE_BASE_URL, GOOGLE_CLIENT_ID } from "../__utils__/utils.js" const querySchema = Type.Object({ - redirectURI: Type.String({ format: 'uri-reference' }) + redirectURI: Type.String({ format: "uri-reference" }), }) type QuerySchemaType = Static const getServiceSchema: FastifySchema = { - description: 'Google OAuth2 - signin', - tags: ['oauth2'] as string[], + description: "Google OAuth2 - signin", + tags: ["oauth2"] as string[], querystring: querySchema, response: { 200: Type.String(), 400: fastifyErrors[400], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const getSigninGoogleOAuth2Service: FastifyPluginAsync = async ( - fastify + fastify, ) => { await fastify.route<{ Querystring: QuerySchemaType }>({ - method: 'GET', - url: '/users/oauth2/google/signin', + method: "GET", + url: "/users/oauth2/google/signin", schema: getServiceSchema, handler: async (request, reply) => { const { redirectURI } = request.query @@ -38,6 +38,6 @@ export const getSigninGoogleOAuth2Service: FastifyPluginAsync = async ( const url = `${GOOGLE_BASE_URL}?client_id=${GOOGLE_CLIENT_ID}&redirect_uri=${redirectCallback}&response_type=code&scope=profile&access_type=online` reply.statusCode = 200 return url - } + }, }) } diff --git a/src/services/users/refresh-token/__test__/post.test.ts b/src/services/users/refresh-token/__test__/post.test.ts index 7c22795..be1d2b9 100644 --- a/src/services/users/refresh-token/__test__/post.test.ts +++ b/src/services/users/refresh-token/__test__/post.test.ts @@ -1,73 +1,73 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import sinon from 'sinon' -import jwt from 'jsonwebtoken' +import sinon from "sinon" +import jwt from "jsonwebtoken" -import { application } from '#src/application.js' -import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' -import prisma from '#src/tools/database/prisma.js' -import { refreshTokenExample } from '#src/models/RefreshToken.js' -import { expiresIn } from '#src/tools/utils/jwtToken.js' +import { application } from "#src/application.js" +import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" +import prisma from "#src/tools/database/prisma.js" +import { refreshTokenExample } from "#src/models/RefreshToken.js" +import { expiresIn } from "#src/tools/utils/jwtToken.js" -await test('POST /users/refresh-token', async (t) => { +await test("POST /users/refresh-token", async (t) => { t.afterEach(() => { sinon.restore() }) - await t.test('succeeds', async () => { + await t.test("succeeds", async () => { const { refreshToken, refreshTokenStubValue } = await authenticateUserTest() - sinon.stub(prisma, 'refreshToken').value({ + sinon.stub(prisma, "refreshToken").value({ ...refreshTokenStubValue, findFirst: async () => { return { ...refreshTokenExample, id: 1, - token: refreshToken + token: refreshToken, } - } + }, }) const response = await application.inject({ - method: 'POST', - url: '/users/refresh-token', - payload: { refreshToken } + method: "POST", + url: "/users/refresh-token", + payload: { refreshToken }, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 200) - assert.strictEqual(responseJson.type, 'Bearer') + assert.strictEqual(responseJson.type, "Bearer") assert.strictEqual(responseJson.expiresIn, expiresIn) - assert.strictEqual(typeof responseJson.accessToken, 'string') + assert.strictEqual(typeof responseJson.accessToken, "string") }) - await t.test('fails with refreshToken not saved in database', async () => { - sinon.stub(prisma, 'refreshToken').value({ + await t.test("fails with refreshToken not saved in database", async () => { + sinon.stub(prisma, "refreshToken").value({ findFirst: async () => { return null - } + }, }) const response = await application.inject({ - method: 'POST', - url: '/users/refresh-token', - payload: { refreshToken: 'somerandomtoken' } + method: "POST", + url: "/users/refresh-token", + payload: { refreshToken: "somerandomtoken" }, }) assert.strictEqual(response.statusCode, 403) }) - await t.test('fails with invalid jwt refreshToken', async () => { + await t.test("fails with invalid jwt refreshToken", async () => { const { refreshToken, refreshTokenStubValue } = await authenticateUserTest() - sinon.stub(prisma, 'refreshToken').value({ + sinon.stub(prisma, "refreshToken").value({ ...refreshTokenStubValue, findFirst: async () => { return refreshTokenExample - } + }, }) - sinon.stub(jwt, 'verify').value(() => { - throw new Error('Invalid token') + sinon.stub(jwt, "verify").value(() => { + throw new Error("Invalid token") }) const response = await application.inject({ - method: 'POST', - url: '/users/refresh-token', - payload: { refreshToken } + method: "POST", + url: "/users/refresh-token", + payload: { refreshToken }, }) assert.strictEqual(response.statusCode, 403) }) diff --git a/src/services/users/refresh-token/post.ts b/src/services/users/refresh-token/post.ts index 60ddb9d..dff6842 100644 --- a/src/services/users/refresh-token/post.ts +++ b/src/services/users/refresh-token/post.ts @@ -1,73 +1,73 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' -import jwt from 'jsonwebtoken' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" +import jwt from "jsonwebtoken" -import prisma from '#src/tools/database/prisma.js' -import { fastifyErrors } from '#src/models/utils.js' +import prisma from "#src/tools/database/prisma.js" +import { fastifyErrors } from "#src/models/utils.js" import { generateAccessToken, jwtSchema, - expiresIn -} from '#src/tools/utils/jwtToken.js' -import type { UserRefreshJWT } from '#src/models/User.js' -import { JWT_REFRESH_SECRET } from '#src/tools/configurations.js' + expiresIn, +} from "#src/tools/utils/jwtToken.js" +import type { UserRefreshJWT } from "#src/models/User.js" +import { JWT_REFRESH_SECRET } from "#src/tools/configurations.js" const bodyPostRefreshTokenSchema = Type.Object({ - refreshToken: jwtSchema.refreshToken + refreshToken: jwtSchema.refreshToken, }) type BodyPostRefreshTokenSchemaType = Static const postRefreshTokenSchema: FastifySchema = { - description: 'Refresh the accessToken of the user', - tags: ['users'] as string[], + description: "Refresh the accessToken of the user", + tags: ["users"] as string[], body: bodyPostRefreshTokenSchema, response: { 200: Type.Object({ accessToken: jwtSchema.accessToken, expiresIn: jwtSchema.expiresIn, - type: jwtSchema.type + type: jwtSchema.type, }), 400: fastifyErrors[400], 403: fastifyErrors[403], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const postRefreshTokenUser: FastifyPluginAsync = async (fastify) => { await fastify.route<{ Body: BodyPostRefreshTokenSchemaType }>({ - method: 'POST', - url: '/users/refresh-token', + method: "POST", + url: "/users/refresh-token", schema: postRefreshTokenSchema, handler: async (request, reply) => { const { refreshToken } = request.body try { const userRefreshJWT = jwt.verify( refreshToken, - JWT_REFRESH_SECRET + JWT_REFRESH_SECRET, ) as UserRefreshJWT const foundRefreshToken = await prisma.refreshToken.findFirst({ - where: { token: userRefreshJWT.tokenUUID } + where: { token: userRefreshJWT.tokenUUID }, }) if (foundRefreshToken == null) { throw fastify.httpErrors.forbidden() } const accessToken = generateAccessToken({ id: userRefreshJWT.id, - currentStrategy: userRefreshJWT.currentStrategy + currentStrategy: userRefreshJWT.currentStrategy, }) reply.statusCode = 200 return { accessToken, expiresIn, - type: 'Bearer' + type: "Bearer", } } catch { throw fastify.httpErrors.forbidden() } - } + }, }) } diff --git a/src/services/users/reset-password/__test__/post.test.ts b/src/services/users/reset-password/__test__/post.test.ts index 097a4da..8737a72 100644 --- a/src/services/users/reset-password/__test__/post.test.ts +++ b/src/services/users/reset-password/__test__/post.test.ts @@ -1,116 +1,116 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import sinon from 'sinon' -import ms from 'ms' +import sinon from "sinon" +import ms from "ms" -import { application } from '#src/application.js' -import prisma from '#src/tools/database/prisma.js' -import { userExample } from '#src/models/User.js' -import { userSettingsExample } from '#src/models/UserSettings.js' -import { emailTransporter } from '#src/tools/email/emailTransporter.js' +import { application } from "#src/application.js" +import prisma from "#src/tools/database/prisma.js" +import { userExample } from "#src/models/User.js" +import { userSettingsExample } from "#src/models/UserSettings.js" +import { emailTransporter } from "#src/tools/email/emailTransporter.js" -await test('POST /users/reset-password', async (t) => { +await test("POST /users/reset-password", async (t) => { t.afterEach(() => { sinon.restore() }) - await t.test('succeeds', async () => { - sinon.stub(prisma, 'user').value({ + await t.test("succeeds", async () => { + sinon.stub(prisma, "user").value({ findUnique: async () => { return userExample }, update: async () => { return { ...userExample, - temporaryExpirationToken: new Date(Date.now() + ms('1 hour')), - temporaryToken: 'random-token' + temporaryExpirationToken: new Date(Date.now() + ms("1 hour")), + temporaryToken: "random-token", } - } + }, }) - sinon.stub(prisma, 'userSetting').value({ + sinon.stub(prisma, "userSetting").value({ findFirst: async () => { return userSettingsExample - } + }, }) - sinon.stub(emailTransporter, 'sendMail').value(() => {}) + sinon.stub(emailTransporter, "sendMail").value(() => {}) const response = await application.inject({ - method: 'POST', - url: '/users/reset-password?redirectURI=https://redirecturi.com', - payload: { email: userExample.email } + method: "POST", + url: "/users/reset-password?redirectURI=https://redirecturi.com", + payload: { email: userExample.email }, }) assert.strictEqual(response.statusCode, 200) }) await t.test("fails with email that doesn't exist", async () => { - sinon.stub(prisma, 'user').value({ + sinon.stub(prisma, "user").value({ findUnique: async () => { return null - } + }, }) const response = await application.inject({ - method: 'POST', - url: '/users/reset-password?redirectURI=https://redirecturi.com', - payload: { email: userExample.email } + method: "POST", + url: "/users/reset-password?redirectURI=https://redirecturi.com", + payload: { email: userExample.email }, }) assert.strictEqual(response.statusCode, 400) }) - await t.test('fails with unconfirmed account', async () => { - sinon.stub(prisma, 'user').value({ + await t.test("fails with unconfirmed account", async () => { + sinon.stub(prisma, "user").value({ findUnique: async () => { return { ...userExample, - isConfirmed: false + isConfirmed: false, } - } + }, }) const response = await application.inject({ - method: 'POST', - url: '/users/reset-password?redirectURI=https://redirecturi.com', - payload: { email: userExample.email } + method: "POST", + url: "/users/reset-password?redirectURI=https://redirecturi.com", + payload: { email: userExample.email }, }) assert.strictEqual(response.statusCode, 400) }) await t.test("fails if userSettings doesn't exist", async () => { - sinon.stub(prisma, 'user').value({ + sinon.stub(prisma, "user").value({ findUnique: async () => { return userExample - } + }, }) - sinon.stub(prisma, 'userSetting').value({ + sinon.stub(prisma, "userSetting").value({ findFirst: async () => { return null - } + }, }) const response = await application.inject({ - method: 'POST', - url: '/users/reset-password?redirectURI=https://redirecturi.com', - payload: { email: userExample.email } + method: "POST", + url: "/users/reset-password?redirectURI=https://redirecturi.com", + payload: { email: userExample.email }, }) assert.strictEqual(response.statusCode, 400) }) - await t.test('fails with a request already in progress', async () => { - sinon.stub(prisma, 'user').value({ + await t.test("fails with a request already in progress", async () => { + sinon.stub(prisma, "user").value({ findUnique: async () => { return { ...userExample, - temporaryToken: 'random-token', - temporaryExpirationToken: new Date(Date.now() + ms('1 hour')) + temporaryToken: "random-token", + temporaryExpirationToken: new Date(Date.now() + ms("1 hour")), } - } + }, }) - sinon.stub(prisma, 'userSetting').value({ + sinon.stub(prisma, "userSetting").value({ findFirst: async () => { return userSettingsExample - } + }, }) const response = await application.inject({ - method: 'POST', - url: '/users/reset-password?redirectURI=https://redirecturi.com', - payload: { email: userExample.email } + method: "POST", + url: "/users/reset-password?redirectURI=https://redirecturi.com", + payload: { email: userExample.email }, }) assert.strictEqual(response.statusCode, 400) }) diff --git a/src/services/users/reset-password/__test__/put.test.ts b/src/services/users/reset-password/__test__/put.test.ts index 8c7436a..20925d1 100644 --- a/src/services/users/reset-password/__test__/put.test.ts +++ b/src/services/users/reset-password/__test__/put.test.ts @@ -1,69 +1,69 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import sinon from 'sinon' -import ms from 'ms' +import sinon from "sinon" +import ms from "ms" -import { application } from '#src/application.js' -import prisma from '#src/tools/database/prisma.js' -import { userExample } from '#src/models/User.js' +import { application } from "#src/application.js" +import prisma from "#src/tools/database/prisma.js" +import { userExample } from "#src/models/User.js" -await test('PUT /users/reset-password', async (t) => { +await test("PUT /users/reset-password", async (t) => { t.afterEach(() => { sinon.restore() }) - await t.test('succeeds', async () => { - const temporaryToken = 'random-token' - sinon.stub(prisma, 'user').value({ + await t.test("succeeds", async () => { + const temporaryToken = "random-token" + sinon.stub(prisma, "user").value({ findFirst: async () => { return { ...userExample, temporaryToken, - temporaryExpirationToken: new Date(Date.now() + ms('1 hour')) + temporaryExpirationToken: new Date(Date.now() + ms("1 hour")), } }, update: async () => { return userExample - } + }, }) - sinon.stub(prisma, 'refreshToken').value({ + sinon.stub(prisma, "refreshToken").value({ deleteMany: async () => { return { count: 1 } - } + }, }) const response = await application.inject({ - method: 'PUT', - url: '/users/reset-password', + method: "PUT", + url: "/users/reset-password", payload: { - password: 'new password', - temporaryToken: userExample.temporaryToken - } + password: "new password", + temporaryToken: userExample.temporaryToken, + }, }) assert.strictEqual(response.statusCode, 200) }) - await t.test('fails with expired temporaryToken', async () => { - const temporaryToken = 'random-token' - sinon.stub(prisma, 'user').value({ + await t.test("fails with expired temporaryToken", async () => { + const temporaryToken = "random-token" + sinon.stub(prisma, "user").value({ findFirst: async () => { return { ...userExample, temporaryToken, - temporaryExpirationToken: new Date(Date.now() - ms('1 hour')) + temporaryExpirationToken: new Date(Date.now() - ms("1 hour")), } }, update: async () => { return userExample - } + }, }) const response = await application.inject({ - method: 'PUT', - url: '/users/reset-password', + method: "PUT", + url: "/users/reset-password", payload: { - password: 'new password', - temporaryToken: userExample.temporaryToken - } + password: "new password", + temporaryToken: userExample.temporaryToken, + }, }) assert.strictEqual(response.statusCode, 400) }) diff --git a/src/services/users/reset-password/post.ts b/src/services/users/reset-password/post.ts index 85a1938..329a0bb 100644 --- a/src/services/users/reset-password/post.ts +++ b/src/services/users/reset-password/post.ts @@ -1,18 +1,18 @@ -import { randomUUID } from 'node:crypto' +import { randomUUID } from "node:crypto" -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' -import ms from 'ms' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" +import ms from "ms" -import prisma from '#src/tools/database/prisma.js' -import { fastifyErrors } from '#src/models/utils.js' -import { userSchema } from '#src/models/User.js' -import { sendEmail } from '#src/tools/email/sendEmail.js' -import type { Language, Theme } from '#src/models/UserSettings.js' +import prisma from "#src/tools/database/prisma.js" +import { fastifyErrors } from "#src/models/utils.js" +import { userSchema } from "#src/models/User.js" +import { sendEmail } from "#src/tools/email/sendEmail.js" +import type { Language, Theme } from "#src/models/UserSettings.js" const queryPostResetPasswordSchema = Type.Object({ - redirectURI: Type.String({ format: 'uri-reference' }) + redirectURI: Type.String({ format: "uri-reference" }), }) type QueryPostResetPasswordSchemaType = Static< @@ -20,7 +20,7 @@ type QueryPostResetPasswordSchemaType = Static< > const bodyPostResetPasswordSchema = Type.Object({ - email: userSchema.email + email: userSchema.email, }) type BodyPostResetPasswordSchemaType = Static< @@ -28,15 +28,15 @@ type BodyPostResetPasswordSchemaType = Static< > const postResetPasswordSchema: FastifySchema = { - description: 'Request a password-reset change', - tags: ['users'] as string[], + description: "Request a password-reset change", + tags: ["users"] as string[], body: bodyPostResetPasswordSchema, querystring: queryPostResetPasswordSchema, response: { 200: Type.String(), 400: fastifyErrors[400], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const postResetPasswordUser: FastifyPluginAsync = async (fastify) => { @@ -44,23 +44,23 @@ export const postResetPasswordUser: FastifyPluginAsync = async (fastify) => { Body: BodyPostResetPasswordSchemaType Querystring: QueryPostResetPasswordSchemaType }>({ - method: 'POST', - url: '/users/reset-password', + method: "POST", + url: "/users/reset-password", schema: postResetPasswordSchema, handler: async (request, reply) => { const { email } = request.body const { redirectURI } = request.query const user = await prisma.user.findUnique({ where: { - email - } + email, + }, }) if (user == null) { throw fastify.httpErrors.badRequest("Email address doesn't exist") } if (!user.isConfirmed) { throw fastify.httpErrors.badRequest( - 'You should have a confirmed account, please check your email and follow the instructions to verify your account' + "You should have a confirmed account, please check your email and follow the instructions to verify your account", ) } const isValidTemporaryToken = @@ -68,11 +68,11 @@ export const postResetPasswordUser: FastifyPluginAsync = async (fastify) => { user.temporaryExpirationToken.getTime() > Date.now() if (user.temporaryToken != null && isValidTemporaryToken) { throw fastify.httpErrors.badRequest( - 'A request to reset-password is already in progress' + "A request to reset-password is already in progress", ) } const userSettings = await prisma.userSetting.findFirst({ - where: { userId: user.id } + where: { userId: user.id }, }) if (userSettings == null) { throw fastify.httpErrors.badRequest() @@ -80,22 +80,22 @@ export const postResetPasswordUser: FastifyPluginAsync = async (fastify) => { const temporaryToken = randomUUID() await prisma.user.update({ where: { - id: user.id + id: user.id, }, data: { - temporaryExpirationToken: new Date(Date.now() + ms('1 hour')), - temporaryToken - } + temporaryExpirationToken: new Date(Date.now() + ms("1 hour")), + temporaryToken, + }, }) await sendEmail({ - type: 'reset-password', + type: "reset-password", email, url: `${redirectURI}?temporaryToken=${temporaryToken}`, language: userSettings.language as Language, - theme: userSettings.theme as Theme + theme: userSettings.theme as Theme, }) reply.statusCode = 200 - return 'Password-reset request successful, please check your emails!' - } + return "Password-reset request successful, please check your emails!" + }, }) } diff --git a/src/services/users/reset-password/put.ts b/src/services/users/reset-password/put.ts index 94d756f..ebe62ee 100644 --- a/src/services/users/reset-password/put.ts +++ b/src/services/users/reset-password/put.ts @@ -1,37 +1,37 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' -import bcrypt from 'bcryptjs' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" +import bcrypt from "bcryptjs" -import prisma from '#src/tools/database/prisma.js' -import { fastifyErrors } from '#src/models/utils.js' -import { userSchema } from '#src/models/User.js' +import prisma from "#src/tools/database/prisma.js" +import { fastifyErrors } from "#src/models/utils.js" +import { userSchema } from "#src/models/User.js" const bodyPutResetPasswordSchema = Type.Object({ password: userSchema.password, - temporaryToken: userSchema.temporaryToken + temporaryToken: userSchema.temporaryToken, }) type BodyPutResetPasswordSchemaType = Static const putResetPasswordSchema: FastifySchema = { description: - 'Change the user password if the provided temporaryToken (sent in the email of POST /users/reset-password) is correct.', - tags: ['users'] as string[], + "Change the user password if the provided temporaryToken (sent in the email of POST /users/reset-password) is correct.", + tags: ["users"] as string[], body: bodyPutResetPasswordSchema, response: { 200: Type.String(), 400: fastifyErrors[400], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const putResetPasswordUser: FastifyPluginAsync = async (fastify) => { await fastify.route<{ Body: BodyPutResetPasswordSchemaType }>({ - method: 'PUT', - url: '/users/reset-password', + method: "PUT", + url: "/users/reset-password", schema: putResetPasswordSchema, handler: async (request, reply) => { const { password, temporaryToken } = request.body @@ -40,26 +40,26 @@ export const putResetPasswordUser: FastifyPluginAsync = async (fastify) => { user?.temporaryExpirationToken != null && user.temporaryExpirationToken.getTime() > Date.now() if (user == null || !isValidTemporaryToken) { - throw fastify.httpErrors.badRequest('`temporaryToken` is invalid.') + throw fastify.httpErrors.badRequest("`temporaryToken` is invalid.") } const hashedPassword = await bcrypt.hash(password, 12) await prisma.user.update({ where: { - id: user.id + id: user.id, }, data: { password: hashedPassword, temporaryToken: null, - temporaryExpirationToken: null - } + temporaryExpirationToken: null, + }, }) await prisma.refreshToken.deleteMany({ where: { - userId: user.id - } + userId: user.id, + }, }) reply.statusCode = 200 - return 'The new password has been saved!' - } + return "The new password has been saved!" + }, }) } diff --git a/src/services/users/signin/__test__/post.test.ts b/src/services/users/signin/__test__/post.test.ts index 90a3a5e..7046ef7 100644 --- a/src/services/users/signin/__test__/post.test.ts +++ b/src/services/users/signin/__test__/post.test.ts @@ -1,111 +1,111 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import sinon from 'sinon' -import bcrypt from 'bcryptjs' +import sinon from "sinon" +import bcrypt from "bcryptjs" -import { application } from '#src/application.js' -import prisma from '#src/tools/database/prisma.js' -import { userExample } from '#src/models/User.js' -import { refreshTokenExample } from '#src/models/RefreshToken.js' -import { expiresIn } from '#src/tools/utils/jwtToken.js' +import { application } from "#src/application.js" +import prisma from "#src/tools/database/prisma.js" +import { userExample } from "#src/models/User.js" +import { refreshTokenExample } from "#src/models/RefreshToken.js" +import { expiresIn } from "#src/tools/utils/jwtToken.js" const payload = { email: userExample.email, - password: userExample.password + password: userExample.password, } -await test('POST /users/signin', async (t) => { +await test("POST /users/signin", async (t) => { t.afterEach(() => { sinon.restore() }) - await t.test('succeeds', async () => { - sinon.stub(prisma, 'user').value({ + await t.test("succeeds", async () => { + sinon.stub(prisma, "user").value({ findUnique: async () => { return { ...userExample, - password: await bcrypt.hash(payload.password as string, 12) + password: await bcrypt.hash(payload.password as string, 12), } - } + }, }) - sinon.stub(prisma, 'refreshToken').value({ + sinon.stub(prisma, "refreshToken").value({ create: async () => { return refreshTokenExample - } + }, }) const response = await application.inject({ - method: 'POST', - url: '/users/signin', - payload + method: "POST", + url: "/users/signin", + payload, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 200) - assert.strictEqual(responseJson.type, 'Bearer') + assert.strictEqual(responseJson.type, "Bearer") assert.strictEqual(responseJson.expiresIn, expiresIn) }) - await t.test('fails with invalid user', async () => { - sinon.stub(prisma, 'user').value({ + await t.test("fails with invalid user", async () => { + sinon.stub(prisma, "user").value({ findUnique: () => { return null - } + }, }) const response = await application.inject({ - method: 'POST', - url: '/users/signin', - payload + method: "POST", + url: "/users/signin", + payload, }) assert.strictEqual(response.statusCode, 400) }) - await t.test('fails with invalid email', async () => { - sinon.stub(prisma, 'user').value({ + await t.test("fails with invalid email", async () => { + sinon.stub(prisma, "user").value({ findUnique: () => { return null - } + }, }) const response = await application.inject({ - method: 'POST', - url: '/users/signin', + method: "POST", + url: "/users/signin", payload: { ...payload, - email: 'incorrect-email' - } + email: "incorrect-email", + }, }) assert.strictEqual(response.statusCode, 400) }) await t.test("fails if user hasn't got a password", async () => { - sinon.stub(prisma, 'user').value({ + sinon.stub(prisma, "user").value({ findUnique: () => { return { ...userExample, - password: null + password: null, } - } + }, }) const response = await application.inject({ - method: 'POST', - url: '/users/signin', - payload + method: "POST", + url: "/users/signin", + payload, }) assert.strictEqual(response.statusCode, 400) }) - await t.test('fails with incorrect password', async () => { - sinon.stub(prisma, 'user').value({ + await t.test("fails with incorrect password", async () => { + sinon.stub(prisma, "user").value({ findUnique: async () => { return userExample - } + }, }) const response = await application.inject({ - method: 'POST', - url: '/users/signin', + method: "POST", + url: "/users/signin", payload: { ...payload, - password: 'incorrect-password' - } + password: "incorrect-password", + }, }) assert.strictEqual(response.statusCode, 400) }) diff --git a/src/services/users/signin/post.ts b/src/services/users/signin/post.ts index 0350c08..584e35d 100644 --- a/src/services/users/signin/post.ts +++ b/src/services/users/signin/post.ts @@ -1,73 +1,73 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' -import bcrypt from 'bcryptjs' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" +import bcrypt from "bcryptjs" -import prisma from '#src/tools/database/prisma.js' -import { fastifyErrors } from '#src/models/utils.js' -import { userSchema } from '#src/models/User.js' +import prisma from "#src/tools/database/prisma.js" +import { fastifyErrors } from "#src/models/utils.js" +import { userSchema } from "#src/models/User.js" import { generateAccessToken, generateRefreshToken, jwtSchema, - expiresIn -} from '#src/tools/utils/jwtToken.js' + expiresIn, +} from "#src/tools/utils/jwtToken.js" const bodyPostSigninSchema = Type.Object({ email: userSchema.email, - password: userSchema.password + password: userSchema.password, }) type BodyPostSigninSchemaType = Static const postSigninSchema: FastifySchema = { - description: 'Signin the user', - tags: ['users'] as string[], + description: "Signin the user", + tags: ["users"] as string[], body: bodyPostSigninSchema, response: { 200: Type.Object(jwtSchema), 400: fastifyErrors[400], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const postSigninUser: FastifyPluginAsync = async (fastify) => { await fastify.route<{ Body: BodyPostSigninSchemaType }>({ - method: 'POST', - url: '/users/signin', + method: "POST", + url: "/users/signin", schema: postSigninSchema, handler: async (request, reply) => { const { email, password } = request.body const user = await prisma.user.findUnique({ - where: { email } + where: { email }, }) if (user == null) { - throw fastify.httpErrors.badRequest('Invalid credentials.') + throw fastify.httpErrors.badRequest("Invalid credentials.") } if (user.password == null) { - throw fastify.httpErrors.badRequest('Invalid credentials.') + throw fastify.httpErrors.badRequest("Invalid credentials.") } const isCorrectPassword = await bcrypt.compare(password, user.password) if (!isCorrectPassword) { - throw fastify.httpErrors.badRequest('Invalid credentials.') + throw fastify.httpErrors.badRequest("Invalid credentials.") } const accessToken = generateAccessToken({ - currentStrategy: 'Local', - id: user.id + currentStrategy: "Local", + id: user.id, }) const refreshToken = await generateRefreshToken({ - currentStrategy: 'Local', - id: user.id + currentStrategy: "Local", + id: user.id, }) reply.statusCode = 200 return { accessToken, refreshToken, expiresIn, - type: 'Bearer' + type: "Bearer", } - } + }, }) } diff --git a/src/services/users/signout/__test__/delete.test.ts b/src/services/users/signout/__test__/delete.test.ts index 9bbe8b6..f1fc0bc 100644 --- a/src/services/users/signout/__test__/delete.test.ts +++ b/src/services/users/signout/__test__/delete.test.ts @@ -1,39 +1,39 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import sinon from 'sinon' +import sinon from "sinon" -import { application } from '#src/application.js' -import { authenticateUserTest } from '#src/__test__/utils/authenticateUserTest.js' -import prisma from '#src/tools/database/prisma.js' +import { application } from "#src/application.js" +import { authenticateUserTest } from "#src/__test__/utils/authenticateUserTest.js" +import prisma from "#src/tools/database/prisma.js" -await test('DELETE /users/signout', async (t) => { +await test("DELETE /users/signout", async (t) => { t.afterEach(() => { sinon.restore() }) - await t.test('succeeds', async () => { + await t.test("succeeds", async () => { const { accessToken, refreshTokenStubValue } = await authenticateUserTest() - sinon.stub(prisma, 'refreshToken').value({ + sinon.stub(prisma, "refreshToken").value({ ...refreshTokenStubValue, deleteMany: async () => { return { count: 1 } - } + }, }) const response = await application.inject({ - method: 'DELETE', - url: '/users/signout', + method: "DELETE", + url: "/users/signout", headers: { - authorization: `Bearer ${accessToken}` - } + authorization: `Bearer ${accessToken}`, + }, }) assert.strictEqual(response.statusCode, 200) }) - await t.test('fails with empty authorized header', async () => { + await t.test("fails with empty authorized header", async () => { const response = await application.inject({ - method: 'DELETE', - url: '/users/signout' + method: "DELETE", + url: "/users/signout", }) assert.strictEqual(response.statusCode, 401) }) diff --git a/src/services/users/signout/__test__/post.test.ts b/src/services/users/signout/__test__/post.test.ts index 0034c70..d0124a4 100644 --- a/src/services/users/signout/__test__/post.test.ts +++ b/src/services/users/signout/__test__/post.test.ts @@ -1,52 +1,52 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import sinon from 'sinon' -import jwt from 'jsonwebtoken' +import sinon from "sinon" +import jwt from "jsonwebtoken" -import { application } from '#src/application.js' -import prisma from '#src/tools/database/prisma.js' -import { refreshTokenExample } from '#src/models/RefreshToken.js' -import type { UserRefreshJWT } from '#src/models/User.js' +import { application } from "#src/application.js" +import prisma from "#src/tools/database/prisma.js" +import { refreshTokenExample } from "#src/models/RefreshToken.js" +import type { UserRefreshJWT } from "#src/models/User.js" -await test('POST /users/signout', async (t) => { +await test("POST /users/signout", async (t) => { t.afterEach(() => { sinon.restore() }) - await t.test('succeeds', async () => { - sinon.stub(prisma, 'refreshToken').value({ + await t.test("succeeds", async () => { + sinon.stub(prisma, "refreshToken").value({ findFirst: async () => { return refreshTokenExample }, - delete: async () => {} + delete: async () => {}, }) - sinon.stub(jwt, 'verify').value(() => { + sinon.stub(jwt, "verify").value(() => { const value: UserRefreshJWT = { id: 1, tokenUUID: refreshTokenExample.token, - currentStrategy: 'Local' + currentStrategy: "Local", } return value }) const response = await application.inject({ - method: 'POST', - url: '/users/signout', - payload: { refreshToken: 'jwt token' } + method: "POST", + url: "/users/signout", + payload: { refreshToken: "jwt token" }, }) assert.strictEqual(response.statusCode, 200) }) - await t.test('fails with invalid refreshToken', async () => { - sinon.stub(prisma, 'refreshToken').value({ + await t.test("fails with invalid refreshToken", async () => { + sinon.stub(prisma, "refreshToken").value({ findFirst: async () => { return null - } + }, }) const response = await application.inject({ - method: 'POST', - url: '/users/signout', - payload: { refreshToken: 'somerandomtoken' } + method: "POST", + url: "/users/signout", + payload: { refreshToken: "somerandomtoken" }, }) assert.strictEqual(response.statusCode, 404) }) diff --git a/src/services/users/signout/delete.ts b/src/services/users/signout/delete.ts index b8f0254..c15cbcb 100644 --- a/src/services/users/signout/delete.ts +++ b/src/services/users/signout/delete.ts @@ -1,33 +1,33 @@ -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import prisma from '#src/tools/database/prisma.js' -import { fastifyErrors } from '#src/models/utils.js' -import authenticateUser from '#src/tools/plugins/authenticateUser.js' +import prisma from "#src/tools/database/prisma.js" +import { fastifyErrors } from "#src/models/utils.js" +import authenticateUser from "#src/tools/plugins/authenticateUser.js" const deleteSignoutSchema: FastifySchema = { - description: 'Signout the user to every connected devices', - tags: ['users'] as string[], + description: "Signout the user to every connected devices", + tags: ["users"] as string[], security: [ { - bearerAuth: [] - } + bearerAuth: [], + }, ] as Array<{ [key: string]: [] }>, response: { 200: Type.Object({}), 400: fastifyErrors[400], 401: fastifyErrors[401], 403: fastifyErrors[403], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const deleteSignoutUser: FastifyPluginAsync = async (fastify) => { await fastify.register(authenticateUser) fastify.route({ - method: 'DELETE', - url: '/users/signout', + method: "DELETE", + url: "/users/signout", schema: deleteSignoutSchema, handler: async (request, reply) => { if (request.user == null) { @@ -35,11 +35,11 @@ export const deleteSignoutUser: FastifyPluginAsync = async (fastify) => { } await prisma.refreshToken.deleteMany({ where: { - userId: request.user.current.id - } + userId: request.user.current.id, + }, }) reply.statusCode = 200 return {} - } + }, }) } diff --git a/src/services/users/signout/post.ts b/src/services/users/signout/post.ts index b58f064..e4878b8 100644 --- a/src/services/users/signout/post.ts +++ b/src/services/users/signout/post.ts @@ -1,62 +1,62 @@ -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' -import jwt from 'jsonwebtoken' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import type { FastifyPluginAsync, FastifySchema } from "fastify" +import jwt from "jsonwebtoken" -import prisma from '#src/tools/database/prisma.js' -import { fastifyErrors } from '#src/models/utils.js' -import { JWT_REFRESH_SECRET } from '#src/tools/configurations.js' -import type { UserRefreshJWT } from '#src/models/User.js' -import { jwtSchema } from '#src/tools/utils/jwtToken.js' +import prisma from "#src/tools/database/prisma.js" +import { fastifyErrors } from "#src/models/utils.js" +import { JWT_REFRESH_SECRET } from "#src/tools/configurations.js" +import type { UserRefreshJWT } from "#src/models/User.js" +import { jwtSchema } from "#src/tools/utils/jwtToken.js" const bodyPostSignoutSchema = Type.Object({ - refreshToken: jwtSchema.refreshToken + refreshToken: jwtSchema.refreshToken, }) type BodyPostSignoutSchemaType = Static const postSignoutSchema: FastifySchema = { - description: 'Signout the user', - tags: ['users'] as string[], + description: "Signout the user", + tags: ["users"] as string[], body: bodyPostSignoutSchema, response: { 200: Type.Object({}), 400: fastifyErrors[400], 404: fastifyErrors[404], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const postSignoutUser: FastifyPluginAsync = async (fastify) => { await fastify.route<{ Body: BodyPostSignoutSchemaType }>({ - method: 'POST', - url: '/users/signout', + method: "POST", + url: "/users/signout", schema: postSignoutSchema, handler: async (request, reply) => { const { refreshToken } = request.body try { const userRefreshJWT = jwt.verify( refreshToken, - JWT_REFRESH_SECRET + JWT_REFRESH_SECRET, ) as UserRefreshJWT const foundRefreshToken = await prisma.refreshToken.findFirst({ - where: { token: userRefreshJWT.tokenUUID } + where: { token: userRefreshJWT.tokenUUID }, }) if (foundRefreshToken == null) { throw fastify.httpErrors.notFound() } await prisma.refreshToken.delete({ where: { - id: foundRefreshToken.id - } + id: foundRefreshToken.id, + }, }) reply.statusCode = 200 return {} } catch { throw fastify.httpErrors.notFound() } - } + }, }) } diff --git a/src/services/users/signup/__test__/post.test.ts b/src/services/users/signup/__test__/post.test.ts index b487900..4cb25dc 100644 --- a/src/services/users/signup/__test__/post.test.ts +++ b/src/services/users/signup/__test__/post.test.ts @@ -1,46 +1,46 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import sinon from 'sinon' +import sinon from "sinon" -import { application } from '#src/application.js' -import prisma from '#src/tools/database/prisma.js' -import { userExample } from '#src/models/User.js' -import { userSettingsExample } from '#src/models/UserSettings.js' -import { emailTransporter } from '#src/tools/email/emailTransporter.js' +import { application } from "#src/application.js" +import prisma from "#src/tools/database/prisma.js" +import { userExample } from "#src/models/User.js" +import { userSettingsExample } from "#src/models/UserSettings.js" +import { emailTransporter } from "#src/tools/email/emailTransporter.js" const payload = { name: userExample.name, email: userExample.email, password: userExample.password, theme: userSettingsExample.theme, - language: userSettingsExample.language + language: userSettingsExample.language, } -await test('POST /users/signup', async (t) => { +await test("POST /users/signup", async (t) => { t.afterEach(() => { sinon.restore() }) - await t.test('succeeds', async () => { - sinon.stub(prisma, 'user').value({ + await t.test("succeeds", async () => { + sinon.stub(prisma, "user").value({ findFirst: async () => { return null }, create: async () => { return userExample - } + }, }) - sinon.stub(prisma, 'userSetting').value({ + sinon.stub(prisma, "userSetting").value({ create: async () => { return userSettingsExample - } + }, }) - sinon.stub(emailTransporter, 'sendMail').value(() => {}) + sinon.stub(emailTransporter, "sendMail").value(() => {}) const response = await application.inject({ - method: 'POST', - url: '/users/signup', - payload + method: "POST", + url: "/users/signup", + payload, }) const responseJson = response.json() assert.strictEqual(response.statusCode, 201) @@ -48,35 +48,35 @@ await test('POST /users/signup', async (t) => { assert.strictEqual(responseJson.user.email, userExample.email) }) - await t.test('fails with invalid email', async () => { - sinon.stub(prisma, 'user').value({ + await t.test("fails with invalid email", async () => { + sinon.stub(prisma, "user").value({ findFirst: async () => { return null - } + }, }) - sinon.stub(emailTransporter, 'sendMail').value(() => {}) + sinon.stub(emailTransporter, "sendMail").value(() => {}) const response = await application.inject({ - method: 'POST', - url: '/users/signup', + method: "POST", + url: "/users/signup", payload: { ...payload, - email: 'incorrect-email@abc' - } + email: "incorrect-email@abc", + }, }) assert.strictEqual(response.statusCode, 400) }) - await t.test('fails with already taken `name` or `email`', async () => { - sinon.stub(prisma, 'user').value({ + await t.test("fails with already taken `name` or `email`", async () => { + sinon.stub(prisma, "user").value({ findFirst: async () => { return userExample - } + }, }) - sinon.stub(emailTransporter, 'sendMail').value(() => {}) + sinon.stub(emailTransporter, "sendMail").value(() => {}) const response = await application.inject({ - method: 'POST', - url: '/users/signup', - payload + method: "POST", + url: "/users/signup", + payload, }) assert.strictEqual(response.statusCode, 400) }) diff --git a/src/services/users/signup/post.ts b/src/services/users/signup/post.ts index 506d367..7d214f0 100644 --- a/src/services/users/signup/post.ts +++ b/src/services/users/signup/post.ts @@ -1,34 +1,34 @@ -import { randomUUID } from 'node:crypto' +import { randomUUID } from "node:crypto" -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' -import bcrypt from 'bcryptjs' -import type { FastifyPluginAsync, FastifySchema } from 'fastify' +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" +import bcrypt from "bcryptjs" +import type { FastifyPluginAsync, FastifySchema } from "fastify" -import prisma from '#src/tools/database/prisma.js' -import { fastifyErrors } from '#src/models/utils.js' -import type { BodyUserSchemaType } from '#src/models/User.js' -import { bodyUserSchema, userPublicSchema } from '#src/models/User.js' -import { sendEmail } from '#src/tools/email/sendEmail.js' -import { API_URL } from '#src/tools/configurations.js' +import prisma from "#src/tools/database/prisma.js" +import { fastifyErrors } from "#src/models/utils.js" +import type { BodyUserSchemaType } from "#src/models/User.js" +import { bodyUserSchema, userPublicSchema } from "#src/models/User.js" +import { sendEmail } from "#src/tools/email/sendEmail.js" +import { API_URL } from "#src/tools/configurations.js" const queryPostSignupSchema = Type.Object({ - redirectURI: Type.Optional(Type.String({ format: 'uri-reference' })) + redirectURI: Type.Optional(Type.String({ format: "uri-reference" })), }) type QueryPostSignupSchemaType = Static const postSignupSchema: FastifySchema = { description: - 'Allows a new user to signup, if success he would need to confirm his email.', - tags: ['users'] as string[], + "Allows a new user to signup, if success he would need to confirm his email.", + tags: ["users"] as string[], body: bodyUserSchema, querystring: queryPostSignupSchema, response: { 201: Type.Object({ user: Type.Object(userPublicSchema) }), 400: fastifyErrors[400], - 500: fastifyErrors[500] - } + 500: fastifyErrors[500], + }, } as const export const postSignupUser: FastifyPluginAsync = async (fastify) => { @@ -36,20 +36,20 @@ export const postSignupUser: FastifyPluginAsync = async (fastify) => { Body: BodyUserSchemaType Querystring: QueryPostSignupSchemaType }>({ - method: 'POST', - url: '/users/signup', + method: "POST", + url: "/users/signup", schema: postSignupSchema, handler: async (request, reply) => { const { name, email, password, theme, language } = request.body const { redirectURI } = request.query const userValidation = await prisma.user.findFirst({ where: { - OR: [{ email }, { name }] - } + OR: [{ email }, { name }], + }, }) if (userValidation != null) { throw fastify.httpErrors.badRequest( - 'body.email or body.name already taken.' + "body.email or body.name already taken.", ) } const hashedPassword = await bcrypt.hash(password, 12) @@ -59,32 +59,32 @@ export const postSignupUser: FastifyPluginAsync = async (fastify) => { name, email, password: hashedPassword, - temporaryToken - } + temporaryToken, + }, }) const userSettings = await prisma.userSetting.create({ data: { userId: user.id, theme, - language - } + language, + }, }) const redirectQuery = - redirectURI != null ? `&redirectURI=${redirectURI}` : '' + redirectURI != null ? `&redirectURI=${redirectURI}` : "" await sendEmail({ - type: 'confirm-email', + type: "confirm-email", email, url: `${API_URL}/users/confirm-email?temporaryToken=${temporaryToken}${redirectQuery}`, language, - theme + theme, }) reply.statusCode = 201 return { user: { ...user, - settings: { ...userSettings } - } + settings: { ...userSettings }, + }, } - } + }, }) } diff --git a/src/tools/configurations.ts b/src/tools/configurations.ts index 63439a0..4575e6f 100644 --- a/src/tools/configurations.ts +++ b/src/tools/configurations.ts @@ -1,25 +1,25 @@ -import { URL } from 'node:url' +import { URL } from "node:url" -import dotenv from 'dotenv' +import dotenv from "dotenv" dotenv.config() -export const PORT = Number.parseInt(process.env['PORT'] ?? '8080', 10) -export const HOST = process.env['HOST'] ?? '0.0.0.0' -export const API_URL = process.env['API_URL'] ?? `http://${HOST}:${PORT}` +export const PORT = Number.parseInt(process.env["PORT"] ?? "8080", 10) +export const HOST = process.env["HOST"] ?? "0.0.0.0" +export const API_URL = process.env["API_URL"] ?? `http://${HOST}:${PORT}` export const FILE_UPLOADS_API_URL = - process.env['FILE_UPLOADS_API_URL'] ?? 'http://localhost:8000' + process.env["FILE_UPLOADS_API_URL"] ?? "http://localhost:8000" export const FILE_UPLOADS_API_KEY = - process.env['FILE_UPLOADS_API_KEY'] ?? 'apiKeySecret' + process.env["FILE_UPLOADS_API_KEY"] ?? "apiKeySecret" export const JWT_ACCESS_SECRET = - process.env['JWT_ACCESS_SECRET'] ?? 'accessTokenSecret' + process.env["JWT_ACCESS_SECRET"] ?? "accessTokenSecret" export const JWT_REFRESH_SECRET = - process.env['JWT_REFRESH_SECRET'] ?? 'refreshTokenSecret' + process.env["JWT_REFRESH_SECRET"] ?? "refreshTokenSecret" export const JWT_ACCESS_EXPIRES_IN = - process.env['JWT_ACCESS_EXPIRES_IN'] ?? '15 minutes' + process.env["JWT_ACCESS_EXPIRES_IN"] ?? "15 minutes" -export const SRC_URL = new URL('../', import.meta.url) -export const ROOT_URL = new URL('../', SRC_URL) -export const EMAIL_URL = new URL('./email/', ROOT_URL) -export const EMAIL_TEMPLATE_URL = new URL('./email-template.ejs', EMAIL_URL) -export const EMAIL_LOCALES_URL = new URL('./locales/', EMAIL_URL) +export const SRC_URL = new URL("../", import.meta.url) +export const ROOT_URL = new URL("../", SRC_URL) +export const EMAIL_URL = new URL("./email/", ROOT_URL) +export const EMAIL_TEMPLATE_URL = new URL("./email-template.ejs", EMAIL_URL) +export const EMAIL_LOCALES_URL = new URL("./locales/", EMAIL_URL) diff --git a/src/tools/database/pagination.ts b/src/tools/database/pagination.ts index ceb13ad..027bc43 100644 --- a/src/tools/database/pagination.ts +++ b/src/tools/database/pagination.ts @@ -1,6 +1,6 @@ -import type { Prisma } from '@prisma/client' -import type { Static } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import type { Prisma } from "@prisma/client" +import type { Static } from "@sinclair/typebox" +import { Type } from "@sinclair/typebox" export const queryPaginationSchema = { /** Maximum number of items to return */ @@ -8,11 +8,11 @@ export const queryPaginationSchema = { /** The before and after are mutually exclusive, only one may be passed at a time. */ before: Type.Optional( - Type.Integer({ minimum: 1, description: 'Get items before this id' }) + Type.Integer({ minimum: 1, description: "Get items before this id" }), ), after: Type.Optional( - Type.Integer({ minimum: 1, description: 'Get items after this id' }) - ) + Type.Integer({ minimum: 1, description: "Get items after this id" }), + ), } export const queryPaginationObjectSchema = Type.Object(queryPaginationSchema) @@ -22,20 +22,20 @@ export type QueryPaginationSchemaType = Static< > export const getPaginationOptions = ( - query: QueryPaginationSchemaType + query: QueryPaginationSchemaType, ): Prisma.SelectSubset => { return { take: query.before != null ? query.limit * -1 : query.limit, skip: query.after != null || query.before != null ? 1 : undefined, ...(query.after != null && { cursor: { - id: query.after - } + id: query.after, + }, }), ...(query.before != null && { cursor: { - id: query.before - } - }) + id: query.before, + }, + }), } } diff --git a/src/tools/database/prisma.ts b/src/tools/database/prisma.ts index 423a93d..838b4d6 100644 --- a/src/tools/database/prisma.ts +++ b/src/tools/database/prisma.ts @@ -1,12 +1,12 @@ -import Prisma from '@prisma/client' +import Prisma from "@prisma/client" const { PrismaClient } = Prisma const prisma = new PrismaClient({ log: - process.env['NODE_ENV'] === 'development' - ? ['query', 'info', 'warn', 'error'] - : ['error'] + process.env["NODE_ENV"] === "development" + ? ["query", "info", "warn", "error"] + : ["error"], }) export default prisma diff --git a/src/tools/email/emailTransporter.ts b/src/tools/email/emailTransporter.ts index 8d94aa1..3a1b1cf 100644 --- a/src/tools/email/emailTransporter.ts +++ b/src/tools/email/emailTransporter.ts @@ -1,19 +1,19 @@ -import dotenv from 'dotenv' -import nodemailer from 'nodemailer' -import type SMTPTransport from 'nodemailer/lib/smtp-transport/index.js' +import dotenv from "dotenv" +import nodemailer from "nodemailer" +import type SMTPTransport from "nodemailer/lib/smtp-transport/index.js" dotenv.config() -const EMAIL_PORT = Number.parseInt(process.env['EMAIL_PORT'] ?? '465', 10) +const EMAIL_PORT = Number.parseInt(process.env["EMAIL_PORT"] ?? "465", 10) export const EMAIL_INFO: SMTPTransport.Options = { - host: process.env['EMAIL_HOST'], + host: process.env["EMAIL_HOST"], port: EMAIL_PORT, secure: EMAIL_PORT === 465, auth: { - user: process.env['EMAIL_USER'], - pass: process.env['EMAIL_PASSWORD'] + user: process.env["EMAIL_USER"], + pass: process.env["EMAIL_PASSWORD"], }, - ignoreTLS: process.env['NODE_ENV'] !== 'production' + ignoreTLS: process.env["NODE_ENV"] !== "production", } export const emailTransporter = nodemailer.createTransport(EMAIL_INFO) diff --git a/src/tools/email/sendEmail.ts b/src/tools/email/sendEmail.ts index e3a5f1e..976769e 100644 --- a/src/tools/email/sendEmail.ts +++ b/src/tools/email/sendEmail.ts @@ -1,17 +1,17 @@ -import fs from 'node:fs' -import { URL, fileURLToPath } from 'node:url' +import fs from "node:fs" +import { URL, fileURLToPath } from "node:url" -import ejs from 'ejs' +import ejs from "ejs" -import type { Language, Theme } from '#src/models/UserSettings.js' +import type { Language, Theme } from "#src/models/UserSettings.js" import { EMAIL_LOCALES_URL, - EMAIL_TEMPLATE_URL -} from '#src/tools/configurations.js' + EMAIL_TEMPLATE_URL, +} from "#src/tools/configurations.js" import { emailTransporter, - EMAIL_INFO -} from '#src/tools/email/emailTransporter.js' + EMAIL_INFO, +} from "#src/tools/email/emailTransporter.js" interface EmailTranslation { subject: string @@ -22,7 +22,7 @@ interface EmailTranslation { } } -type EmailType = 'confirm-email' | 'reset-password' +type EmailType = "confirm-email" | "reset-password" interface SendEmailOptions { email: string @@ -42,46 +42,46 @@ type ThemeColor = { const themeColors: ThemeColor = { dark: { - backgroundPrimary: '#212121', - colorPrimary: '#27B05E', - colorSecondary: '#fff' + backgroundPrimary: "#212121", + colorPrimary: "#27B05E", + colorSecondary: "#fff", }, light: { - backgroundPrimary: '#fff', - colorPrimary: '#27B05E', - colorSecondary: '#181818' - } + backgroundPrimary: "#fff", + colorPrimary: "#27B05E", + colorSecondary: "#181818", + }, } const getEmailTranslation = async ( language: Language, - type: EmailType + type: EmailType, ): Promise => { const filename = `${type}.json` let emailTranslationURL = new URL( `./${language}/${filename}`, - EMAIL_LOCALES_URL + EMAIL_LOCALES_URL, ) if (!fs.existsSync(emailTranslationURL)) { emailTranslationURL = new URL(`./en/${filename}`, EMAIL_LOCALES_URL) } const translationString = await fs.promises.readFile(emailTranslationURL, { - encoding: 'utf-8' + encoding: "utf-8", }) return JSON.parse(translationString) } export const sendEmail = async (options: SendEmailOptions): Promise => { - const { email, type, url, theme = 'dark', language = 'en' } = options + const { email, type, url, theme = "dark", language = "en" } = options const emailTranslation = await getEmailTranslation(language, type) const emailHTML = await ejs.renderFile(fileURLToPath(EMAIL_TEMPLATE_URL), { text: { ...emailTranslation.renderOptions, url }, - theme: themeColors[theme] + theme: themeColors[theme], }) await emailTransporter.sendMail({ from: `"Thream" <${EMAIL_INFO?.auth?.user as string}>`, to: email, subject: `Thream - ${emailTranslation.subject}`, - html: emailHTML + html: emailHTML, }) } diff --git a/src/tools/plugins/__test__/authenticateUser.test.ts b/src/tools/plugins/__test__/authenticateUser.test.ts index 314383e..7c89c20 100644 --- a/src/tools/plugins/__test__/authenticateUser.test.ts +++ b/src/tools/plugins/__test__/authenticateUser.test.ts @@ -1,97 +1,97 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import sinon from 'sinon' -import httpErrors from 'http-errors' -import jwt from 'jsonwebtoken' +import sinon from "sinon" +import httpErrors from "http-errors" +import jwt from "jsonwebtoken" -import { getUserWithBearerToken } from '#src/tools/plugins/authenticateUser.js' -import prisma from '#src/tools/database/prisma.js' -import { userExample } from '#src/models/User.js' +import { getUserWithBearerToken } from "#src/tools/plugins/authenticateUser.js" +import prisma from "#src/tools/database/prisma.js" +import { userExample } from "#src/models/User.js" const { Unauthorized, Forbidden, BadRequest } = httpErrors -await test('tools/plugins/authenticateUser - getUserWithBearerToken', async (t) => { +await test("tools/plugins/authenticateUser - getUserWithBearerToken", async (t) => { t.afterEach(() => { sinon.restore() }) - await t.test('shoulds succeeds with the right information', async () => { - sinon.stub(prisma, 'user').value({ + await t.test("shoulds succeeds with the right information", async () => { + sinon.stub(prisma, "user").value({ findUnique: async () => { return userExample - } + }, }) - const currentStrategy = 'Local' - sinon.stub(jwt, 'verify').value(() => { + const currentStrategy = "Local" + sinon.stub(jwt, "verify").value(() => { return { id: userExample.id, currentStrategy } }) - const userWithBearerToken = await getUserWithBearerToken('Bearer token') + const userWithBearerToken = await getUserWithBearerToken("Bearer token") assert.strictEqual(userWithBearerToken.current.id, userExample.id) assert.strictEqual(userWithBearerToken.current.name, userExample.name) - assert.strictEqual(userWithBearerToken.accessToken, 'token') + assert.strictEqual(userWithBearerToken.accessToken, "token") assert.strictEqual(userWithBearerToken.currentStrategy, currentStrategy) }) await t.test( - 'shoulds throws `Unauthorized` if `bearerToken` is not a string', + "shoulds throws `Unauthorized` if `bearerToken` is not a string", async () => { await assert.rejects(getUserWithBearerToken(undefined), Unauthorized) - } + }, ) await t.test( 'shoulds throws `Unauthorized` if `bearerToken` is not to the right format: `"Bearer token"`', async () => { - await assert.rejects(getUserWithBearerToken('Bearer'), Unauthorized) - await assert.rejects(getUserWithBearerToken(''), Unauthorized) + await assert.rejects(getUserWithBearerToken("Bearer"), Unauthorized) + await assert.rejects(getUserWithBearerToken(""), Unauthorized) await assert.rejects( - getUserWithBearerToken('Bearer token token2'), - Unauthorized + getUserWithBearerToken("Bearer token token2"), + Unauthorized, ) - } + }, ) await t.test( - 'shoulds throws `Forbidden` if invalid `bearerToken` by `jwt.verify`', + "shoulds throws `Forbidden` if invalid `bearerToken` by `jwt.verify`", async () => { - sinon.stub(jwt, 'verify').value(() => { - throw new Error('Invalid token') + sinon.stub(jwt, "verify").value(() => { + throw new Error("Invalid token") }) - await assert.rejects(getUserWithBearerToken('Bearer token'), Forbidden) - } + await assert.rejects(getUserWithBearerToken("Bearer token"), Forbidden) + }, ) await t.test( "shoulds throws `Forbidden` if the user doesn't exist", async () => { - sinon.stub(prisma, 'user').value({ + sinon.stub(prisma, "user").value({ findUnique: async () => { return null - } + }, }) - sinon.stub(jwt, 'verify').value(() => { + sinon.stub(jwt, "verify").value(() => { return { id: userExample.id } }) - await assert.rejects(getUserWithBearerToken('Bearer token'), Forbidden) - } + await assert.rejects(getUserWithBearerToken("Bearer token"), Forbidden) + }, ) await t.test( - 'shoulds throws `BadRequest` if the user account is not confirmed', + "shoulds throws `BadRequest` if the user account is not confirmed", async () => { - sinon.stub(prisma, 'user').value({ + sinon.stub(prisma, "user").value({ findUnique: async () => { return { ...userExample, - isConfirmed: false + isConfirmed: false, } - } + }, }) - sinon.stub(jwt, 'verify').value(() => { - return { id: userExample.id, currentStrategy: 'Local' } + sinon.stub(jwt, "verify").value(() => { + return { id: userExample.id, currentStrategy: "Local" } }) - await assert.rejects(getUserWithBearerToken('Bearer token'), BadRequest) - } + await assert.rejects(getUserWithBearerToken("Bearer token"), BadRequest) + }, ) }) diff --git a/src/tools/plugins/__test__/socket-io.test.ts b/src/tools/plugins/__test__/socket-io.test.ts index b39ecf8..330cc76 100644 --- a/src/tools/plugins/__test__/socket-io.test.ts +++ b/src/tools/plugins/__test__/socket-io.test.ts @@ -1,17 +1,17 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import fastify from 'fastify' +import fastify from "fastify" -import fastifySocketIo from '#src/tools/plugins/socket-io.js' +import fastifySocketIo from "#src/tools/plugins/socket-io.js" -await test('tools/plugins/socket-io', async (t) => { - await t.test('should close socket server on fastify close', async () => { +await test("tools/plugins/socket-io", async (t) => { + await t.test("should close socket server on fastify close", async () => { const PORT = 3030 const application = fastify() await application.register(fastifySocketIo) await application.listen({ - port: PORT + port: PORT, }) assert.notStrictEqual(application.io, null) await application.close() diff --git a/src/tools/plugins/authenticateUser.ts b/src/tools/plugins/authenticateUser.ts index ba2e46c..4ba1450 100644 --- a/src/tools/plugins/authenticateUser.ts +++ b/src/tools/plugins/authenticateUser.ts @@ -1,26 +1,26 @@ -import fastifyPlugin from 'fastify-plugin' -import httpErrors from 'http-errors' -import jwt from 'jsonwebtoken' +import fastifyPlugin from "fastify-plugin" +import httpErrors from "http-errors" +import jwt from "jsonwebtoken" -import prisma from '#src/tools/database/prisma.js' -import type { UserJWT, UserRequest } from '#src/models/User.js' -import { JWT_ACCESS_SECRET } from '#src/tools/configurations.js' +import prisma from "#src/tools/database/prisma.js" +import type { UserJWT, UserRequest } from "#src/models/User.js" +import { JWT_ACCESS_SECRET } from "#src/tools/configurations.js" const { Unauthorized, Forbidden, BadRequest } = httpErrors export const getUserWithBearerToken = async ( - bearerToken?: string + bearerToken?: string, ): Promise => { - if (bearerToken == null || typeof bearerToken !== 'string') { + if (bearerToken == null || typeof bearerToken !== "string") { throw new Unauthorized() } - const tokenSplitted = bearerToken.split(' ') - if (tokenSplitted.length !== 2 || tokenSplitted[0] !== 'Bearer') { + const tokenSplitted = bearerToken.split(" ") + if (tokenSplitted.length !== 2 || tokenSplitted[0] !== "Bearer") { throw new Unauthorized() } - const token = tokenSplitted[1] ?? 'token' + const token = tokenSplitted[1] ?? "token" let payload: UserJWT try { payload = jwt.verify(token, JWT_ACCESS_SECRET) as unknown as UserJWT @@ -33,20 +33,20 @@ export const getUserWithBearerToken = async ( throw new Forbidden() } - if (!user.isConfirmed && payload.currentStrategy === 'Local') { + if (!user.isConfirmed && payload.currentStrategy === "Local") { throw new BadRequest( - 'You should have a confirmed account, please check your email and follow the instructions to verify your account' + "You should have a confirmed account, please check your email and follow the instructions to verify your account", ) } return { current: user, currentStrategy: payload.currentStrategy, - accessToken: token + accessToken: token, } } -declare module 'fastify' { +declare module "fastify" { export interface FastifyRequest { user?: UserRequest } @@ -54,12 +54,12 @@ declare module 'fastify' { export default fastifyPlugin( async (fastify) => { - fastify.decorateRequest('user', undefined) - fastify.addHook('onRequest', async (request) => { + fastify.decorateRequest("user", undefined) + fastify.addHook("onRequest", async (request) => { const { authorization } = request.headers const user = await getUserWithBearerToken(authorization) request.user = user }) }, - { fastify: '4.x' } + { fastify: "4.x" }, ) diff --git a/src/tools/plugins/socket-io.ts b/src/tools/plugins/socket-io.ts index 63c9126..2eb9c04 100644 --- a/src/tools/plugins/socket-io.ts +++ b/src/tools/plugins/socket-io.ts @@ -1,15 +1,15 @@ -import fastifyPlugin from 'fastify-plugin' -import type { ServerOptions } from 'socket.io' -import { Server as SocketIoServer } from 'socket.io' -import { authorize } from '@thream/socketio-jwt' +import fastifyPlugin from "fastify-plugin" +import type { ServerOptions } from "socket.io" +import { Server as SocketIoServer } from "socket.io" +import { authorize } from "@thream/socketio-jwt" -import prisma from '#src/tools/database/prisma.js' -import { JWT_ACCESS_SECRET } from '#src/tools/configurations.js' +import prisma from "#src/tools/database/prisma.js" +import { JWT_ACCESS_SECRET } from "#src/tools/configurations.js" interface EmitEventOptions { event: string payload: { - action: 'create' | 'delete' | 'update' + action: "create" | "delete" | "update" item: object } } @@ -20,7 +20,7 @@ interface EmitToAuthorizedUsersOptions extends EmitEventOptions { } type EmitToAuthorizedUsers = ( - options: EmitToAuthorizedUsersOptions + options: EmitToAuthorizedUsersOptions, ) => Promise interface EmitToMembersOptions extends EmitEventOptions { @@ -35,7 +35,7 @@ interface FastifyIo { emitToMembers: EmitToMembers } -declare module 'fastify' { +declare module "fastify" { export interface FastifyInstance { io: FastifyIo } @@ -46,8 +46,8 @@ export default fastifyPlugin( const instance = new SocketIoServer(fastify.server, options) instance.use( authorize({ - secret: JWT_ACCESS_SECRET - }) + secret: JWT_ACCESS_SECRET, + }), ) const emitToAuthorizedUsers: EmitToAuthorizedUsers = async (options) => { const { event, payload, isAuthorizedCallback } = options @@ -70,21 +70,21 @@ export default fastifyPlugin( payload, isAuthorizedCallback: async (userId) => { const memberCount = await prisma.member.count({ - where: { userId, guildId } + where: { userId, guildId }, }) return memberCount > 0 - } + }, }) } const io: FastifyIo = { instance, emitToAuthorizedUsers, - emitToMembers + emitToMembers, } - fastify.decorate('io', io) - fastify.addHook('onClose', (fastify) => { + fastify.decorate("io", io) + fastify.addHook("onClose", (fastify) => { fastify.io.instance.close() }) }, - { fastify: '4.x' } + { fastify: "4.x" }, ) diff --git a/src/tools/utils/OAuthStrategy.ts b/src/tools/utils/OAuthStrategy.ts index f6b87fe..40722f9 100644 --- a/src/tools/utils/OAuthStrategy.ts +++ b/src/tools/utils/OAuthStrategy.ts @@ -1,12 +1,12 @@ -import type { ResponseJWT } from './jwtToken.js' +import type { ResponseJWT } from "./jwtToken.js" import { expiresIn, generateAccessToken, - generateRefreshToken -} from '#src/tools/utils/jwtToken.js' -import prisma from '#src/tools/database/prisma.js' -import type { ProviderOAuth } from '#src/models/OAuth.js' -import type { UserRequest } from '#src/models/User.js' + generateRefreshToken, +} from "#src/tools/utils/jwtToken.js" +import prisma from "#src/tools/database/prisma.js" +import type { ProviderOAuth } from "#src/models/OAuth.js" +import type { UserRequest } from "#src/models/User.js" interface ProviderData { name: string @@ -14,40 +14,46 @@ interface ProviderData { } type ResponseCallbackAddStrategy = - | 'success' - | 'This account is already used by someone else' - | 'You are already using this account' + | "success" + | "This account is already used by someone else" + | "You are already using this account" export class OAuthStrategy { constructor(public provider: ProviderOAuth) {} async callbackAddStrategy( providerData: ProviderData, - userRequest: UserRequest + userRequest: UserRequest, ): Promise { const OAuthUser = await prisma.oAuth.findFirst({ - where: { providerId: providerData.id.toString(), provider: this.provider } + where: { + providerId: providerData.id.toString(), + provider: this.provider, + }, }) - let message: ResponseCallbackAddStrategy = 'success' + let message: ResponseCallbackAddStrategy = "success" if (OAuthUser == null) { await prisma.oAuth.create({ data: { provider: this.provider, providerId: providerData.id.toString(), - userId: userRequest.current.id - } + userId: userRequest.current.id, + }, }) } else if (OAuthUser.userId !== userRequest.current.id) { - message = 'This account is already used by someone else' + message = "This account is already used by someone else" } else { - message = 'You are already using this account' + message = "You are already using this account" } return message } async callbackSignin(providerData: ProviderData): Promise { const OAuthUser = await prisma.oAuth.findFirst({ - where: { providerId: providerData.id.toString(), provider: this.provider } + where: { + providerId: providerData.id.toString(), + provider: this.provider, + }, }) let userId: number = OAuthUser?.userId ?? 0 if (OAuthUser == null) { @@ -65,31 +71,31 @@ export class OAuthStrategy { const user = await prisma.user.create({ data: { name } }) await prisma.userSetting.create({ data: { - userId: user.id - } + userId: user.id, + }, }) userId = user.id await prisma.oAuth.create({ data: { provider: this.provider, providerId: providerData.id.toString(), - userId - } + userId, + }, }) } const accessToken = generateAccessToken({ currentStrategy: this.provider, - id: userId + id: userId, }) const refreshToken = await generateRefreshToken({ currentStrategy: this.provider, - id: userId + id: userId, }) return { accessToken, refreshToken, expiresIn, - type: 'Bearer' + type: "Bearer", } } } diff --git a/src/tools/utils/__test__/OAuthStrategy.test.ts b/src/tools/utils/__test__/OAuthStrategy.test.ts index ff5cda1..4ee5fb0 100644 --- a/src/tools/utils/__test__/OAuthStrategy.test.ts +++ b/src/tools/utils/__test__/OAuthStrategy.test.ts @@ -1,47 +1,47 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import sinon from 'sinon' +import sinon from "sinon" -import { userExample } from '#src/models/User.js' -import { userSettingsExample } from '#src/models/UserSettings.js' -import { OAuthStrategy } from '#src/tools/utils/OAuthStrategy.js' -import prisma from '#src/tools/database/prisma.js' -import { refreshTokenExample } from '#src/models/RefreshToken.js' +import { userExample } from "#src/models/User.js" +import { userSettingsExample } from "#src/models/UserSettings.js" +import { OAuthStrategy } from "#src/tools/utils/OAuthStrategy.js" +import prisma from "#src/tools/database/prisma.js" +import { refreshTokenExample } from "#src/models/RefreshToken.js" -const oauthStrategy = new OAuthStrategy('Discord') +const oauthStrategy = new OAuthStrategy("Discord") -await test('tools/utils/OAuthStrategy', async (t) => { - await t.test('callbackSignin', async (t) => { +await test("tools/utils/OAuthStrategy", async (t) => { + await t.test("callbackSignin", async (t) => { t.afterEach(() => { sinon.restore() }) - await t.test('should signup the user', async () => { - const name = 'Martin' - const id = '12345' - sinon.stub(prisma, 'user').value({ + await t.test("should signup the user", async () => { + const name = "Martin" + const id = "12345" + sinon.stub(prisma, "user").value({ count: async () => { return 0 }, create: async () => { return { ...userExample, - name + name, } - } + }, }) - sinon.stub(prisma, 'refreshToken').value({ + sinon.stub(prisma, "refreshToken").value({ create: async () => { return refreshTokenExample - } + }, }) - sinon.stub(prisma, 'userSetting').value({ + sinon.stub(prisma, "userSetting").value({ create: async () => { return userSettingsExample - } + }, }) - sinon.stub(prisma, 'oAuth').value({ + sinon.stub(prisma, "oAuth").value({ findFirst: async () => { return null }, @@ -49,60 +49,60 @@ await test('tools/utils/OAuthStrategy', async (t) => { return { id: 1, userId: userExample.id, - provider: 'Discord', + provider: "Discord", providerId: id, updatedAt: new Date(), - createdAt: new Date() + createdAt: new Date(), } - } + }, }) - const oAuthCreateSpy = sinon.spy(prisma.oAuth, 'create') - const oAuthFindFirstSpy = sinon.spy(prisma.oAuth, 'findFirst') - const userCountSpy = sinon.spy(prisma.user, 'count') - const userCreateSpy = sinon.spy(prisma.user, 'create') - const userSettingCreateSpy = sinon.spy(prisma.userSetting, 'create') + const oAuthCreateSpy = sinon.spy(prisma.oAuth, "create") + const oAuthFindFirstSpy = sinon.spy(prisma.oAuth, "findFirst") + const userCountSpy = sinon.spy(prisma.user, "count") + const userCreateSpy = sinon.spy(prisma.user, "create") + const userSettingCreateSpy = sinon.spy(prisma.userSetting, "create") await oauthStrategy.callbackSignin({ id, name }) assert.strictEqual( oAuthCreateSpy.calledWith({ data: { userId: userExample.id, - provider: 'Discord', - providerId: id - } + provider: "Discord", + providerId: id, + }, }), - true + true, ) assert.strictEqual( oAuthFindFirstSpy.calledWith({ where: { - provider: 'Discord', - providerId: id - } + provider: "Discord", + providerId: id, + }, }), - true + true, ) assert.strictEqual(userCountSpy.calledWith({ where: { name } }), true) assert.strictEqual(userCreateSpy.calledWith({ data: { name } }), true) assert.strictEqual( userSettingCreateSpy.calledWith({ data: { - userId: userExample.id - } + userId: userExample.id, + }, }), - true + true, ) }) }) - await t.test('callbackAddStrategy', async (t) => { + await t.test("callbackAddStrategy", async (t) => { t.afterEach(() => { sinon.restore() }) - await t.test('should add the strategy to the user', async () => { + await t.test("should add the strategy to the user", async () => { const name = userExample.name - const id = '12345' - sinon.stub(prisma, 'oAuth').value({ + const id = "12345" + sinon.stub(prisma, "oAuth").value({ findFirst: async () => { return null }, @@ -110,112 +110,120 @@ await test('tools/utils/OAuthStrategy', async (t) => { return { id: 1, userId: userExample.id, - provider: 'Discord', + provider: "Discord", providerId: id, updatedAt: new Date(), - createdAt: new Date() + createdAt: new Date(), } - } + }, }) - const oAuthCreateSpy = sinon.spy(prisma.oAuth, 'create') - const oAuthFindFirstSpy = sinon.spy(prisma.oAuth, 'findFirst') + const oAuthCreateSpy = sinon.spy(prisma.oAuth, "create") + const oAuthFindFirstSpy = sinon.spy(prisma.oAuth, "findFirst") const result = await oauthStrategy.callbackAddStrategy( { id, name }, - { accessToken: '123', current: userExample, currentStrategy: 'Local' } + { accessToken: "123", current: userExample, currentStrategy: "Local" }, ) - assert.strictEqual(result, 'success') + assert.strictEqual(result, "success") assert.strictEqual( oAuthCreateSpy.calledWith({ data: { userId: userExample.id, - provider: 'Discord', - providerId: id - } + provider: "Discord", + providerId: id, + }, }), - true + true, ) assert.strictEqual( oAuthFindFirstSpy.calledWith({ where: { - provider: 'Discord', - providerId: id - } + provider: "Discord", + providerId: id, + }, }), - true + true, ) }) await t.test( - 'should not add the strategy if the account of the provider is already used', + "should not add the strategy if the account of the provider is already used", async () => { const name = userExample.name - const id = '12345' - sinon.stub(prisma, 'oAuth').value({ + const id = "12345" + sinon.stub(prisma, "oAuth").value({ findFirst: async () => { return { id: 1, userId: 2, - provider: 'Discord', + provider: "Discord", providerId: id, updatedAt: new Date(), - createdAt: new Date() + createdAt: new Date(), } - } + }, }) - const oAuthFindFirstSpy = sinon.spy(prisma.oAuth, 'findFirst') + const oAuthFindFirstSpy = sinon.spy(prisma.oAuth, "findFirst") const result = await oauthStrategy.callbackAddStrategy( { id, name }, - { accessToken: '123', current: userExample, currentStrategy: 'Local' } + { + accessToken: "123", + current: userExample, + currentStrategy: "Local", + }, ) assert.strictEqual( result, - 'This account is already used by someone else' + "This account is already used by someone else", ) assert.strictEqual( oAuthFindFirstSpy.calledWith({ where: { - provider: 'Discord', - providerId: id - } + provider: "Discord", + providerId: id, + }, }), - true + true, ) - } + }, ) await t.test( - 'should not add the strategy if the user is already connected with it', + "should not add the strategy if the user is already connected with it", async () => { const name = userExample.name - const id = '12345' - sinon.stub(prisma, 'oAuth').value({ + const id = "12345" + sinon.stub(prisma, "oAuth").value({ findFirst: async () => { return { id: 1, userId: userExample.id, - provider: 'Discord', + provider: "Discord", providerId: id, updatedAt: new Date(), - createdAt: new Date() + createdAt: new Date(), } - } + }, }) - const oAuthFindFirstSpy = sinon.spy(prisma.oAuth, 'findFirst') + const oAuthFindFirstSpy = sinon.spy(prisma.oAuth, "findFirst") const result = await oauthStrategy.callbackAddStrategy( { id, name }, - { accessToken: '123', current: userExample, currentStrategy: 'Local' } + { + accessToken: "123", + current: userExample, + currentStrategy: "Local", + }, ) - assert.strictEqual(result, 'You are already using this account') + assert.strictEqual(result, "You are already using this account") assert.strictEqual( oAuthFindFirstSpy.calledWith({ where: { - provider: 'Discord', - providerId: id - } + provider: "Discord", + providerId: id, + }, }), - true + true, ) - } + }, ) }) }) diff --git a/src/tools/utils/__test__/buildQueryURL.test.ts b/src/tools/utils/__test__/buildQueryURL.test.ts index 78635c1..9ddc59f 100644 --- a/src/tools/utils/__test__/buildQueryURL.test.ts +++ b/src/tools/utils/__test__/buildQueryURL.test.ts @@ -1,26 +1,26 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import { buildQueryURL } from '../buildQueryURL.js' +import { buildQueryURL } from "../buildQueryURL.js" -await test('tools/utils/buildQueryUrl', async () => { +await test("tools/utils/buildQueryUrl", async () => { assert.strictEqual( - buildQueryURL('http://localhost:8080', { - test: 'query' + buildQueryURL("http://localhost:8080", { + test: "query", }), - 'http://localhost:8080/?test=query' + "http://localhost:8080/?test=query", ) assert.strictEqual( - buildQueryURL('http://localhost:8080/', { - test: 'query' + buildQueryURL("http://localhost:8080/", { + test: "query", }), - 'http://localhost:8080/?test=query' + "http://localhost:8080/?test=query", ) assert.strictEqual( - buildQueryURL('http://localhost:3000', { - test: 'query', - code: 'abc' + buildQueryURL("http://localhost:3000", { + test: "query", + code: "abc", }), - 'http://localhost:3000/?test=query&code=abc' + "http://localhost:3000/?test=query&code=abc", ) }) diff --git a/src/tools/utils/__test__/parseStringNullish.test.ts b/src/tools/utils/__test__/parseStringNullish.test.ts index 05aed0b..5bdd969 100644 --- a/src/tools/utils/__test__/parseStringNullish.test.ts +++ b/src/tools/utils/__test__/parseStringNullish.test.ts @@ -1,31 +1,31 @@ -import test from 'node:test' -import assert from 'node:assert/strict' +import test from "node:test" +import assert from "node:assert/strict" -import { parseStringNullish } from '../parseStringNullish.js' +import { parseStringNullish } from "../parseStringNullish.js" -const defaultString = 'defaultString' +const defaultString = "defaultString" -await test('tools/utils/parseStringNullish', async (t) => { +await test("tools/utils/parseStringNullish", async (t) => { await t.test( - 'returns `defaultString` if `string === undefined`', + "returns `defaultString` if `string === undefined`", async () => { assert.strictEqual( parseStringNullish(defaultString, undefined), - defaultString + defaultString, ) - } + }, ) - await t.test('returns `null` if `string === null`', async () => { + await t.test("returns `null` if `string === null`", async () => { assert.strictEqual(parseStringNullish(defaultString, null), null) }) - await t.test('returns `null` if `string.length === 0`', async () => { - assert.strictEqual(parseStringNullish(defaultString, ''), null) + await t.test("returns `null` if `string.length === 0`", async () => { + assert.strictEqual(parseStringNullish(defaultString, ""), null) }) - await t.test('returns `string` if `string.length > 0`', async () => { - const string = 'myString' + await t.test("returns `string` if `string.length > 0`", async () => { + const string = "myString" assert.strictEqual(parseStringNullish(defaultString, string), string) }) }) diff --git a/src/tools/utils/buildQueryURL.ts b/src/tools/utils/buildQueryURL.ts index a413e57..90fd531 100644 --- a/src/tools/utils/buildQueryURL.ts +++ b/src/tools/utils/buildQueryURL.ts @@ -1,4 +1,4 @@ -import { URL } from 'node:url' +import { URL } from "node:url" export interface ObjectAny { [key: string]: any @@ -6,7 +6,7 @@ export interface ObjectAny { export const buildQueryURL = ( baseURL: string, - queryObject: ObjectAny + queryObject: ObjectAny, ): string => { const url = new URL(baseURL) for (const [query, value] of Object.entries(queryObject)) { diff --git a/src/tools/utils/jwtToken.ts b/src/tools/utils/jwtToken.ts index eb9d9f8..a8beab1 100644 --- a/src/tools/utils/jwtToken.ts +++ b/src/tools/utils/jwtToken.ts @@ -1,22 +1,22 @@ -import { randomUUID } from 'node:crypto' +import { randomUUID } from "node:crypto" -import { Type } from '@sinclair/typebox' -import jwt from 'jsonwebtoken' -import ms from 'ms' +import { Type } from "@sinclair/typebox" +import jwt from "jsonwebtoken" +import ms from "ms" -import prisma from '#src/tools/database/prisma.js' -import type { UserJWT } from '#src/models/User.js' +import prisma from "#src/tools/database/prisma.js" +import type { UserJWT } from "#src/models/User.js" import { JWT_ACCESS_EXPIRES_IN, JWT_ACCESS_SECRET, - JWT_REFRESH_SECRET -} from '#src/tools/configurations.js' + JWT_REFRESH_SECRET, +} from "#src/tools/configurations.js" export interface ResponseJWT { accessToken: string refreshToken?: string expiresIn: number - type: 'Bearer' + type: "Bearer" } export const jwtSchema = { @@ -24,9 +24,9 @@ export const jwtSchema = { refreshToken: Type.String(), expiresIn: Type.Integer({ description: - 'expiresIn is how long, in milliseconds, until the accessToken expires' + "expiresIn is how long, in milliseconds, until the accessToken expires", }), - type: Type.Literal('Bearer') + type: Type.Literal("Bearer"), } export const expiresIn = ms(JWT_ACCESS_EXPIRES_IN) @@ -40,12 +40,12 @@ export const generateRefreshToken = async (user: UserJWT): Promise => { const refreshToken = jwt.sign( { ...user, - tokenUUID + tokenUUID, }, - JWT_REFRESH_SECRET + JWT_REFRESH_SECRET, ) await prisma.refreshToken.create({ - data: { token: tokenUUID, userId: user.id } + data: { token: tokenUUID, userId: user.id }, }) return refreshToken } diff --git a/src/tools/utils/parseStringNullish.ts b/src/tools/utils/parseStringNullish.ts index bf4ece0..b9d1071 100644 --- a/src/tools/utils/parseStringNullish.ts +++ b/src/tools/utils/parseStringNullish.ts @@ -10,7 +10,7 @@ */ export const parseStringNullish = ( defaultString: string | null, - string?: string | null + string?: string | null, ): string | null => { if (string === undefined) { return defaultString diff --git a/src/tools/utils/uploadFile.ts b/src/tools/utils/uploadFile.ts index 99f084e..c7be105 100644 --- a/src/tools/utils/uploadFile.ts +++ b/src/tools/utils/uploadFile.ts @@ -1,27 +1,27 @@ -import fs from 'node:fs' +import fs from "node:fs" -import axios from 'axios' -import FormData from 'form-data' -import type { FastifyInstance, FastifyRequest } from 'fastify' -import type { SavedMultipartFile } from '@fastify/multipart' +import axios from "axios" +import FormData from "form-data" +import type { FastifyInstance, FastifyRequest } from "fastify" +import type { SavedMultipartFile } from "@fastify/multipart" import { FILE_UPLOADS_API_KEY, - FILE_UPLOADS_API_URL -} from '#src/tools/configurations.js' + FILE_UPLOADS_API_URL, +} from "#src/tools/configurations.js" export const fileUploadAPI = axios.create({ baseURL: FILE_UPLOADS_API_URL, headers: { - 'Content-Type': 'application/json' - } + "Content-Type": "application/json", + }, }) /** in megabytes */ export const MAXIMUM_FILE_SIZE = 100 export interface UploadFileOptions { - folderInUploadsFolder: 'guilds' | 'messages' | 'users' + folderInUploadsFolder: "guilds" | "messages" | "users" request: FastifyRequest fastify: FastifyInstance } @@ -32,7 +32,7 @@ export interface UploadFileResult { } export const uploadFile = async ( - options: UploadFileOptions + options: UploadFileOptions, ): Promise => { const { fastify, request, folderInUploadsFolder } = options let files: SavedMultipartFile[] = [] @@ -40,37 +40,37 @@ export const uploadFile = async ( files = await request.saveRequestFiles({ limits: { files: 1, - fileSize: MAXIMUM_FILE_SIZE * 1024 * 1024 - } + fileSize: MAXIMUM_FILE_SIZE * 1024 * 1024, + }, }) } catch (error) { console.error(error) throw fastify.httpErrors.requestHeaderFieldsTooLarge( - `File should be less than ${MAXIMUM_FILE_SIZE}mb.` + `File should be less than ${MAXIMUM_FILE_SIZE}mb.`, ) } const file = files[0] if (files.length !== 1 || file == null) { - throw fastify.httpErrors.badRequest('You must upload at most one file.') + throw fastify.httpErrors.badRequest("You must upload at most one file.") } const formData = new FormData() - formData.append('file', fs.createReadStream(file.filepath)) + formData.append("file", fs.createReadStream(file.filepath)) try { const response = await fileUploadAPI.post( `/uploads/${folderInUploadsFolder}`, formData, { headers: { - 'X-API-Key': FILE_UPLOADS_API_KEY, - ...formData.getHeaders() - } - } + "X-API-Key": FILE_UPLOADS_API_KEY, + ...formData.getHeaders(), + }, + }, ) return { pathToStoreInDatabase: response.data, mimetype: file.mimetype } } catch (error: any) { throw fastify.httpErrors.createError( error.response.data.statusCode, - error.response.data.message + error.response.data.message, ) } }