From e34ad4649556b52f76bfad1b399b4558258d6fa0 Mon Sep 17 00:00:00 2001 From: phucvinh57 Date: Mon, 8 Jan 2024 13:18:29 +0700 Subject: [PATCH 1/5] chore: change dto folder structure --- .barrelsby.json | 1 + src/dtos/common/index.ts | 5 +++++ src/dtos/{common.dto.ts => common/objectId.dto.ts} | 0 src/dtos/in/auth.dto.ts | 2 -- tsconfig.json | 2 +- 5 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 src/dtos/common/index.ts rename src/dtos/{common.dto.ts => common/objectId.dto.ts} (100%) diff --git a/.barrelsby.json b/.barrelsby.json index af241ec..0fd83fe 100644 --- a/.barrelsby.json +++ b/.barrelsby.json @@ -4,6 +4,7 @@ "./src/constants", "./src/dtos/in", "./src/dtos/out", + "./src/dtos/common", "./src/configs", "./src/hooks", "./src/interfaces", diff --git a/src/dtos/common/index.ts b/src/dtos/common/index.ts new file mode 100644 index 0000000..ea98105 --- /dev/null +++ b/src/dtos/common/index.ts @@ -0,0 +1,5 @@ +/** + * @file Automatically generated by barrelsby. + */ + +export * from './objectId.dto'; diff --git a/src/dtos/common.dto.ts b/src/dtos/common/objectId.dto.ts similarity index 100% rename from src/dtos/common.dto.ts rename to src/dtos/common/objectId.dto.ts diff --git a/src/dtos/in/auth.dto.ts b/src/dtos/in/auth.dto.ts index ac15331..dd6a04f 100644 --- a/src/dtos/in/auth.dto.ts +++ b/src/dtos/in/auth.dto.ts @@ -1,8 +1,6 @@ import { MIN_EMAIL_LENGTH, MIN_PASSWORD_LENGTH } from '@constants'; import { Static, Type } from '@sinclair/typebox'; -// See https://github.com/sinclairzx81/typebox - export const AuthInputDto = Type.Object({ email: Type.String({ minLength: MIN_EMAIL_LENGTH }), password: Type.String({ minLength: MIN_PASSWORD_LENGTH }) diff --git a/tsconfig.json b/tsconfig.json index 1004d00..ff9c64f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -30,7 +30,7 @@ "@services": ["src/services"], "@dtos/in": ["src/dtos/in"], "@dtos/out": ["src/dtos/out"], - "@dtos/common": ["src/dtos/common.dto"], + "@dtos/common": ["src/dtos/common"], "@configs": ["src/configs"], "@configs/*": ["src/configs/*"], "@repositories": ["src/repositories"], From ce2dbc2f593bdc38023cc43e6ae319fddebac253 Mon Sep 17 00:00:00 2001 From: phucvinh57 Date: Mon, 8 Jan 2024 15:10:30 +0700 Subject: [PATCH 2/5] test: reconfig jest & add additional tests --- README.md | 10 +- docker-compose.dev.yml | 3 +- jest.config.js | 82 ++++++-- package.json | 5 +- src/Server.ts | 6 +- src/configs/logger/discord.ts | 10 +- src/configs/swagger.ts | 11 +- src/handlers/__specs__/auth.spec.ts | 20 ++ src/handlers/auth.handler.ts | 22 +-- src/setupTest.ts | 21 +++ src/types/log.d.ts | 2 +- src/types/serverConfig.d.ts | 2 +- src/{ => utils}/__tests__/sample.test.ts | 0 tsconfig.json | 2 +- yarn.lock | 227 ++++++++++++++++++++++- 15 files changed, 365 insertions(+), 58 deletions(-) create mode 100644 src/handlers/__specs__/auth.spec.ts create mode 100644 src/setupTest.ts rename src/{ => utils}/__tests__/sample.test.ts (100%) diff --git a/README.md b/README.md index 16d043f..b8b7b1f 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ Using [fastify](https://www.fastify.io), this template includes: -- API Docs: `SwaggerUI` -- Input validation: `fluent-json-schema` -- ORM & migration tools: `Prisma` +- API Docs: `Swagger UI` +- Input definition: [`Typebox`](https://github.com/sinclairzx81/typebox) +- ORM: `Prisma` - Deployment: - Dockerfile & docker-compose files - Script CI/CD in `.github/workflows` @@ -53,10 +53,10 @@ Note: Fill in `.env` file (use template from `.env.example`) before starts. ┣ 📂constants # Constants and enums go here ┣ 📂dtos # Schema for input (from requests) & output (from responses) ┃ ┣ 📂in - ┃ ┗ 📂out + ┃ ┣ 📂out + ┃ ┗ 📂common # Reusable schemas ┣ 📂handlers # Handlers, which are responsible for handling core business logic ┣ 📂interfaces # Interfaces - ┣ 📂middlewares # Middlewares such as logging or verifying tokens ┣ 📂plugins # Plugin, in charge of organizing api routings & registering middleware ┣ 📂repositories # Datasource configurations & connections. Could have more than one datasource. ┣ 📂services # 3rd-party services or business logic services diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index d063ae3..141f489 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -2,13 +2,12 @@ version: "3.5" services: postgres: image: postgres:15 - container_name: postgres environment: POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_DB: ${POSTGRES_DB} ports: - - 5432:5432 + - 5433:5432 volumes: - postgres_db:/var/lib/postgresql/data restart: always diff --git a/jest.config.js b/jest.config.js index 7f36ba7..faf4530 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,9 +1,14 @@ const { pathsToModuleNameMapper } = require('ts-jest'); // which contains the path mapping (ie the `compilerOptions.paths` option): const { compilerOptions } = require('./tsconfig'); +const fs = require('fs'); +const path = require('path'); -/** @type {import('@jest/types').Config.ProjectConfig} */ -const generalConfig = { +/** + * @type {import('@jest/types').Config.ProjectConfig} + * Config used between test projects. + */ +const baseConfig = { // Automatically clear mock calls and instances between every test clearMocks: true, coveragePathIgnorePatterns: ['index.ts', '/node_modules/'], @@ -14,12 +19,65 @@ const generalConfig = { transform: { '\\.(ts)$': 'ts-jest' }, - testPathIgnorePatterns: ['/node_modules/', '/dist/'], transformIgnorePatterns: ['node_modules'], moduleDirectories: ['node_modules', 'src'], roots: [''], modulePaths: [compilerOptions.baseUrl], // <-- This will be set to 'baseUrl' value - moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths /*, { prefix: '/' } */) + moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths /*, { prefix: '/' } */), + modulePathIgnorePatterns: ['/dist/'] +}; + +/** + * For easier debugging, create a test label for each test file. + * @param {string} baseDir + * @returns + */ +function createTestLabels(baseDir) { + // Read the contents of the directory synchronously + const filePaths = fs.readdirSync(baseDir).map((f) => path.join(baseDir, f)); + const results = []; + + while (filePaths.length > 0) { + const filePath = filePaths.pop(); + const fileStat = fs.statSync(filePath); // Get the file's stats + + if (fileStat.isDirectory()) { + const subFiles = fs.readdirSync(filePath); + filePaths.push(...subFiles.map((f) => path.join(filePath, f))); + continue; + } + + // Check if the file is test file + let type; + if (filePath.endsWith('.spec.ts')) type = 'spec'; + else if (filePath.endsWith('.test.ts')) type = 'test'; + else continue; + + const fileName = path.basename(filePath); + const label = fileName.slice(0, -8); + + results.push({ label, type }); + } + + return results.map((r) => ({ + ...baseConfig, + displayName: r.label, + testMatch: [`**/${r.label}.${r.type}.ts`], + setupFilesAfterEnv: r.type === 'spec' ? ['/src/setupTest.ts'] : undefined + })); +} + +const unitTestProject = { + ...baseConfig, + displayName: 'unit', + testMatch: ['**/*.test.ts'] +}; + +const integrationTestProject = { + ...baseConfig, + displayName: 'integration', + testMatch: ['**/*.spec.ts'], + setupFilesAfterEnv: ['/src/setupTest.ts'] }; /** @type {import('jest').Config} */ @@ -32,19 +90,9 @@ module.exports = { // The directory where Jest should output its coverage files coverageDirectory: 'coverage', detectOpenHandles: true, - // Wait at most 5 seconds util all promises are resolved before exiting the test openHandlesTimeout: 5000, - projects: [ - { - ...generalConfig, - displayName: 'unit-tests', - testMatch: ['**/*.test.ts'] - }, - { - ...generalConfig, - displayName: 'integration-tests', - testMatch: ['**/*.spec.ts'] - } - ] + // Exit the test suite immediately upon the first failing test + bail: true, + projects: [unitTestProject, integrationTestProject, ...createTestLabels(compilerOptions.sourceRoot)] }; diff --git a/package.json b/package.json index 3911153..4b2397b 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "build": "tsc --project tsconfig.compile.json && tsc-alias", "barrels": "barrelsby --config .barrelsby.json -q", "start": "yarn barrels && cross-env NODE_ENV=development tsnd --ignore-watch node_modules --respawn --transpile-only --max-old-space-size=2048 -r tsconfig-paths/register src/index.ts", - "test": "jest --passWithNoTests", + "test": "jest --forceExit --selectProjects", "db:migrate": "npx prisma migrate dev", "db:reset": "npx prisma migrate reset --force", "db:deploy": "npx prisma migrate deploy", @@ -42,10 +42,12 @@ "prisma": "^5.1.1" }, "devDependencies": { + "@faker-js/faker": "^8.3.1", "@types/bcrypt": "^5.0.0", "@types/jest": "^29.5.3", "@types/jsonwebtoken": "^9.0.2", "@types/node": "^20.4.8", + "@types/supertest": "^6.0.2", "@typescript-eslint/eslint-plugin": "^6.2.1", "@typescript-eslint/parser": "^6.2.1", "barrelsby": "^2.8.0", @@ -59,6 +61,7 @@ "lint-staged": "^13.2.3", "pino-pretty": "^10.2.0", "prettier": "^3.0.1", + "supertest": "^6.3.3", "ts-jest": "^29.1.1", "ts-node-dev": "^2.0.0", "tsc-alias": "^1.8.7", diff --git a/src/Server.ts b/src/Server.ts index 7bdb544..1bae053 100644 --- a/src/Server.ts +++ b/src/Server.ts @@ -19,8 +19,8 @@ export function createServer(config: ServerConfig): FastifyInstance { hook: 'onRequest' } as FastifyCookieOptions); - // Swagger on production will be turned off in the future - if (envs.isDev) { + // Swagger on production should be turned off + if (!envs.isProd) { app.register(import('@fastify/swagger'), swaggerConfig); app.register(import('@fastify/swagger-ui'), swaggerUIConfig); } @@ -42,7 +42,7 @@ export function createServer(config: ServerConfig): FastifyInstance { await app.ready(); if (!envs.isProd) { app.swagger({ yaml: true }); - app.log.info(`Swagger documentation is on http://${config.host}:${config.port}/docs`); + app.log.info(`Swagger documentation is on ${app.server.address()}/docs`); } }; diff --git a/src/configs/logger/discord.ts b/src/configs/logger/discord.ts index e20616c..1ae74ba 100644 --- a/src/configs/logger/discord.ts +++ b/src/configs/logger/discord.ts @@ -17,10 +17,12 @@ export default function (options: DiscordLogOptions) { for await (const obj of source) { const level = obj.level; let levelName: LogLevel; - if (level >= 60) levelName = 'FATAL'; - else if (level >= 50) levelName = 'ERROR'; - else if (level >= 40) levelName = 'WARN'; - else levelName = 'INFO'; + if (level === 60) levelName = 'FATAL'; + else if (level === 50) levelName = 'ERROR'; + else if (level === 40) levelName = 'WARN'; + else if (level === 30) levelName = 'INFO'; + else if (level === 20) levelName = 'DEBUG'; + else levelName = 'TRACE'; if (ignoreFields) ignoreFields.forEach((field) => delete obj[field]); const time = moment.tz('Asia/Ho_Chi_Minh').format('HH:mm:ss DD/MM/YYYY'); diff --git a/src/configs/swagger.ts b/src/configs/swagger.ts index 7e5030f..077e6b7 100644 --- a/src/configs/swagger.ts +++ b/src/configs/swagger.ts @@ -1,14 +1,13 @@ import { FastifyDynamicSwaggerOptions } from '@fastify/swagger'; import { FastifySwaggerUiOptions } from '@fastify/swagger-ui'; -import pkg from '#package.json'; export const swaggerConfig: FastifyDynamicSwaggerOptions = { openapi: { info: { - title: pkg.name, - version: pkg.version, + title: '', + version: '1.0.0', license: { name: 'ISC' }, - summary: pkg.description + summary: '' }, externalDocs: { url: 'https://swagger.io', @@ -19,8 +18,6 @@ export const swaggerConfig: FastifyDynamicSwaggerOptions = { export const swaggerUIConfig: FastifySwaggerUiOptions = { routePrefix: '/docs', - uiConfig: { - deepLinking: false - }, + uiConfig: { deepLinking: false }, staticCSP: false }; diff --git a/src/handlers/__specs__/auth.spec.ts b/src/handlers/__specs__/auth.spec.ts new file mode 100644 index 0000000..a1fe611 --- /dev/null +++ b/src/handlers/__specs__/auth.spec.ts @@ -0,0 +1,20 @@ +import { supertest } from '@testUtils'; +import { faker } from '@faker-js/faker'; + +describe('Test auth handler', () => { + const account = { + email: faker.internet.email().toLowerCase(), + password: faker.internet.password() + '1@A_' + }; + test('Sign up', async () => { + const { body } = await supertest.post('/auth/signup').send(account).expect(200); + expect(body).toHaveProperty('id'); + expect(body.email).toBe(account.email); + }); + + test('Login', async () => { + const { body } = await supertest.post('/auth/login').send(account).expect(200); + expect(body).toHaveProperty('id'); + expect(body.email).toBe(account.email); + }); +}); diff --git a/src/handlers/auth.handler.ts b/src/handlers/auth.handler.ts index 53e1465..ea35414 100644 --- a/src/handlers/auth.handler.ts +++ b/src/handlers/auth.handler.ts @@ -1,13 +1,11 @@ import { compare, hash } from 'bcrypt'; import { prisma } from '@repositories'; -import { cookieOptions, DUPLICATED_EMAIL, LOGIN_FAIL, SALT_ROUNDS, USER_NOT_FOUND } from '@constants'; +import { cookieOptions, LOGIN_FAIL, SALT_ROUNDS, USER_NOT_FOUND } from '@constants'; import jwt from 'jsonwebtoken'; import { envs } from '@configs'; -import { User } from '@prisma/client'; import { AuthInputDto } from '@dtos/in'; import { AuthResultDto } from '@dtos/out'; import { Handler } from '@interfaces'; -import { logger } from '@utils'; const login: Handler = async (req, res) => { const user = await prisma.user.findUnique({ @@ -34,18 +32,12 @@ const login: Handler = async (req, res) = const signup: Handler = async (req, res) => { const hashPassword = await hash(req.body.password, SALT_ROUNDS); - let user: User; - try { - user = await prisma.user.create({ - data: { - email: req.body.email, - password: hashPassword - } - }); - } catch (err) { - logger.info(err); - return res.badRequest(DUPLICATED_EMAIL); - } + const user = await prisma.user.create({ + data: { + email: req.body.email, + password: hashPassword + } + }); const userToken = jwt.sign({ userId: user.id }, envs.JWT_SECRET); res.setCookie('token', userToken, cookieOptions); diff --git a/src/setupTest.ts b/src/setupTest.ts new file mode 100644 index 0000000..c8a884a --- /dev/null +++ b/src/setupTest.ts @@ -0,0 +1,21 @@ +import { FastifyInstance } from 'fastify'; +import { createServer } from './Server'; +import SuperTest from 'supertest'; + +let app: FastifyInstance; +let supertest: ReturnType; + +beforeAll(async () => { + app = createServer({ host: 'localhost' }); + await app.start(); + supertest = SuperTest(app.server); + + jest.useFakeTimers({ doNotFake: ['nextTick', 'Date', 'hrtime'] }); +}); + +afterAll(async () => { + await app.shutdown(); + jest.clearAllTimers(); +}); + +export { supertest }; diff --git a/src/types/log.d.ts b/src/types/log.d.ts index bc5fbcd..ac38438 100644 --- a/src/types/log.d.ts +++ b/src/types/log.d.ts @@ -1,4 +1,4 @@ -type LogLevel = 'INFO' | 'WARN' | 'DEBUG' | 'ERROR' | 'FATAL'; +type LogLevel = 'TRACE' | 'INFO' | 'WARN' | 'DEBUG' | 'ERROR' | 'FATAL'; type DiscordLogOptions = { webhookUrl: string; diff --git a/src/types/serverConfig.d.ts b/src/types/serverConfig.d.ts index 870bb48..365eda1 100644 --- a/src/types/serverConfig.d.ts +++ b/src/types/serverConfig.d.ts @@ -1,4 +1,4 @@ type ServerConfig = { host: string; - port: number; + port?: number; }; diff --git a/src/__tests__/sample.test.ts b/src/utils/__tests__/sample.test.ts similarity index 100% rename from src/__tests__/sample.test.ts rename to src/utils/__tests__/sample.test.ts diff --git a/tsconfig.json b/tsconfig.json index ff9c64f..b35397c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -38,7 +38,7 @@ "@hooks": ["src/hooks"], "@routes": ["src/routes"], "@interfaces": ["src/interfaces"], - "#package.json": ["./package.json"] + "@testUtils": ["src/setupTest"] } }, "include": ["src", "**/*.js", "**/*.ts"], diff --git a/yarn.lock b/yarn.lock index 3afdbec..7e28840 100644 --- a/yarn.lock +++ b/yarn.lock @@ -405,6 +405,11 @@ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.46.0.tgz#3f7802972e8b6fe3f88ed1aabc74ec596c456db6" integrity sha512-a8TLtmPi8xzPkCbp/OGFUo5yhRkHM2Ko9kOWP4znJr0WAhWyThaw3PnwX4vOTWOAMsV2uRt32PPDcEz63esSaA== +"@faker-js/faker@^8.3.1": + version "8.3.1" + resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-8.3.1.tgz#7753df0cb88d7649becf984a96dd1bd0a26f43e3" + integrity sha512-FdgpFxY6V6rLZE9mmIBb9hM0xpfvQOSNOLnzolzKwsE1DH+gC7lEKV1p1IbR0lAYyvYd5a4u3qWJzowUkw1bIw== + "@fastify/accept-negotiator@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@fastify/accept-negotiator/-/accept-negotiator-1.1.0.tgz#c1c66b3b771c09742a54dd5bc87c582f6b0630ff" @@ -961,6 +966,11 @@ dependencies: "@types/node" "*" +"@types/cookiejar@^2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@types/cookiejar/-/cookiejar-2.1.5.tgz#14a3e83fa641beb169a2dd8422d91c3c345a9a78" + integrity sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q== + "@types/graceful-fs@^4.1.3": version "4.1.6" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.6.tgz#e14b2576a1c25026b7f02ede1de3b84c3a1efeae" @@ -1007,6 +1017,11 @@ dependencies: "@types/node" "*" +"@types/methods@^1.1.4": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@types/methods/-/methods-1.1.4.tgz#d3b7ac30ac47c91054ea951ce9eed07b1051e547" + integrity sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ== + "@types/node@*", "@types/node@^20.4.8": version "20.4.8" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.4.8.tgz#b5dda19adaa473a9bf0ab5cbd8f30ec7d43f5c85" @@ -1032,6 +1047,23 @@ resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ== +"@types/superagent@^8.1.0": + version "8.1.1" + resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-8.1.1.tgz#dbc620c5df3770b0c3092f947d6d5e808adae2bc" + integrity sha512-YQyEXA4PgCl7EVOoSAS3o0fyPFU6erv5mMixztQYe1bqbWmmn8c+IrqoxjQeZe4MgwXikgcaZPiI/DsbmOVlzA== + dependencies: + "@types/cookiejar" "^2.1.5" + "@types/methods" "^1.1.4" + "@types/node" "*" + +"@types/supertest@^6.0.2": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@types/supertest/-/supertest-6.0.2.tgz#2af1c466456aaf82c7c6106c6b5cbd73a5e86588" + integrity sha512-137ypx2lk/wTQbW6An6safu9hXmajAifU/s7szAHLN/FeIm5w7yR0Wkl9fdJMRSHwOn4HLAI0DaB2TOORuhPDg== + dependencies: + "@types/methods" "^1.1.4" + "@types/superagent" "^8.1.0" + "@types/ws@8.5.9": version "8.5.9" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.9.tgz#384c489f99c83225a53f01ebc3eddf3b8e202a8c" @@ -1312,11 +1344,21 @@ array-union@^2.1.0: resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== +asap@^2.0.0: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== + astral-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + atomic-sleep@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" @@ -1487,6 +1529,15 @@ buffer@^6.0.3: base64-js "^1.3.1" ieee754 "^1.2.1" +call-bind@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" + integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== + dependencies: + function-bind "^1.1.2" + get-intrinsic "^1.2.1" + set-function-length "^1.1.1" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -1645,6 +1696,13 @@ colorette@^2.0.19, colorette@^2.0.7: resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + commander@^10.0.0: version "10.0.1" resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" @@ -1655,6 +1713,11 @@ commander@^9.0.0: resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== +component-emitter@^1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.1.tgz#ef1d5796f7d93f135ee6fb684340b26403c97d17" + integrity sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ== + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -1687,6 +1750,11 @@ cookie@^0.5.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== +cookiejar@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.4.tgz#ee669c1fea2cf42dc31585469d193fef0d65771b" + integrity sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw== + create-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" @@ -1735,6 +1803,20 @@ deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== +define-data-property@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" + integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ== + dependencies: + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + delegates@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" @@ -1755,6 +1837,14 @@ detect-newline@^3.0.0: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== +dezalgo@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.4.tgz#751235260469084c132157dfa857f386d4c33d81" + integrity sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig== + dependencies: + asap "^2.0.0" + wrappy "1" + diff-sequences@^29.4.3: version "29.4.3" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" @@ -2247,6 +2337,25 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +formidable@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/formidable/-/formidable-2.1.2.tgz#fa973a2bec150e4ce7cac15589d7a25fc30ebd89" + integrity sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g== + dependencies: + dezalgo "^1.0.4" + hexoid "^1.0.0" + once "^1.4.0" + qs "^6.11.0" + forwarded@0.2.0, forwarded@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -2274,6 +2383,11 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + gauge@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" @@ -2299,6 +2413,16 @@ get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-intrinsic@^1.0.2, get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" + integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== + dependencies: + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" @@ -2370,6 +2494,13 @@ globby@^11.0.4, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + graceful-fs@^4.1.2, graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" @@ -2390,6 +2521,23 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-property-descriptors@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340" + integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg== + dependencies: + get-intrinsic "^1.2.2" + +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + +has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + has-unicode@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" @@ -2402,6 +2550,13 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +hasown@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" + integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== + dependencies: + function-bind "^1.1.2" + helmet@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/helmet/-/helmet-7.0.0.tgz#ac3011ba82fa2467f58075afa58a49427ba6212d" @@ -2415,6 +2570,11 @@ help-me@^4.0.1: glob "^8.0.0" readable-stream "^3.6.0" +hexoid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hexoid/-/hexoid-1.0.0.tgz#ad10c6573fb907de23d9ec63a711267d9dc9bc18" + integrity sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g== + html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" @@ -3274,6 +3434,11 @@ merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== +methods@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== + micromatch@^4.0.4, micromatch@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" @@ -3287,13 +3452,18 @@ mime-db@1.52.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@~2.1.24: +mime-types@^2.1.12, mime-types@~2.1.24: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: mime-db "1.52.0" +mime@2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" + integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== + mime@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7" @@ -3465,6 +3635,11 @@ object-inspect@^1.12.3: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== +object-inspect@^1.9.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== + obliterator@^2.0.1: version "2.0.4" resolved "https://registry.yarnpkg.com/obliterator/-/obliterator-2.0.4.tgz#fa650e019b2d075d745e44f1effeb13a2adbe816" @@ -3799,6 +3974,13 @@ pure-rand@^6.0.0: resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.2.tgz#a9c2ddcae9b68d736a8163036f088a2781c8b306" integrity sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ== +qs@^6.11.0: + version "6.11.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" + integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== + dependencies: + side-channel "^1.0.4" + queue-lit@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/queue-lit/-/queue-lit-1.5.0.tgz#8197fdafda1edd615c8a0fc14c48353626e5160a" @@ -3987,6 +4169,16 @@ set-cookie-parser@^2.4.1: resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz#131921e50f62ff1a66a461d7d62d7b21d5d15a51" integrity sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ== +set-function-length@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.1.1.tgz#4bc39fafb0307224a33e106a7d35ca1218d659ed" + integrity sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ== + dependencies: + define-data-property "^1.1.1" + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + setprototypeof@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" @@ -4004,6 +4196,15 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" @@ -4186,6 +4387,30 @@ strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +superagent@^8.0.5: + version "8.1.2" + resolved "https://registry.yarnpkg.com/superagent/-/superagent-8.1.2.tgz#03cb7da3ec8b32472c9d20f6c2a57c7f3765f30b" + integrity sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA== + dependencies: + component-emitter "^1.3.0" + cookiejar "^2.1.4" + debug "^4.3.4" + fast-safe-stringify "^2.1.1" + form-data "^4.0.0" + formidable "^2.1.2" + methods "^1.1.2" + mime "2.6.0" + qs "^6.11.0" + semver "^7.3.8" + +supertest@^6.3.3: + version "6.3.3" + resolved "https://registry.yarnpkg.com/supertest/-/supertest-6.3.3.tgz#42f4da199fee656106fd422c094cf6c9578141db" + integrity sha512-EMCG6G8gDu5qEqRQ3JjjPs6+FYT1a7Hv5ApHvtSghmOFJYtsU5S+pSb6Y2EUeCEY3CmEL3mmQ8YWlPOzQomabA== + dependencies: + methods "^1.1.2" + superagent "^8.0.5" + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" From c3ce80327eea77390c92cd810531d45e99adb2df Mon Sep 17 00:00:00 2001 From: phucvinh57 Date: Mon, 8 Jan 2024 15:25:28 +0700 Subject: [PATCH 3/5] docs: update README.md --- README.md | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index b8b7b1f..e883e8d 100644 --- a/README.md +++ b/README.md @@ -16,14 +16,14 @@ Using [fastify](https://www.fastify.io), this template includes: For applying conventional commits, refer [commitizen](https://github.com/commitizen/cz-cli). -## Prerequisites +## 1. Prerequisites - `docker` v20.10.22 - `docker-compose` v1.29.2 - `node` v18.13.0 - `npm` 8.19.3 -## Commands +## 2. Commands Note: Fill in `.env` file (use template from `.env.example`) before starts. @@ -40,8 +40,9 @@ Note: Fill in `.env` file (use template from `.env.example`) before starts. - `yarn start:docker`: Run `docker-compose.dev.yml` file to set up local database - `yarn clean:docker`: Remove local database instance include its data. - `yarn clean:git`: Clean local branches which were merged on remote +- `yarn test `: Run test with label ``. For example: `yarn test auth` will run all tests in `auth.test.ts` or `auth.spec.ts` file. -## Project structure +## 3. Project structure ```py 📦prisma @@ -66,7 +67,25 @@ Note: Fill in `.env` file (use template from `.env.example`) before starts. ┗ 📜index.ts # Program entry ``` -## Project configurations +## 4. Appendix + +This section contains some useful information for development. + +### Testing + +These are some best practices for testing and overall quality: + +1. At the very least, write API (component) testing. +2. Include 3 parts in each test name +3. Structure tests by the AAA pattern +4. Ensure Node version is unified +5. Avoid global test fixtures and seeds, add data per-test +6. Check your test coverage, it helps to identify wrong test patterns +7. Refactor regularly using static analysis tools +8. Mock responses of external HTTP services +9. Test your middlewares in isolation +10. Specify a port in production, randomize in testing +11. Test the five possible outcomes ### Code linting & formating @@ -132,15 +151,13 @@ yarn barrels To avoid using many `..` in relative path, config path alias in `tsconfig.json`. See the guideline [here](https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping). -## Git working culture +### Git working culture - For every updates, DO NOT push directly to `master` branch. Create a new branch, commit, publish branch and create a pull request (PR) instead. - A branch should have prefix `feat/` for a feature update, prefix `hotf/` for a hotfix, `improv/` for an improvement ... - A PR should be small enough to review. To split a large PR, use [stacked PRs](https://blog.logrocket.com/using-stacked-pull-requests-in-github/). -## Helpful resources - -### Prisma +### About Prisma ORM - [Database schema](https://www.prisma.io/docs/concepts/components/prisma-schema) - [Type mapping Prisma & PostgreSQL](https://www.prisma.io/docs/concepts/database-connectors/postgresql#type-mapping-between-postgresql-to-prisma-schema) From b293623ee428953a8d25aa9759f94145017a042f Mon Sep 17 00:00:00 2001 From: phucvinh57 Date: Mon, 8 Jan 2024 15:36:35 +0700 Subject: [PATCH 4/5] chore: bump all package version --- package.json | 60 +- src/handlers/error.handler.ts | 3 +- yarn.lock | 1727 +++++++++++++++++++-------------- 3 files changed, 1005 insertions(+), 785 deletions(-) diff --git a/package.json b/package.json index 4b2397b..1fe9d0f 100644 --- a/package.json +++ b/package.json @@ -24,49 +24,49 @@ "clean:git": "git branch --merged >/tmp/merged-branches && nano /tmp/merged-branches && xargs git branch -D Date: Mon, 8 Jan 2024 15:41:53 +0700 Subject: [PATCH 5/5] ci: update scripts --- .github/workflows/ci.yml | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 39f2236..e1a0ab5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,7 @@ on: paths-ignore: - '.husky/**' - '**.md' + - 'docs/**' - 'package.json' jobs: @@ -15,24 +16,12 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [19.x] + node-version: [18.x, 20.x] steps: - uses: actions/checkout@v3 name: Checkout repository - - - name: Get yarn cache directory path - id: yarn-cache-dir-path - run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT - - - uses: actions/cache@v3 - id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) - with: - path: ${{ steps.yarn-cache-dir-path.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - + - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: @@ -40,10 +29,10 @@ jobs: cache: 'yarn' - name: Install dependencies - run: yarn + run: yarn && yarn db:generate - - name: Test - run: yarn lint && yarn test + - name: Run unit tests + run: yarn test unit - name: Build run: yarn build