From 0170421d16c0ad28ef15997bd0ffd7e7b3d5fca7 Mon Sep 17 00:00:00 2001 From: geonhee-min Date: Fri, 21 Nov 2025 15:18:37 +0900 Subject: [PATCH] =?UTF-8?q?issue=20#=20nestjs=20=EB=A1=9C=20=EC=B4=88?= =?UTF-8?q?=EA=B8=B0=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 8 +- .gitignore | 15 +- .gitlab-ci.yml | 80 +-- .prettierrc | 4 + .yarn/sdks/eslint/bin/eslint.js | 32 + .yarn/sdks/eslint/lib/api.js | 32 + .yarn/sdks/eslint/lib/config-api.js | 32 + .yarn/sdks/eslint/lib/types/config-api.d.ts | 32 + .yarn/sdks/eslint/lib/types/index.d.ts | 32 + .yarn/sdks/eslint/lib/types/rules.d.ts | 32 + .yarn/sdks/eslint/lib/types/universal.d.ts | 32 + .../lib/types/use-at-your-own-risk.d.ts | 32 + .yarn/sdks/eslint/lib/universal.js | 32 + .yarn/sdks/eslint/lib/unsupported-api.js | 32 + .yarn/sdks/eslint/package.json | 31 + .yarn/sdks/integrations.yml | 5 + .yarn/sdks/prettier/bin/prettier.cjs | 32 + .yarn/sdks/prettier/index.cjs | 32 + .yarn/sdks/prettier/package.json | 7 + .yarn/sdks/typescript/bin/tsc | 32 + .yarn/sdks/typescript/bin/tsserver | 32 + .yarn/sdks/typescript/lib/tsc.js | 32 + .yarn/sdks/typescript/lib/tsserver.js | 248 +++++++ .yarn/sdks/typescript/lib/tsserverlibrary.js | 248 +++++++ .yarn/sdks/typescript/lib/typescript.js | 32 + .yarn/sdks/typescript/package.json | 10 + README.md | 98 +++ drizzle.config.ts | 13 + drizzle/0000_parallel_rocket_racer.sql | 79 +++ drizzle/meta/0000_snapshot.json | 613 ++++++++++++++++++ drizzle/meta/_journal.json | 13 + drizzle/relations.ts | 74 +++ drizzle/schema.ts | 120 ++++ eslint.config.mjs | 35 + nest-cli.json | 8 + package.json | 92 ++- src/app.controller.spec.ts | 22 + src/app.controller.ts | 12 + src/app.module.ts | 12 + src/app.service.ts | 8 + src/app.ts | 17 - src/db/db.module.ts | 22 + src/main.ts | 8 + src/modules/account/account.controller.ts | 14 + src/modules/account/account.module.ts | 10 + src/modules/account/account.repo.ts | 21 + src/modules/account/account.service.ts | 15 + .../check-duplication-request.dto.ts | 9 + .../check-duplication-response.dto.ts | 3 + src/redis/redis.module.ts | 19 + src/route/user.route.ts | 0 src/server.ts | 14 - src/utils/plugin/swagger.ts | 33 - test/app.e2e-spec.ts | 25 + test/jest-e2e.json | 9 + tsconfig.build.json | 4 + tsconfig.json | 36 +- 57 files changed, 2483 insertions(+), 143 deletions(-) create mode 100644 .prettierrc create mode 100644 .yarn/sdks/eslint/bin/eslint.js create mode 100644 .yarn/sdks/eslint/lib/api.js create mode 100644 .yarn/sdks/eslint/lib/config-api.js create mode 100644 .yarn/sdks/eslint/lib/types/config-api.d.ts create mode 100644 .yarn/sdks/eslint/lib/types/index.d.ts create mode 100644 .yarn/sdks/eslint/lib/types/rules.d.ts create mode 100644 .yarn/sdks/eslint/lib/types/universal.d.ts create mode 100644 .yarn/sdks/eslint/lib/types/use-at-your-own-risk.d.ts create mode 100644 .yarn/sdks/eslint/lib/universal.js create mode 100644 .yarn/sdks/eslint/lib/unsupported-api.js create mode 100644 .yarn/sdks/eslint/package.json create mode 100644 .yarn/sdks/integrations.yml create mode 100644 .yarn/sdks/prettier/bin/prettier.cjs create mode 100644 .yarn/sdks/prettier/index.cjs create mode 100644 .yarn/sdks/prettier/package.json create mode 100644 .yarn/sdks/typescript/bin/tsc create mode 100644 .yarn/sdks/typescript/bin/tsserver create mode 100644 .yarn/sdks/typescript/lib/tsc.js create mode 100644 .yarn/sdks/typescript/lib/tsserver.js create mode 100644 .yarn/sdks/typescript/lib/tsserverlibrary.js create mode 100644 .yarn/sdks/typescript/lib/typescript.js create mode 100644 .yarn/sdks/typescript/package.json create mode 100644 README.md create mode 100644 drizzle.config.ts create mode 100644 drizzle/0000_parallel_rocket_racer.sql create mode 100644 drizzle/meta/0000_snapshot.json create mode 100644 drizzle/meta/_journal.json create mode 100644 drizzle/relations.ts create mode 100644 drizzle/schema.ts create mode 100644 eslint.config.mjs create mode 100644 nest-cli.json create mode 100644 src/app.controller.spec.ts create mode 100644 src/app.controller.ts create mode 100644 src/app.module.ts create mode 100644 src/app.service.ts delete mode 100644 src/app.ts create mode 100644 src/db/db.module.ts create mode 100644 src/main.ts create mode 100644 src/modules/account/account.controller.ts create mode 100644 src/modules/account/account.module.ts create mode 100644 src/modules/account/account.repo.ts create mode 100644 src/modules/account/account.service.ts create mode 100644 src/modules/account/dto/checkDuplication/check-duplication-request.dto.ts create mode 100644 src/modules/account/dto/checkDuplication/check-duplication-response.dto.ts create mode 100644 src/redis/redis.module.ts delete mode 100644 src/route/user.route.ts delete mode 100644 src/server.ts delete mode 100644 src/utils/plugin/swagger.ts create mode 100644 test/app.e2e-spec.ts create mode 100644 test/jest-e2e.json create mode 100644 tsconfig.build.json diff --git a/.env b/.env index c363173..1f489e6 100644 --- a/.env +++ b/.env @@ -4,8 +4,14 @@ PGPORT=15454 PGDATABASE=scheduler PGUSER=baekyangdan PGPASSWORD=qwas745478! +PG_DATABASE_URL=postgres://baekyangdan:qwas745478!@bkdhome.p-e.kr:15454/scheduler -# Fastify 서버 포트 +# Redis 설정 +RD_HOST=bkdhome.p-e.kr +RD_PORT=16779 +RD_URL=redis://bkdhome.p-e.kr:16779 + +# Express 서버 포트 PORT=3000 # Gmail SMTP 설정 diff --git a/.gitignore b/.gitignore index 84f7bd7..de8ab67 100644 --- a/.gitignore +++ b/.gitignore @@ -5,14 +5,24 @@ yarn-error.log* package-lock.json yarn.lock +.pnp.* +.pnp.loader.mjs +.yarn/install-state.gz + # TypeScript dist/ +build/ *.tsbuildinfo # 환경 변수 # .env # .env.*.local +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + # IDE / OS .vscode/ .idea/ @@ -30,4 +40,7 @@ Thumbs.db # Logs logs/ -*.log \ No newline at end of file +*.log + +.cache/ +.eslintcache \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 13e6a5b..b6de400 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,43 +1,43 @@ -# This file is a template, and might need editing before it works on your project. -# This is a sample GitLab CI/CD configuration file that should run without any modifications. -# It demonstrates a basic 3 stage CI/CD pipeline. Instead of real tests or scripts, -# it uses echo commands to simulate the pipeline execution. -# -# A pipeline is composed of independent jobs that run scripts, grouped into stages. -# Stages run in sequential order, but jobs within stages run in parallel. -# -# For more information, see: https://docs.gitlab.com/ee/ci/yaml/#stages -# -# You can copy and paste this template into a new `.gitlab-ci.yml` file. -# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword. -# -# To contribute improvements to CI/CD templates, please follow the Development guide at: -# https://docs.gitlab.com/development/cicd/templates/ -# This specific template is located at: -# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Getting-Started.gitlab-ci.yml +# # This file is a template, and might need editing before it works on your project. +# # This is a sample GitLab CI/CD configuration file that should run without any modifications. +# # It demonstrates a basic 3 stage CI/CD pipeline. Instead of real tests or scripts, +# # it uses echo commands to simulate the pipeline execution. +# # +# # A pipeline is composed of independent jobs that run scripts, grouped into stages. +# # Stages run in sequential order, but jobs within stages run in parallel. +# # +# # For more information, see: https://docs.gitlab.com/ee/ci/yaml/#stages +# # +# # You can copy and paste this template into a new `.gitlab-ci.yml` file. +# # You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword. +# # +# # To contribute improvements to CI/CD templates, please follow the Development guide at: +# # https://docs.gitlab.com/development/cicd/templates/ +# # This specific template is located at: +# # https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Getting-Started.gitlab-ci.yml -stages: # List of stages for jobs, and their order of execution - - build +# stages: # List of stages for jobs, and their order of execution +# - build -cache: - key: - files: - - package-lock.json - paths: - - node_modules/ +# cache: +# key: +# files: +# - package-lock.json +# paths: +# - node_modules/ -build: # This job runs in the build stage, which runs first. - stage: build - tags: - - local-runner - before_script: - script: - - echo "Compiling the code..." - - echo $DOCKER_VOLUME - - echo $DOCKER_COMPOSE_VOLUME - - npm install - - npm run build - - sudo cp -r $PWD/dist/. $DOCKER_VOLUME/scheduler/back/dist - - sudo cp $PWD/package.json $DOCKER_VOLUME/scheduler/back/dist - - docker compose -f $DOCKER_COMPOSE_VOLUME/scheduler/docker-compose.yaml up -d back - - echo "Compile complete." \ No newline at end of file +# build: # This job runs in the build stage, which runs first. +# stage: build +# tags: +# - local-runner +# before_script: +# script: +# - echo "Compiling the code..." +# - echo $DOCKER_VOLUME +# - echo $DOCKER_COMPOSE_VOLUME +# - npm install +# - npm run build +# - sudo cp -r $PWD/dist/. $DOCKER_VOLUME/scheduler/back/dist +# - sudo cp $PWD/package.json $DOCKER_VOLUME/scheduler/back/dist +# - docker compose -f $DOCKER_COMPOSE_VOLUME/scheduler/docker-compose.yaml up -d back +# - echo "Compile complete." \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..a20502b --- /dev/null +++ b/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "trailingComma": "all" +} diff --git a/.yarn/sdks/eslint/bin/eslint.js b/.yarn/sdks/eslint/bin/eslint.js new file mode 100644 index 0000000..e6604ff --- /dev/null +++ b/.yarn/sdks/eslint/bin/eslint.js @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); + +const relPnpApiPath = "../../../../.pnp.cjs"; + +const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); +const absRequire = createRequire(absPnpApiPath); + +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require eslint/bin/eslint.js + require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } + } +} + +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + +// Defer to the real eslint/bin/eslint.js your application uses +module.exports = wrapWithUserWrapper(absRequire(`eslint/bin/eslint.js`)); diff --git a/.yarn/sdks/eslint/lib/api.js b/.yarn/sdks/eslint/lib/api.js new file mode 100644 index 0000000..8addf97 --- /dev/null +++ b/.yarn/sdks/eslint/lib/api.js @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); + +const relPnpApiPath = "../../../../.pnp.cjs"; + +const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); +const absRequire = createRequire(absPnpApiPath); + +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require eslint + require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } + } +} + +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + +// Defer to the real eslint your application uses +module.exports = wrapWithUserWrapper(absRequire(`eslint`)); diff --git a/.yarn/sdks/eslint/lib/config-api.js b/.yarn/sdks/eslint/lib/config-api.js new file mode 100644 index 0000000..e84435d --- /dev/null +++ b/.yarn/sdks/eslint/lib/config-api.js @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); + +const relPnpApiPath = "../../../../.pnp.cjs"; + +const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); +const absRequire = createRequire(absPnpApiPath); + +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require eslint/config + require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } + } +} + +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + +// Defer to the real eslint/config your application uses +module.exports = wrapWithUserWrapper(absRequire(`eslint/config`)); diff --git a/.yarn/sdks/eslint/lib/types/config-api.d.ts b/.yarn/sdks/eslint/lib/types/config-api.d.ts new file mode 100644 index 0000000..174070b --- /dev/null +++ b/.yarn/sdks/eslint/lib/types/config-api.d.ts @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); + +const relPnpApiPath = "../../../../../.pnp.cjs"; + +const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); +const absRequire = createRequire(absPnpApiPath); + +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require eslint/config + require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } + } +} + +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + +// Defer to the real eslint/config your application uses +module.exports = wrapWithUserWrapper(absRequire(`eslint/config`)); diff --git a/.yarn/sdks/eslint/lib/types/index.d.ts b/.yarn/sdks/eslint/lib/types/index.d.ts new file mode 100644 index 0000000..19293d0 --- /dev/null +++ b/.yarn/sdks/eslint/lib/types/index.d.ts @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); + +const relPnpApiPath = "../../../../../.pnp.cjs"; + +const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); +const absRequire = createRequire(absPnpApiPath); + +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require eslint + require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } + } +} + +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + +// Defer to the real eslint your application uses +module.exports = wrapWithUserWrapper(absRequire(`eslint`)); diff --git a/.yarn/sdks/eslint/lib/types/rules.d.ts b/.yarn/sdks/eslint/lib/types/rules.d.ts new file mode 100644 index 0000000..8d79c4c --- /dev/null +++ b/.yarn/sdks/eslint/lib/types/rules.d.ts @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); + +const relPnpApiPath = "../../../../../.pnp.cjs"; + +const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); +const absRequire = createRequire(absPnpApiPath); + +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require eslint/rules + require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } + } +} + +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + +// Defer to the real eslint/rules your application uses +module.exports = wrapWithUserWrapper(absRequire(`eslint/rules`)); diff --git a/.yarn/sdks/eslint/lib/types/universal.d.ts b/.yarn/sdks/eslint/lib/types/universal.d.ts new file mode 100644 index 0000000..662b3f4 --- /dev/null +++ b/.yarn/sdks/eslint/lib/types/universal.d.ts @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); + +const relPnpApiPath = "../../../../../.pnp.cjs"; + +const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); +const absRequire = createRequire(absPnpApiPath); + +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require eslint/universal + require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } + } +} + +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + +// Defer to the real eslint/universal your application uses +module.exports = wrapWithUserWrapper(absRequire(`eslint/universal`)); diff --git a/.yarn/sdks/eslint/lib/types/use-at-your-own-risk.d.ts b/.yarn/sdks/eslint/lib/types/use-at-your-own-risk.d.ts new file mode 100644 index 0000000..2e2ccca --- /dev/null +++ b/.yarn/sdks/eslint/lib/types/use-at-your-own-risk.d.ts @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); + +const relPnpApiPath = "../../../../../.pnp.cjs"; + +const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); +const absRequire = createRequire(absPnpApiPath); + +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require eslint/use-at-your-own-risk + require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } + } +} + +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + +// Defer to the real eslint/use-at-your-own-risk your application uses +module.exports = wrapWithUserWrapper(absRequire(`eslint/use-at-your-own-risk`)); diff --git a/.yarn/sdks/eslint/lib/universal.js b/.yarn/sdks/eslint/lib/universal.js new file mode 100644 index 0000000..85a8ccb --- /dev/null +++ b/.yarn/sdks/eslint/lib/universal.js @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); + +const relPnpApiPath = "../../../../.pnp.cjs"; + +const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); +const absRequire = createRequire(absPnpApiPath); + +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require eslint/universal + require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } + } +} + +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + +// Defer to the real eslint/universal your application uses +module.exports = wrapWithUserWrapper(absRequire(`eslint/universal`)); diff --git a/.yarn/sdks/eslint/lib/unsupported-api.js b/.yarn/sdks/eslint/lib/unsupported-api.js new file mode 100644 index 0000000..c2b464c --- /dev/null +++ b/.yarn/sdks/eslint/lib/unsupported-api.js @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); + +const relPnpApiPath = "../../../../.pnp.cjs"; + +const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); +const absRequire = createRequire(absPnpApiPath); + +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require eslint/use-at-your-own-risk + require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } + } +} + +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + +// Defer to the real eslint/use-at-your-own-risk your application uses +module.exports = wrapWithUserWrapper(absRequire(`eslint/use-at-your-own-risk`)); diff --git a/.yarn/sdks/eslint/package.json b/.yarn/sdks/eslint/package.json new file mode 100644 index 0000000..6cda410 --- /dev/null +++ b/.yarn/sdks/eslint/package.json @@ -0,0 +1,31 @@ +{ + "name": "eslint", + "version": "9.39.1-sdk", + "main": "./lib/api.js", + "type": "commonjs", + "bin": { + "eslint": "./bin/eslint.js" + }, + "exports": { + ".": { + "types": "./lib/types/index.d.ts", + "default": "./lib/api.js" + }, + "./config": { + "types": "./lib/types/config-api.d.ts", + "default": "./lib/config-api.js" + }, + "./package.json": "./package.json", + "./use-at-your-own-risk": { + "types": "./lib/types/use-at-your-own-risk.d.ts", + "default": "./lib/unsupported-api.js" + }, + "./rules": { + "types": "./lib/types/rules.d.ts" + }, + "./universal": { + "types": "./lib/types/universal.d.ts", + "default": "./lib/universal.js" + } + } +} diff --git a/.yarn/sdks/integrations.yml b/.yarn/sdks/integrations.yml new file mode 100644 index 0000000..aa9d0d0 --- /dev/null +++ b/.yarn/sdks/integrations.yml @@ -0,0 +1,5 @@ +# This file is automatically generated by @yarnpkg/sdks. +# Manual changes might be lost! + +integrations: + - vscode diff --git a/.yarn/sdks/prettier/bin/prettier.cjs b/.yarn/sdks/prettier/bin/prettier.cjs new file mode 100644 index 0000000..9a4098f --- /dev/null +++ b/.yarn/sdks/prettier/bin/prettier.cjs @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); + +const relPnpApiPath = "../../../../.pnp.cjs"; + +const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); +const absRequire = createRequire(absPnpApiPath); + +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require prettier/bin/prettier.cjs + require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } + } +} + +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + +// Defer to the real prettier/bin/prettier.cjs your application uses +module.exports = wrapWithUserWrapper(absRequire(`prettier/bin/prettier.cjs`)); diff --git a/.yarn/sdks/prettier/index.cjs b/.yarn/sdks/prettier/index.cjs new file mode 100644 index 0000000..57cb2ab --- /dev/null +++ b/.yarn/sdks/prettier/index.cjs @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); + +const relPnpApiPath = "../../../.pnp.cjs"; + +const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); +const absRequire = createRequire(absPnpApiPath); + +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require prettier + require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } + } +} + +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + +// Defer to the real prettier your application uses +module.exports = wrapWithUserWrapper(absRequire(`prettier`)); diff --git a/.yarn/sdks/prettier/package.json b/.yarn/sdks/prettier/package.json new file mode 100644 index 0000000..1488e98 --- /dev/null +++ b/.yarn/sdks/prettier/package.json @@ -0,0 +1,7 @@ +{ + "name": "prettier", + "version": "3.6.2-sdk", + "main": "./index.cjs", + "type": "commonjs", + "bin": "./bin/prettier.cjs" +} diff --git a/.yarn/sdks/typescript/bin/tsc b/.yarn/sdks/typescript/bin/tsc new file mode 100644 index 0000000..867a7bd --- /dev/null +++ b/.yarn/sdks/typescript/bin/tsc @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); + +const relPnpApiPath = "../../../../.pnp.cjs"; + +const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); +const absRequire = createRequire(absPnpApiPath); + +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require typescript/bin/tsc + require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } + } +} + +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + +// Defer to the real typescript/bin/tsc your application uses +module.exports = wrapWithUserWrapper(absRequire(`typescript/bin/tsc`)); diff --git a/.yarn/sdks/typescript/bin/tsserver b/.yarn/sdks/typescript/bin/tsserver new file mode 100644 index 0000000..3fc5aa3 --- /dev/null +++ b/.yarn/sdks/typescript/bin/tsserver @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); + +const relPnpApiPath = "../../../../.pnp.cjs"; + +const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); +const absRequire = createRequire(absPnpApiPath); + +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require typescript/bin/tsserver + require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } + } +} + +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + +// Defer to the real typescript/bin/tsserver your application uses +module.exports = wrapWithUserWrapper(absRequire(`typescript/bin/tsserver`)); diff --git a/.yarn/sdks/typescript/lib/tsc.js b/.yarn/sdks/typescript/lib/tsc.js new file mode 100644 index 0000000..da411bd --- /dev/null +++ b/.yarn/sdks/typescript/lib/tsc.js @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); + +const relPnpApiPath = "../../../../.pnp.cjs"; + +const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); +const absRequire = createRequire(absPnpApiPath); + +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require typescript/lib/tsc.js + require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } + } +} + +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + +// Defer to the real typescript/lib/tsc.js your application uses +module.exports = wrapWithUserWrapper(absRequire(`typescript/lib/tsc.js`)); diff --git a/.yarn/sdks/typescript/lib/tsserver.js b/.yarn/sdks/typescript/lib/tsserver.js new file mode 100644 index 0000000..6249c46 --- /dev/null +++ b/.yarn/sdks/typescript/lib/tsserver.js @@ -0,0 +1,248 @@ +#!/usr/bin/env node + +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); + +const relPnpApiPath = "../../../../.pnp.cjs"; + +const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); +const absRequire = createRequire(absPnpApiPath); + +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require typescript/lib/tsserver.js + require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } + } +} + +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + +const moduleWrapper = exports => { + return wrapWithUserWrapper(moduleWrapperFn(exports)); +}; + +const moduleWrapperFn = tsserver => { + if (!process.versions.pnp) { + return tsserver; + } + + const {isAbsolute} = require(`path`); + const pnpApi = require(`pnpapi`); + + const isVirtual = str => str.match(/\/(\$\$virtual|__virtual__)\//); + const isPortal = str => str.startsWith("portal:/"); + const normalize = str => str.replace(/\\/g, `/`).replace(/^\/?/, `/`); + + const dependencyTreeRoots = new Set(pnpApi.getDependencyTreeRoots().map(locator => { + return `${locator.name}@${locator.reference}`; + })); + + // VSCode sends the zip paths to TS using the "zip://" prefix, that TS + // doesn't understand. This layer makes sure to remove the protocol + // before forwarding it to TS, and to add it back on all returned paths. + + function toEditorPath(str) { + // We add the `zip:` prefix to both `.zip/` paths and virtual paths + if (isAbsolute(str) && !str.match(/^\^?(zip:|\/zip\/)/) && (str.match(/\.zip\//) || isVirtual(str))) { + // We also take the opportunity to turn virtual paths into physical ones; + // this makes it much easier to work with workspaces that list peer + // dependencies, since otherwise Ctrl+Click would bring us to the virtual + // file instances instead of the real ones. + // + // We only do this to modules owned by the the dependency tree roots. + // This avoids breaking the resolution when jumping inside a vendor + // with peer dep (otherwise jumping into react-dom would show resolution + // errors on react). + // + const resolved = isVirtual(str) ? pnpApi.resolveVirtual(str) : str; + if (resolved) { + const locator = pnpApi.findPackageLocator(resolved); + if (locator && (dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) || isPortal(locator.reference))) { + str = resolved; + } + } + + str = normalize(str); + + if (str.match(/\.zip\//)) { + switch (hostInfo) { + // Absolute VSCode `Uri.fsPath`s need to start with a slash. + // VSCode only adds it automatically for supported schemes, + // so we have to do it manually for the `zip` scheme. + // The path needs to start with a caret otherwise VSCode doesn't handle the protocol + // + // Ref: https://github.com/microsoft/vscode/issues/105014#issuecomment-686760910 + // + // 2021-10-08: VSCode changed the format in 1.61. + // Before | ^zip:/c:/foo/bar.zip/package.json + // After | ^/zip//c:/foo/bar.zip/package.json + // + // 2022-04-06: VSCode changed the format in 1.66. + // Before | ^/zip//c:/foo/bar.zip/package.json + // After | ^/zip/c:/foo/bar.zip/package.json + // + // 2022-05-06: VSCode changed the format in 1.68 + // Before | ^/zip/c:/foo/bar.zip/package.json + // After | ^/zip//c:/foo/bar.zip/package.json + // + case `vscode <1.61`: { + str = `^zip:${str}`; + } break; + + case `vscode <1.66`: { + str = `^/zip/${str}`; + } break; + + case `vscode <1.68`: { + str = `^/zip${str}`; + } break; + + case `vscode`: { + str = `^/zip/${str}`; + } break; + + // To make "go to definition" work, + // We have to resolve the actual file system path from virtual path + // and convert scheme to supported by [vim-rzip](https://github.com/lbrayner/vim-rzip) + case `coc-nvim`: { + str = normalize(resolved).replace(/\.zip\//, `.zip::`); + str = resolve(`zipfile:${str}`); + } break; + + // Support neovim native LSP and [typescript-language-server](https://github.com/theia-ide/typescript-language-server) + // We have to resolve the actual file system path from virtual path, + // everything else is up to neovim + case `neovim`: { + str = normalize(resolved).replace(/\.zip\//, `.zip::`); + str = `zipfile://${str}`; + } break; + + default: { + str = `zip:${str}`; + } break; + } + } else { + str = str.replace(/^\/?/, process.platform === `win32` ? `` : `/`); + } + } + + return str; + } + + function fromEditorPath(str) { + switch (hostInfo) { + case `coc-nvim`: { + str = str.replace(/\.zip::/, `.zip/`); + // The path for coc-nvim is in format of //zipfile://.yarn/... + // So in order to convert it back, we use .* to match all the thing + // before `zipfile:` + return process.platform === `win32` + ? str.replace(/^.*zipfile:\//, ``) + : str.replace(/^.*zipfile:/, ``); + } break; + + case `neovim`: { + str = str.replace(/\.zip::/, `.zip/`); + // The path for neovim is in format of zipfile:////.yarn/... + return str.replace(/^zipfile:\/\//, ``); + } break; + + case `vscode`: + default: { + return str.replace(/^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/, process.platform === `win32` ? `` : `/`) + } break; + } + } + + // Force enable 'allowLocalPluginLoads' + // TypeScript tries to resolve plugins using a path relative to itself + // which doesn't work when using the global cache + // https://github.com/microsoft/TypeScript/blob/1b57a0395e0bff191581c9606aab92832001de62/src/server/project.ts#L2238 + // VSCode doesn't want to enable 'allowLocalPluginLoads' due to security concerns but + // TypeScript already does local loads and if this code is running the user trusts the workspace + // https://github.com/microsoft/vscode/issues/45856 + const ConfiguredProject = tsserver.server.ConfiguredProject; + const {enablePluginsWithOptions: originalEnablePluginsWithOptions} = ConfiguredProject.prototype; + ConfiguredProject.prototype.enablePluginsWithOptions = function() { + this.projectService.allowLocalPluginLoads = true; + return originalEnablePluginsWithOptions.apply(this, arguments); + }; + + // And here is the point where we hijack the VSCode <-> TS communications + // by adding ourselves in the middle. We locate everything that looks + // like an absolute path of ours and normalize it. + + const Session = tsserver.server.Session; + const {onMessage: originalOnMessage, send: originalSend} = Session.prototype; + let hostInfo = `unknown`; + + Object.assign(Session.prototype, { + onMessage(/** @type {string | object} */ message) { + const isStringMessage = typeof message === 'string'; + const parsedMessage = isStringMessage ? JSON.parse(message) : message; + + if ( + parsedMessage != null && + typeof parsedMessage === `object` && + parsedMessage.arguments && + typeof parsedMessage.arguments.hostInfo === `string` + ) { + hostInfo = parsedMessage.arguments.hostInfo; + if (hostInfo === `vscode` && process.env.VSCODE_IPC_HOOK) { + const [, major, minor] = (process.env.VSCODE_IPC_HOOK.match( + // The RegExp from https://semver.org/ but without the caret at the start + /(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/ + ) ?? []).map(Number) + + if (major === 1) { + if (minor < 61) { + hostInfo += ` <1.61`; + } else if (minor < 66) { + hostInfo += ` <1.66`; + } else if (minor < 68) { + hostInfo += ` <1.68`; + } + } + } + } + + const processedMessageJSON = JSON.stringify(parsedMessage, (key, value) => { + return typeof value === 'string' ? fromEditorPath(value) : value; + }); + + return originalOnMessage.call( + this, + isStringMessage ? processedMessageJSON : JSON.parse(processedMessageJSON) + ); + }, + + send(/** @type {any} */ msg) { + return originalSend.call(this, JSON.parse(JSON.stringify(msg, (key, value) => { + return typeof value === `string` ? toEditorPath(value) : value; + }))); + } + }); + + return tsserver; +}; + +const [major, minor] = absRequire(`typescript/package.json`).version.split(`.`, 2).map(value => parseInt(value, 10)); +// In TypeScript@>=5.5 the tsserver uses the public TypeScript API so that needs to be patched as well. +// Ref https://github.com/microsoft/TypeScript/pull/55326 +if (major > 5 || (major === 5 && minor >= 5)) { + moduleWrapper(absRequire(`typescript`)); +} + +// Defer to the real typescript/lib/tsserver.js your application uses +module.exports = moduleWrapper(absRequire(`typescript/lib/tsserver.js`)); diff --git a/.yarn/sdks/typescript/lib/tsserverlibrary.js b/.yarn/sdks/typescript/lib/tsserverlibrary.js new file mode 100644 index 0000000..0e50e0a --- /dev/null +++ b/.yarn/sdks/typescript/lib/tsserverlibrary.js @@ -0,0 +1,248 @@ +#!/usr/bin/env node + +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); + +const relPnpApiPath = "../../../../.pnp.cjs"; + +const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); +const absRequire = createRequire(absPnpApiPath); + +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require typescript/lib/tsserverlibrary.js + require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } + } +} + +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + +const moduleWrapper = exports => { + return wrapWithUserWrapper(moduleWrapperFn(exports)); +}; + +const moduleWrapperFn = tsserver => { + if (!process.versions.pnp) { + return tsserver; + } + + const {isAbsolute} = require(`path`); + const pnpApi = require(`pnpapi`); + + const isVirtual = str => str.match(/\/(\$\$virtual|__virtual__)\//); + const isPortal = str => str.startsWith("portal:/"); + const normalize = str => str.replace(/\\/g, `/`).replace(/^\/?/, `/`); + + const dependencyTreeRoots = new Set(pnpApi.getDependencyTreeRoots().map(locator => { + return `${locator.name}@${locator.reference}`; + })); + + // VSCode sends the zip paths to TS using the "zip://" prefix, that TS + // doesn't understand. This layer makes sure to remove the protocol + // before forwarding it to TS, and to add it back on all returned paths. + + function toEditorPath(str) { + // We add the `zip:` prefix to both `.zip/` paths and virtual paths + if (isAbsolute(str) && !str.match(/^\^?(zip:|\/zip\/)/) && (str.match(/\.zip\//) || isVirtual(str))) { + // We also take the opportunity to turn virtual paths into physical ones; + // this makes it much easier to work with workspaces that list peer + // dependencies, since otherwise Ctrl+Click would bring us to the virtual + // file instances instead of the real ones. + // + // We only do this to modules owned by the the dependency tree roots. + // This avoids breaking the resolution when jumping inside a vendor + // with peer dep (otherwise jumping into react-dom would show resolution + // errors on react). + // + const resolved = isVirtual(str) ? pnpApi.resolveVirtual(str) : str; + if (resolved) { + const locator = pnpApi.findPackageLocator(resolved); + if (locator && (dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) || isPortal(locator.reference))) { + str = resolved; + } + } + + str = normalize(str); + + if (str.match(/\.zip\//)) { + switch (hostInfo) { + // Absolute VSCode `Uri.fsPath`s need to start with a slash. + // VSCode only adds it automatically for supported schemes, + // so we have to do it manually for the `zip` scheme. + // The path needs to start with a caret otherwise VSCode doesn't handle the protocol + // + // Ref: https://github.com/microsoft/vscode/issues/105014#issuecomment-686760910 + // + // 2021-10-08: VSCode changed the format in 1.61. + // Before | ^zip:/c:/foo/bar.zip/package.json + // After | ^/zip//c:/foo/bar.zip/package.json + // + // 2022-04-06: VSCode changed the format in 1.66. + // Before | ^/zip//c:/foo/bar.zip/package.json + // After | ^/zip/c:/foo/bar.zip/package.json + // + // 2022-05-06: VSCode changed the format in 1.68 + // Before | ^/zip/c:/foo/bar.zip/package.json + // After | ^/zip//c:/foo/bar.zip/package.json + // + case `vscode <1.61`: { + str = `^zip:${str}`; + } break; + + case `vscode <1.66`: { + str = `^/zip/${str}`; + } break; + + case `vscode <1.68`: { + str = `^/zip${str}`; + } break; + + case `vscode`: { + str = `^/zip/${str}`; + } break; + + // To make "go to definition" work, + // We have to resolve the actual file system path from virtual path + // and convert scheme to supported by [vim-rzip](https://github.com/lbrayner/vim-rzip) + case `coc-nvim`: { + str = normalize(resolved).replace(/\.zip\//, `.zip::`); + str = resolve(`zipfile:${str}`); + } break; + + // Support neovim native LSP and [typescript-language-server](https://github.com/theia-ide/typescript-language-server) + // We have to resolve the actual file system path from virtual path, + // everything else is up to neovim + case `neovim`: { + str = normalize(resolved).replace(/\.zip\//, `.zip::`); + str = `zipfile://${str}`; + } break; + + default: { + str = `zip:${str}`; + } break; + } + } else { + str = str.replace(/^\/?/, process.platform === `win32` ? `` : `/`); + } + } + + return str; + } + + function fromEditorPath(str) { + switch (hostInfo) { + case `coc-nvim`: { + str = str.replace(/\.zip::/, `.zip/`); + // The path for coc-nvim is in format of //zipfile://.yarn/... + // So in order to convert it back, we use .* to match all the thing + // before `zipfile:` + return process.platform === `win32` + ? str.replace(/^.*zipfile:\//, ``) + : str.replace(/^.*zipfile:/, ``); + } break; + + case `neovim`: { + str = str.replace(/\.zip::/, `.zip/`); + // The path for neovim is in format of zipfile:////.yarn/... + return str.replace(/^zipfile:\/\//, ``); + } break; + + case `vscode`: + default: { + return str.replace(/^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/, process.platform === `win32` ? `` : `/`) + } break; + } + } + + // Force enable 'allowLocalPluginLoads' + // TypeScript tries to resolve plugins using a path relative to itself + // which doesn't work when using the global cache + // https://github.com/microsoft/TypeScript/blob/1b57a0395e0bff191581c9606aab92832001de62/src/server/project.ts#L2238 + // VSCode doesn't want to enable 'allowLocalPluginLoads' due to security concerns but + // TypeScript already does local loads and if this code is running the user trusts the workspace + // https://github.com/microsoft/vscode/issues/45856 + const ConfiguredProject = tsserver.server.ConfiguredProject; + const {enablePluginsWithOptions: originalEnablePluginsWithOptions} = ConfiguredProject.prototype; + ConfiguredProject.prototype.enablePluginsWithOptions = function() { + this.projectService.allowLocalPluginLoads = true; + return originalEnablePluginsWithOptions.apply(this, arguments); + }; + + // And here is the point where we hijack the VSCode <-> TS communications + // by adding ourselves in the middle. We locate everything that looks + // like an absolute path of ours and normalize it. + + const Session = tsserver.server.Session; + const {onMessage: originalOnMessage, send: originalSend} = Session.prototype; + let hostInfo = `unknown`; + + Object.assign(Session.prototype, { + onMessage(/** @type {string | object} */ message) { + const isStringMessage = typeof message === 'string'; + const parsedMessage = isStringMessage ? JSON.parse(message) : message; + + if ( + parsedMessage != null && + typeof parsedMessage === `object` && + parsedMessage.arguments && + typeof parsedMessage.arguments.hostInfo === `string` + ) { + hostInfo = parsedMessage.arguments.hostInfo; + if (hostInfo === `vscode` && process.env.VSCODE_IPC_HOOK) { + const [, major, minor] = (process.env.VSCODE_IPC_HOOK.match( + // The RegExp from https://semver.org/ but without the caret at the start + /(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/ + ) ?? []).map(Number) + + if (major === 1) { + if (minor < 61) { + hostInfo += ` <1.61`; + } else if (minor < 66) { + hostInfo += ` <1.66`; + } else if (minor < 68) { + hostInfo += ` <1.68`; + } + } + } + } + + const processedMessageJSON = JSON.stringify(parsedMessage, (key, value) => { + return typeof value === 'string' ? fromEditorPath(value) : value; + }); + + return originalOnMessage.call( + this, + isStringMessage ? processedMessageJSON : JSON.parse(processedMessageJSON) + ); + }, + + send(/** @type {any} */ msg) { + return originalSend.call(this, JSON.parse(JSON.stringify(msg, (key, value) => { + return typeof value === `string` ? toEditorPath(value) : value; + }))); + } + }); + + return tsserver; +}; + +const [major, minor] = absRequire(`typescript/package.json`).version.split(`.`, 2).map(value => parseInt(value, 10)); +// In TypeScript@>=5.5 the tsserver uses the public TypeScript API so that needs to be patched as well. +// Ref https://github.com/microsoft/TypeScript/pull/55326 +if (major > 5 || (major === 5 && minor >= 5)) { + moduleWrapper(absRequire(`typescript`)); +} + +// Defer to the real typescript/lib/tsserverlibrary.js your application uses +module.exports = moduleWrapper(absRequire(`typescript/lib/tsserverlibrary.js`)); diff --git a/.yarn/sdks/typescript/lib/typescript.js b/.yarn/sdks/typescript/lib/typescript.js new file mode 100644 index 0000000..7b6cc22 --- /dev/null +++ b/.yarn/sdks/typescript/lib/typescript.js @@ -0,0 +1,32 @@ +#!/usr/bin/env node + +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); + +const relPnpApiPath = "../../../../.pnp.cjs"; + +const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); +const absRequire = createRequire(absPnpApiPath); + +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require typescript + require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } + } +} + +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + +// Defer to the real typescript your application uses +module.exports = wrapWithUserWrapper(absRequire(`typescript`)); diff --git a/.yarn/sdks/typescript/package.json b/.yarn/sdks/typescript/package.json new file mode 100644 index 0000000..19a0b6f --- /dev/null +++ b/.yarn/sdks/typescript/package.json @@ -0,0 +1,10 @@ +{ + "name": "typescript", + "version": "5.9.3-sdk", + "main": "./lib/typescript.js", + "type": "commonjs", + "bin": { + "tsc": "./bin/tsc", + "tsserver": "./bin/tsserver" + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..da24eaa --- /dev/null +++ b/README.md @@ -0,0 +1,98 @@ +

+ Nest Logo +

+ +[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456 +[circleci-url]: https://circleci.com/gh/nestjs/nest + +

A progressive Node.js framework for building efficient and scalable server-side applications.

+

+NPM Version +Package License +NPM Downloads +CircleCI +Discord +Backers on Open Collective +Sponsors on Open Collective + Donate us + Support us + Follow us on Twitter +

+ + +## Description + +[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository. + +## Project setup + +```bash +$ yarn install +``` + +## Compile and run the project + +```bash +# development +$ yarn run start + +# watch mode +$ yarn run start:dev + +# production mode +$ yarn run start:prod +``` + +## Run tests + +```bash +# unit tests +$ yarn run test + +# e2e tests +$ yarn run test:e2e + +# test coverage +$ yarn run test:cov +``` + +## Deployment + +When you're ready to deploy your NestJS application to production, there are some key steps you can take to ensure it runs as efficiently as possible. Check out the [deployment documentation](https://docs.nestjs.com/deployment) for more information. + +If you are looking for a cloud-based platform to deploy your NestJS application, check out [Mau](https://mau.nestjs.com), our official platform for deploying NestJS applications on AWS. Mau makes deployment straightforward and fast, requiring just a few simple steps: + +```bash +$ yarn install -g @nestjs/mau +$ mau deploy +``` + +With Mau, you can deploy your application in just a few clicks, allowing you to focus on building features rather than managing infrastructure. + +## Resources + +Check out a few resources that may come in handy when working with NestJS: + +- Visit the [NestJS Documentation](https://docs.nestjs.com) to learn more about the framework. +- For questions and support, please visit our [Discord channel](https://discord.gg/G7Qnnhy). +- To dive deeper and get more hands-on experience, check out our official video [courses](https://courses.nestjs.com/). +- Deploy your application to AWS with the help of [NestJS Mau](https://mau.nestjs.com) in just a few clicks. +- Visualize your application graph and interact with the NestJS application in real-time using [NestJS Devtools](https://devtools.nestjs.com). +- Need help with your project (part-time to full-time)? Check out our official [enterprise support](https://enterprise.nestjs.com). +- To stay in the loop and get updates, follow us on [X](https://x.com/nestframework) and [LinkedIn](https://linkedin.com/company/nestjs). +- Looking for a job, or have a job to offer? Check out our official [Jobs board](https://jobs.nestjs.com). + +## Support + +Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support). + +## Stay in touch + +- Author - [Kamil Myśliwiec](https://twitter.com/kammysliwiec) +- Website - [https://nestjs.com](https://nestjs.com/) +- Twitter - [@nestframework](https://twitter.com/nestframework) + +## License + +Nest is [MIT licensed](https://github.com/nestjs/nest/blob/master/LICENSE). diff --git a/drizzle.config.ts b/drizzle.config.ts new file mode 100644 index 0000000..dcbbd29 --- /dev/null +++ b/drizzle.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from 'drizzle-kit'; +import dotenv from 'dotenv'; + +dotenv.config(); + +export default defineConfig({ + dialect: "postgresql", + schema: "./src/db/schema", + out: "./drizzle", + dbCredentials: { + url: process.env.PG_DATABASE_URL! + } +}); diff --git a/drizzle/0000_parallel_rocket_racer.sql b/drizzle/0000_parallel_rocket_racer.sql new file mode 100644 index 0000000..d3b5baa --- /dev/null +++ b/drizzle/0000_parallel_rocket_racer.sql @@ -0,0 +1,79 @@ +-- Current sql file was generated after introspecting the database +-- If you want to run this migration please uncomment this code before executing migrations +/* +CREATE TABLE "comment" ( + "id" uuid PRIMARY KEY NOT NULL, + "content" text, + "created_at" date, + "is_deleted" boolean DEFAULT false, + "writer_id" uuid, + "parent_id" uuid +); +--> statement-breakpoint +CREATE TABLE "schedule" ( + "id" uuid PRIMARY KEY NOT NULL, + "name" varchar, + "start_at" date, + "end_at" date, + "status" varchar, + "content" text, + "is_deleted" boolean DEFAULT false, + "type" varchar, + "created_at" date, + "owner" uuid +); +--> statement-breakpoint +CREATE TABLE "account" ( + "name" varchar NOT NULL, + "email" varchar NOT NULL, + "password" varchar NOT NULL, + "birthday" date, + "account_id" varchar NOT NULL, + "nickname" varchar NOT NULL, + "status" varchar DEFAULT 'wait' NOT NULL, + "is_deleted" boolean DEFAULT false NOT NULL, + "created_at" date DEFAULT now() NOT NULL, + "id" uuid PRIMARY KEY DEFAULT uuid_generate_v4() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "participant" ( + "participant_id" uuid NOT NULL, + "schedule_id" uuid NOT NULL, + "is_deleted" boolean DEFAULT false +); +--> statement-breakpoint +CREATE TABLE "favorite" ( + "is_deleted" boolean DEFAULT false, + "created_at" date, + "user_id" uuid NOT NULL, + "schedule_id" uuid NOT NULL, + CONSTRAINT "favorite_pk" PRIMARY KEY("user_id","schedule_id") +); +--> statement-breakpoint +CREATE TABLE "follow" ( + "is_deleted" boolean DEFAULT false, + "is_accepted" boolean DEFAULT false, + "is_linked" boolean DEFAULT false, + "created_at" date, + "following" uuid NOT NULL, + "follower" uuid NOT NULL, + CONSTRAINT "follow_pk" PRIMARY KEY("following","follower") +); +--> statement-breakpoint +ALTER TABLE "comment" ADD CONSTRAINT "writer_id" FOREIGN KEY ("writer_id") REFERENCES "public"."account"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "comment" ADD CONSTRAINT "parent_id" FOREIGN KEY ("parent_id") REFERENCES "public"."comment"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "schedule" ADD CONSTRAINT "schedule_user_fk" FOREIGN KEY ("owner") REFERENCES "public"."account"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "participant" ADD CONSTRAINT "schedule_id" FOREIGN KEY ("schedule_id") REFERENCES "public"."schedule"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "participant" ADD CONSTRAINT "participant_id" FOREIGN KEY ("participant_id") REFERENCES "public"."account"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "favorite" ADD CONSTRAINT "schedule_id" FOREIGN KEY ("schedule_id") REFERENCES "public"."schedule"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "favorite" ADD CONSTRAINT "user_id" FOREIGN KEY ("user_id") REFERENCES "public"."account"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "follow" ADD CONSTRAINT "follower_id" FOREIGN KEY ("follower") REFERENCES "public"."account"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "follow" ADD CONSTRAINT "following_id" FOREIGN KEY ("following") REFERENCES "public"."account"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +CREATE INDEX "schedule_enddatetime_idx" ON "schedule" USING btree ("end_at" date_ops);--> statement-breakpoint +CREATE INDEX "schedule_name_idx" ON "schedule" USING btree ("name" text_ops,"content" text_ops);--> statement-breakpoint +CREATE INDEX "schedule_startdatetime_idx" ON "schedule" USING btree ("start_at" date_ops);--> statement-breakpoint +CREATE INDEX "schedule_status_idx" ON "schedule" USING btree ("status" text_ops);--> statement-breakpoint +CREATE INDEX "schedule_type_idx" ON "schedule" USING btree ("type" text_ops);--> statement-breakpoint +CREATE INDEX "participant_participant_id_idx" ON "participant" USING btree ("participant_id" uuid_ops);--> statement-breakpoint +CREATE INDEX "participant_schedule_id_idx" ON "participant" USING btree ("schedule_id" uuid_ops); +*/ \ No newline at end of file diff --git a/drizzle/meta/0000_snapshot.json b/drizzle/meta/0000_snapshot.json new file mode 100644 index 0000000..155cb5e --- /dev/null +++ b/drizzle/meta/0000_snapshot.json @@ -0,0 +1,613 @@ +{ + "id": "00000000-0000-0000-0000-000000000000", + "prevId": "", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.comment": { + "name": "comment", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "is_deleted": { + "name": "is_deleted", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "writer_id": { + "name": "writer_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "parent_id": { + "name": "parent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "writer_id": { + "name": "writer_id", + "tableFrom": "comment", + "tableTo": "account", + "schemaTo": "public", + "columnsFrom": [ + "writer_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "parent_id": { + "name": "parent_id", + "tableFrom": "comment", + "tableTo": "comment", + "schemaTo": "public", + "columnsFrom": [ + "parent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {}, + "policies": {}, + "isRLSEnabled": false + }, + "public.schedule": { + "name": "schedule", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "start_at": { + "name": "start_at", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "end_at": { + "name": "end_at", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_deleted": { + "name": "is_deleted", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "type": { + "name": "type", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "schedule_enddatetime_idx": { + "name": "schedule_enddatetime_idx", + "columns": [ + { + "expression": "end_at", + "asc": true, + "nulls": "last", + "opclass": "date_ops", + "isExpression": false + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "schedule_name_idx": { + "name": "schedule_name_idx", + "columns": [ + { + "expression": "name", + "asc": true, + "nulls": "last", + "opclass": "text_ops", + "isExpression": false + }, + { + "expression": "content", + "asc": true, + "nulls": "last", + "opclass": "text_ops", + "isExpression": false + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "schedule_startdatetime_idx": { + "name": "schedule_startdatetime_idx", + "columns": [ + { + "expression": "start_at", + "asc": true, + "nulls": "last", + "opclass": "date_ops", + "isExpression": false + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "schedule_status_idx": { + "name": "schedule_status_idx", + "columns": [ + { + "expression": "status", + "asc": true, + "nulls": "last", + "opclass": "text_ops", + "isExpression": false + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "schedule_type_idx": { + "name": "schedule_type_idx", + "columns": [ + { + "expression": "type", + "asc": true, + "nulls": "last", + "opclass": "text_ops", + "isExpression": false + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "schedule_user_fk": { + "name": "schedule_user_fk", + "tableFrom": "schedule", + "tableTo": "account", + "schemaTo": "public", + "columnsFrom": [ + "owner" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {}, + "policies": {}, + "isRLSEnabled": false + }, + "public.account": { + "name": "account", + "schema": "", + "columns": { + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "birthday": { + "name": "birthday", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "account_id": { + "name": "account_id", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "nickname": { + "name": "nickname", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "varchar", + "primaryKey": false, + "notNull": true, + "default": "'wait'" + }, + "is_deleted": { + "name": "is_deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "date", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v4()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {}, + "policies": {}, + "isRLSEnabled": false + }, + "public.participant": { + "name": "participant", + "schema": "", + "columns": { + "participant_id": { + "name": "participant_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "schedule_id": { + "name": "schedule_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "is_deleted": { + "name": "is_deleted", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + } + }, + "indexes": { + "participant_participant_id_idx": { + "name": "participant_participant_id_idx", + "columns": [ + { + "expression": "participant_id", + "asc": true, + "nulls": "last", + "opclass": "uuid_ops", + "isExpression": false + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "participant_schedule_id_idx": { + "name": "participant_schedule_id_idx", + "columns": [ + { + "expression": "schedule_id", + "asc": true, + "nulls": "last", + "opclass": "uuid_ops", + "isExpression": false + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "schedule_id": { + "name": "schedule_id", + "tableFrom": "participant", + "tableTo": "schedule", + "schemaTo": "public", + "columnsFrom": [ + "schedule_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "participant_id": { + "name": "participant_id", + "tableFrom": "participant", + "tableTo": "account", + "schemaTo": "public", + "columnsFrom": [ + "participant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {}, + "policies": {}, + "isRLSEnabled": false + }, + "public.favorite": { + "name": "favorite", + "schema": "", + "columns": { + "is_deleted": { + "name": "is_deleted", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "schedule_id": { + "name": "schedule_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "schedule_id": { + "name": "schedule_id", + "tableFrom": "favorite", + "tableTo": "schedule", + "schemaTo": "public", + "columnsFrom": [ + "schedule_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "user_id": { + "name": "user_id", + "tableFrom": "favorite", + "tableTo": "account", + "schemaTo": "public", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "favorite_pk": { + "name": "favorite_pk", + "columns": [ + "user_id", + "schedule_id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraints": {}, + "policies": {}, + "isRLSEnabled": false + }, + "public.follow": { + "name": "follow", + "schema": "", + "columns": { + "is_deleted": { + "name": "is_deleted", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "is_accepted": { + "name": "is_accepted", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "is_linked": { + "name": "is_linked", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "following": { + "name": "following", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "follower": { + "name": "follower", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "follower_id": { + "name": "follower_id", + "tableFrom": "follow", + "tableTo": "account", + "schemaTo": "public", + "columnsFrom": [ + "follower" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "following_id": { + "name": "following_id", + "tableFrom": "follow", + "tableTo": "account", + "schemaTo": "public", + "columnsFrom": [ + "following" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "follow_pk": { + "name": "follow_pk", + "columns": [ + "following", + "follower" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraints": {}, + "policies": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json new file mode 100644 index 0000000..527279b --- /dev/null +++ b/drizzle/meta/_journal.json @@ -0,0 +1,13 @@ +{ + "version": "7", + "dialect": "postgresql", + "entries": [ + { + "idx": 0, + "version": "7", + "when": 1763702111620, + "tag": "0000_parallel_rocket_racer", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/drizzle/relations.ts b/drizzle/relations.ts new file mode 100644 index 0000000..0087d8d --- /dev/null +++ b/drizzle/relations.ts @@ -0,0 +1,74 @@ +import { relations } from "drizzle-orm/relations"; +import { account, comment, schedule, participant, favorite, follow } from "./schema"; + +export const commentRelations = relations(comment, ({one, many}) => ({ + account: one(account, { + fields: [comment.writerId], + references: [account.id] + }), + comment: one(comment, { + fields: [comment.parentId], + references: [comment.id], + relationName: "comment_parentId_comment_id" + }), + comments: many(comment, { + relationName: "comment_parentId_comment_id" + }), +})); + +export const accountRelations = relations(account, ({many}) => ({ + comments: many(comment), + schedules: many(schedule), + participants: many(participant), + favorites: many(favorite), + follows_follower: many(follow, { + relationName: "follow_follower_account_id" + }), + follows_following: many(follow, { + relationName: "follow_following_account_id" + }), +})); + +export const scheduleRelations = relations(schedule, ({one, many}) => ({ + account: one(account, { + fields: [schedule.owner], + references: [account.id] + }), + participants: many(participant), + favorites: many(favorite), +})); + +export const participantRelations = relations(participant, ({one}) => ({ + schedule: one(schedule, { + fields: [participant.scheduleId], + references: [schedule.id] + }), + account: one(account, { + fields: [participant.participantId], + references: [account.id] + }), +})); + +export const favoriteRelations = relations(favorite, ({one}) => ({ + schedule: one(schedule, { + fields: [favorite.scheduleId], + references: [schedule.id] + }), + account: one(account, { + fields: [favorite.userId], + references: [account.id] + }), +})); + +export const followRelations = relations(follow, ({one}) => ({ + account_follower: one(account, { + fields: [follow.follower], + references: [account.id], + relationName: "follow_follower_account_id" + }), + account_following: one(account, { + fields: [follow.following], + references: [account.id], + relationName: "follow_following_account_id" + }), +})); \ No newline at end of file diff --git a/drizzle/schema.ts b/drizzle/schema.ts new file mode 100644 index 0000000..b2ca33a --- /dev/null +++ b/drizzle/schema.ts @@ -0,0 +1,120 @@ +import { pgTable, foreignKey, uuid, text, date, boolean, index, varchar, primaryKey } from "drizzle-orm/pg-core" +import { sql } from "drizzle-orm" + + + +export const comment = pgTable("comment", { + id: uuid().primaryKey().notNull(), + content: text(), + createdAt: date("created_at"), + isDeleted: boolean("is_deleted").default(false), + writerId: uuid("writer_id"), + parentId: uuid("parent_id"), +}, (table) => [ + foreignKey({ + columns: [table.writerId], + foreignColumns: [account.id], + name: "writer_id" + }), + foreignKey({ + columns: [table.parentId], + foreignColumns: [table.id], + name: "parent_id" + }), +]); + +export const schedule = pgTable("schedule", { + id: uuid().primaryKey().notNull(), + name: varchar(), + startAt: date("start_at"), + endAt: date("end_at"), + status: varchar(), + content: text(), + isDeleted: boolean("is_deleted").default(false), + type: varchar(), + createdAt: date("created_at"), + owner: uuid(), +}, (table) => [ + index("schedule_enddatetime_idx").using("btree", table.endAt.asc().nullsLast().op("date_ops")), + index("schedule_name_idx").using("btree", table.name.asc().nullsLast().op("text_ops"), table.content.asc().nullsLast().op("text_ops")), + index("schedule_startdatetime_idx").using("btree", table.startAt.asc().nullsLast().op("date_ops")), + index("schedule_status_idx").using("btree", table.status.asc().nullsLast().op("text_ops")), + index("schedule_type_idx").using("btree", table.type.asc().nullsLast().op("text_ops")), + foreignKey({ + columns: [table.owner], + foreignColumns: [account.id], + name: "schedule_user_fk" + }), +]); + +export const account = pgTable("account", { + name: varchar().notNull(), + email: varchar().notNull(), + password: varchar().notNull(), + birthday: date(), + accountId: varchar("account_id").notNull(), + nickname: varchar().notNull(), + status: varchar().default('wait').notNull(), + isDeleted: boolean("is_deleted").default(false).notNull(), + createdAt: date("created_at").defaultNow().notNull(), + id: uuid().default(sql`uuid_generate_v4()`).primaryKey().notNull(), +}); + +export const participant = pgTable("participant", { + participantId: uuid("participant_id").notNull(), + scheduleId: uuid("schedule_id").notNull(), + isDeleted: boolean("is_deleted").default(false), +}, (table) => [ + index("participant_participant_id_idx").using("btree", table.participantId.asc().nullsLast().op("uuid_ops")), + index("participant_schedule_id_idx").using("btree", table.scheduleId.asc().nullsLast().op("uuid_ops")), + foreignKey({ + columns: [table.scheduleId], + foreignColumns: [schedule.id], + name: "schedule_id" + }), + foreignKey({ + columns: [table.participantId], + foreignColumns: [account.id], + name: "participant_id" + }), +]); + +export const favorite = pgTable("favorite", { + isDeleted: boolean("is_deleted").default(false), + createdAt: date("created_at"), + userId: uuid("user_id").notNull(), + scheduleId: uuid("schedule_id").notNull(), +}, (table) => [ + foreignKey({ + columns: [table.scheduleId], + foreignColumns: [schedule.id], + name: "schedule_id" + }), + foreignKey({ + columns: [table.userId], + foreignColumns: [account.id], + name: "user_id" + }), + primaryKey({ columns: [table.userId, table.scheduleId], name: "favorite_pk"}), +]); + +export const follow = pgTable("follow", { + isDeleted: boolean("is_deleted").default(false), + isAccepted: boolean("is_accepted").default(false), + isLinked: boolean("is_linked").default(false), + createdAt: date("created_at"), + following: uuid().notNull(), + follower: uuid().notNull(), +}, (table) => [ + foreignKey({ + columns: [table.follower], + foreignColumns: [account.id], + name: "follower_id" + }), + foreignKey({ + columns: [table.following], + foreignColumns: [account.id], + name: "following_id" + }), + primaryKey({ columns: [table.following, table.follower], name: "follow_pk"}), +]); diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..4e9f827 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,35 @@ +// @ts-check +import eslint from '@eslint/js'; +import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; +import globals from 'globals'; +import tseslint from 'typescript-eslint'; + +export default tseslint.config( + { + ignores: ['eslint.config.mjs'], + }, + eslint.configs.recommended, + ...tseslint.configs.recommendedTypeChecked, + eslintPluginPrettierRecommended, + { + languageOptions: { + globals: { + ...globals.node, + ...globals.jest, + }, + sourceType: 'commonjs', + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + }, + { + rules: { + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-floating-promises': 'warn', + '@typescript-eslint/no-unsafe-argument': 'warn', + "prettier/prettier": ["error", { endOfLine: "auto" }], + }, + }, +); diff --git a/nest-cli.json b/nest-cli.json new file mode 100644 index 0000000..f9aa683 --- /dev/null +++ b/nest-cli.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://json.schemastore.org/nest-cli", + "collection": "@nestjs/schematics", + "sourceRoot": "src", + "compilerOptions": { + "deleteOutDir": true + } +} diff --git a/package.json b/package.json index 2d59807..8bda7f1 100644 --- a/package.json +++ b/package.json @@ -1,32 +1,80 @@ { - "name": "fastify", - "version": "1.0.0", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "dev": "ts-node src/server.ts", - "build": "tsc", - "start": "node dist/server.js" - }, - "keywords": [], - "author": "", - "license": "ISC", + "name": "back", + "version": "0.0.1", "description": "", + "author": "", + "private": true, + "license": "UNLICENSED", + "scripts": { + "build": "nest build", + "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", + "start": "nest start", + "start:dev": "nest start --watch", + "start:debug": "nest start --debug --watch", + "start:prod": "node dist/main", + "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", + "test": "jest", + "test:watch": "jest --watch", + "test:cov": "jest --coverage", + "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", + "test:e2e": "jest --config ./test/jest-e2e.json" + }, "dependencies": { - "@fastify/swagger": "^9.6.1", - "@fastify/swagger-ui": "^5.2.3", + "@nestjs/class-transformer": "^0.4.0", + "@nestjs/class-validator": "^0.13.4", + "@nestjs/common": "^11.0.1", + "@nestjs/core": "^11.0.1", + "@nestjs/platform-express": "^11.0.1", "dotenv": "^17.2.3", - "fastify": "^5.6.2", - "fastify-postgres": "^3.6.0", - "nodemailer": "^7.0.10", - "pg": "^8.16.3" + "drizzle-kit": "^0.31.7", + "drizzle-orm": "^0.44.7", + "ioredis": "^5.8.2", + "pg": "^8.16.3", + "reflect-metadata": "^0.2.2", + "rxjs": "^7.8.1" }, "devDependencies": { - "@types/dotenv": "^6.1.1", - "@types/node": "^24.10.1", - "@types/nodemailer": "^7.0.3", + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "^9.18.0", + "@nestjs/cli": "^11.0.0", + "@nestjs/schematics": "^11.0.0", + "@nestjs/testing": "^11.0.1", + "@types/express": "^5.0.0", + "@types/ioredis": "^5.0.0", + "@types/jest": "^30.0.0", + "@types/node": "^22.10.7", "@types/pg": "^8.15.6", + "@types/supertest": "^6.0.2", + "eslint": "^9.18.0", + "eslint-config-prettier": "^10.0.1", + "eslint-plugin-prettier": "^5.2.2", + "globals": "^16.0.0", + "jest": "^30.0.0", + "prettier": "^3.4.2", + "source-map-support": "^0.5.21", + "supertest": "^7.0.0", + "ts-jest": "^29.2.5", + "ts-loader": "^9.5.2", "ts-node": "^10.9.2", - "typescript": "^5.9.3" + "tsconfig-paths": "^4.2.0", + "typescript": "^5.7.3", + "typescript-eslint": "^8.20.0" + }, + "jest": { + "moduleFileExtensions": [ + "js", + "json", + "ts" + ], + "rootDir": "src", + "testRegex": ".*\\.spec\\.ts$", + "transform": { + "^.+\\.(t|j)s$": "ts-jest" + }, + "collectCoverageFrom": [ + "**/*.(t|j)s" + ], + "coverageDirectory": "../coverage", + "testEnvironment": "node" } } diff --git a/src/app.controller.spec.ts b/src/app.controller.spec.ts new file mode 100644 index 0000000..d22f389 --- /dev/null +++ b/src/app.controller.spec.ts @@ -0,0 +1,22 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { AppController } from './app.controller'; +import { AppService } from './app.service'; + +describe('AppController', () => { + let appController: AppController; + + beforeEach(async () => { + const app: TestingModule = await Test.createTestingModule({ + controllers: [AppController], + providers: [AppService], + }).compile(); + + appController = app.get(AppController); + }); + + describe('root', () => { + it('should return "Hello World!"', () => { + expect(appController.getHello()).toBe('Hello World!'); + }); + }); +}); diff --git a/src/app.controller.ts b/src/app.controller.ts new file mode 100644 index 0000000..cce879e --- /dev/null +++ b/src/app.controller.ts @@ -0,0 +1,12 @@ +import { Controller, Get } from '@nestjs/common'; +import { AppService } from './app.service'; + +@Controller() +export class AppController { + constructor(private readonly appService: AppService) {} + + @Get() + getHello(): string { + return this.appService.getHello(); + } +} diff --git a/src/app.module.ts b/src/app.module.ts new file mode 100644 index 0000000..5e2e0e7 --- /dev/null +++ b/src/app.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { AppController } from './app.controller'; +import { AppService } from './app.service'; +import { DbModule } from './db/db.module'; +import { RedisModule } from './redis/redis.module'; + +@Module({ + imports: [DbModule, RedisModule], + controllers: [AppController], + providers: [AppService], +}) +export class AppModule {} diff --git a/src/app.service.ts b/src/app.service.ts new file mode 100644 index 0000000..927d7cc --- /dev/null +++ b/src/app.service.ts @@ -0,0 +1,8 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class AppService { + getHello(): string { + return 'Hello World!'; + } +} diff --git a/src/app.ts b/src/app.ts deleted file mode 100644 index 2e2c91c..0000000 --- a/src/app.ts +++ /dev/null @@ -1,17 +0,0 @@ -import Fastify from 'fastify'; -import fastifyPostgres from 'fastify-postgres'; -import { registerSwagger } from './utils/plugin/swagger'; -import dotenv from 'dotenv'; - -dotenv.config(); - -export async function buildApp() { - - const app = Fastify(); - await app.register(fastifyPostgres, { - connectionString: `postgresql://${process.env.PGUSER}:${process.env.PGPASSWORD}@${process.env.PGHOST}:${process.env.PGPORT}/${process.env.PGDATABASE}` - }) - await registerSwagger(app); - - return app; -} \ No newline at end of file diff --git a/src/db/db.module.ts b/src/db/db.module.ts new file mode 100644 index 0000000..a1f0703 --- /dev/null +++ b/src/db/db.module.ts @@ -0,0 +1,22 @@ +import { Global, Module } from "@nestjs/common"; +import { Pool } from "pg"; +import { drizzle, NodePgDatabase } from "drizzle-orm/node-postgres"; +import * as schema from '../../drizzle/schema'; + +@Global() +@Module({ + providers: [ + { + provide: "DRIZZLE", + useFactory: (): NodePgDatabase => { + const pool = new Pool({ + connectionString: process.env.PG_DATABASE_URL + }); + + return drizzle(pool, { schema: schema }); + } + } + ], + exports: ["DRIZZLE"] +}) +export class DbModule {} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..f76bc8d --- /dev/null +++ b/src/main.ts @@ -0,0 +1,8 @@ +import { NestFactory } from '@nestjs/core'; +import { AppModule } from './app.module'; + +async function bootstrap() { + const app = await NestFactory.create(AppModule); + await app.listen(process.env.PORT ?? 3000); +} +bootstrap(); diff --git a/src/modules/account/account.controller.ts b/src/modules/account/account.controller.ts new file mode 100644 index 0000000..90e654e --- /dev/null +++ b/src/modules/account/account.controller.ts @@ -0,0 +1,14 @@ +import { Controller, Get, Post, Query } from "@nestjs/common"; +import { CheckDuplicationRequestDto } from "./dto/checkDuplication/check-duplication-request.dto"; +import { CheckDuplicationResponseDto } from "./dto/checkDuplication/check-duplication-response.dto"; +import { AccountService } from "./account.service"; + +@Controller('account') +export class AccountController { + constructor(private readonly accountService: AccountService) {} + + @Get('check-duplication') + async checkDuplication(@Query() query: CheckDuplicationRequestDto): Promise { + return this.accountService.checkDuplication(query); + } +} \ No newline at end of file diff --git a/src/modules/account/account.module.ts b/src/modules/account/account.module.ts new file mode 100644 index 0000000..eefd14a --- /dev/null +++ b/src/modules/account/account.module.ts @@ -0,0 +1,10 @@ +import { Module } from "@nestjs/common"; +import { AccountController } from "./account.controller"; +import { AccountRepo } from "./account.repo"; +import { AccountService } from "./account.service"; + +@Module({ + controllers: [AccountController], + providers: [AccountService, AccountRepo] +}) +export class AccountModule {} \ No newline at end of file diff --git a/src/modules/account/account.repo.ts b/src/modules/account/account.repo.ts new file mode 100644 index 0000000..ad7b06b --- /dev/null +++ b/src/modules/account/account.repo.ts @@ -0,0 +1,21 @@ +import { Inject, Injectable } from "@nestjs/common"; +import * as schema from "drizzle/schema"; +import { countDistinct, and, eq } from 'drizzle-orm'; +import { NodePgDatabase } from "drizzle-orm/node-postgres"; + +@Injectable() +export class AccountRepo { + constructor(@Inject('DRIZZLE') private readonly db: NodePgDatabase) {} + + async checkDuplication(type: 'email' | 'accountId', value: string) { + const result = await this + .db + .select({ count: countDistinct(schema.account[type])}) + .from(schema.account) + .where( + eq(schema.account[type], value) + ); + + return result[0].count; + } +} \ No newline at end of file diff --git a/src/modules/account/account.service.ts b/src/modules/account/account.service.ts new file mode 100644 index 0000000..f3676c3 --- /dev/null +++ b/src/modules/account/account.service.ts @@ -0,0 +1,15 @@ +import { Injectable } from "@nestjs/common"; +import { AccountRepo } from "./account.repo"; +import { CheckDuplicationRequestDto } from "./dto/checkDuplication/check-duplication-request.dto"; +import { CheckDuplicationResponseDto } from "./dto/checkDuplication/check-duplication-response.dto"; + +@Injectable() +export class AccountService { + constructor(private readonly accountRepo: AccountRepo) {} + + async checkDuplication(data: CheckDuplicationRequestDto): Promise { + const count = await this.accountRepo.checkDuplication(data.type, data.value); + + return { isDuplicated: count > 0 }; + } +} \ No newline at end of file diff --git a/src/modules/account/dto/checkDuplication/check-duplication-request.dto.ts b/src/modules/account/dto/checkDuplication/check-duplication-request.dto.ts new file mode 100644 index 0000000..e2530c5 --- /dev/null +++ b/src/modules/account/dto/checkDuplication/check-duplication-request.dto.ts @@ -0,0 +1,9 @@ +import { IsString, IsIn } from '@nestjs/class-validator' + +export class CheckDuplicationRequestDto { + @IsIn(['email', 'accountId']) + type: 'email' | 'accountId'; + + @IsString() + value: string; +} \ No newline at end of file diff --git a/src/modules/account/dto/checkDuplication/check-duplication-response.dto.ts b/src/modules/account/dto/checkDuplication/check-duplication-response.dto.ts new file mode 100644 index 0000000..63085b3 --- /dev/null +++ b/src/modules/account/dto/checkDuplication/check-duplication-response.dto.ts @@ -0,0 +1,3 @@ +export class CheckDuplicationResponseDto { + isDuplicated: boolean; +} \ No newline at end of file diff --git a/src/redis/redis.module.ts b/src/redis/redis.module.ts new file mode 100644 index 0000000..6d2c99f --- /dev/null +++ b/src/redis/redis.module.ts @@ -0,0 +1,19 @@ +import { Global, Module } from "@nestjs/common"; +import Redis from "ioredis"; + +@Global() +@Module({ + providers: [ + { + provide: "REDIS", + useFactory: () => { + return new Redis({ + host: process.env.RD_HOST!, + port: Number(process.env.RD_PORT || 6779) + }); + } + }, + ], + exports: ["REDIS"] +}) +export class RedisModule{} \ No newline at end of file diff --git a/src/route/user.route.ts b/src/route/user.route.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/server.ts b/src/server.ts deleted file mode 100644 index 441535c..0000000 --- a/src/server.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { buildApp } from './app'; - -const start = async () => { - try { - const app = await buildApp(); - await app.listen({ port: 3000 }); - console.log(`Server running on port ${process.env.PORT}`); - } catch (err) { - console.error(err); - process.exit(1); - } -} - -start(); \ No newline at end of file diff --git a/src/utils/plugin/swagger.ts b/src/utils/plugin/swagger.ts deleted file mode 100644 index 56167a6..0000000 --- a/src/utils/plugin/swagger.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { FastifyInstance } from "fastify"; -import fastifySwagger from "@fastify/swagger"; -import fastifySwaggerUi from "@fastify/swagger-ui"; - -export async function registerSwagger(fastify: FastifyInstance) { - await fastify.register(fastifySwagger, { - openapi: { - info: { - title: "API Docs", - description: "API documentation for Fastify server", - version: "1.0.0" - }, - servers: [ - { - url: "http://localhost:3000", - description: "Local Development", - }, - { - url: "https://api.scheduler.bkdhome.p-e.kr", - description: "Production Server" - } - ] - } - }); - - await fastify.register(fastifySwaggerUi, { - routePrefix: "/docs", - uiConfig: { - docExpansion: "none", - deepLinking: false - } - }); -} \ No newline at end of file diff --git a/test/app.e2e-spec.ts b/test/app.e2e-spec.ts new file mode 100644 index 0000000..36852c5 --- /dev/null +++ b/test/app.e2e-spec.ts @@ -0,0 +1,25 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { INestApplication } from '@nestjs/common'; +import request from 'supertest'; +import { App } from 'supertest/types'; +import { AppModule } from './../src/app.module'; + +describe('AppController (e2e)', () => { + let app: INestApplication; + + beforeEach(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [AppModule], + }).compile(); + + app = moduleFixture.createNestApplication(); + await app.init(); + }); + + it('/ (GET)', () => { + return request(app.getHttpServer()) + .get('/') + .expect(200) + .expect('Hello World!'); + }); +}); diff --git a/test/jest-e2e.json b/test/jest-e2e.json new file mode 100644 index 0000000..e9d912f --- /dev/null +++ b/test/jest-e2e.json @@ -0,0 +1,9 @@ +{ + "moduleFileExtensions": ["js", "json", "ts"], + "rootDir": ".", + "testEnvironment": "node", + "testRegex": ".e2e-spec.ts$", + "transform": { + "^.+\\.(t|j)s$": "ts-jest" + } +} diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 0000000..64f86c6 --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] +} diff --git a/tsconfig.json b/tsconfig.json index a2bec16..89eb88f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,19 +1,25 @@ { "compilerOptions": { - "target": "esnext", - "lib": ["ESNext"], - "outDir": "dist", - "rootDir": "src", - "strict": true, + "module": "nodenext", + "moduleResolution": "nodenext", + "resolvePackageJsonExports": true, "esModuleInterop": true, - "module": "NodeNext", - "moduleResolution": "NodeNext", - "resolveJsonModule": true, - "forceConsistentCasingInFileNames": true, - "skipLibCheck": true, - // "noEmit": true, + "isolatedModules": true, + "declaration": true, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, "allowSyntheticDefaultImports": true, - }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist"] -} \ No newline at end of file + "target": "ES2023", + "sourceMap": true, + "outDir": "./dist", + "baseUrl": "./", + "incremental": true, + "skipLibCheck": true, + "strictNullChecks": true, + "forceConsistentCasingInFileNames": true, + "noImplicitAny": false, + "strictBindCallApply": false, + "noFallthroughCasesInSwitch": false + } +}