Просмотр исходного кода

增加登录相关接口,增加创建文档接口,修复一些bug

zhusiqing 4 лет назад
Родитель
Сommit
f26ad94764

+ 15 - 7
.vscode/launch.json

@@ -5,13 +5,21 @@
   "version": "0.2.0",
   "configurations": [
     {
-      "type": "node",
+      "command": "npm run dev",
+      "name": "Run npm start",
       "request": "launch",
-      "name": "启动程序",
-      "skipFiles": [
-        "<node_internals>/**"
-      ],
-      "program": "${workspaceFolder}/app.js"
-    }
+      "type": "node-terminal"
+    },
+    // {
+    //   "type": "node",
+    //   "request": "launch",
+    //   "name": "启动程序",
+    //   "skipFiles": [
+    //     "<node_internals>/**"
+    //   ],
+    //   // "program": "${workspaceFolder}/src/app.ts"
+    //   "program": "${workspaceFolder}/node_modules/.bin/cross-env",
+    //   "args": ["NODE_ENV=dev nodemon -e ts --exec ts-node -r tsconfig-paths/register ${workspaceFolder}/src/app.ts"]
+    // }
   ]
 }

+ 2 - 1
package.json

@@ -11,7 +11,7 @@
   "main": "index.js",
   "license": "MIT",
   "scripts": {
-    "dev": "cross-env NODE_ENV=dev nodemon -e ts --exec ts-node src/app.ts",
+    "dev": "cross-env NODE_ENV=dev nodemon -e ts --exec ts-node -r tsconfig-paths/register src/app.ts",
     "start": "rm -rf ./dist && cross-env NODE_ENV=prod tsc --inlineSourceMap false"
   },
   "dependencies": {
@@ -40,6 +40,7 @@
     "@types/node": "^14.11.2",
     "@types/redis": "^2.8.27",
     "ts-node": "^9.0.0",
+    "tsconfig-paths": "^3.9.0",
     "typescript": "^4.0.3"
   }
 }

+ 2 - 2
src/app.ts

@@ -12,7 +12,7 @@ import helmet from 'koa-helmet';
 import koaStatic from 'koa-static';
 // 请求转发
 import koaProxy from 'koa-proxies';
-import config from './config';
+import config from '@config';
 import redisMiddleware from './middlewares/redis';
 import limitMiddleware from './middlewares/limit';
 import logsMiddleware from './middlewares/logs';
@@ -21,7 +21,7 @@ import sessionMiddleware from './middlewares/session';
 // import authMiddleware from './middlewares/auth';
 import localDbMiddleware from './middlewares/localDb';
 
-import { loggerInstance } from './utils/logger';
+import { loggerInstance } from '@utils/logger';
 
 const app = new Koa<DefaultState, Context>();
 

+ 2 - 1
src/config.ts

@@ -28,5 +28,6 @@ export default {
     dir: resolve(__dirname, '../db'),
     user: 'dbUser.json',
     doc: 'dbDoc.json'
-  }
+  },
+  secrestKey: 'liaomo'
 };

+ 5 - 1
src/controllers/document.ts

@@ -2,7 +2,7 @@ import { Context } from 'koa';
 import services from '../services';
 
 export interface InterfaceDocument {
-  method: string
+  method: number
   title: string
   url: string
   requestList: InterfaceRequestList[]
@@ -33,6 +33,10 @@ export const putDocument = async (ctx: Context) => {
   ctx.body = ctx.$response(data.data, data.message, data.success, data.code)
 };
 
+export const getDocument = async (ctx:Context) => {
+  const { id } = ctx.query
+}
+
 export default {
   putDocument
 }

+ 2 - 50
src/controllers/index.ts

@@ -1,9 +1,7 @@
 import { Context } from 'koa';
+import user from './user';
 import document from './document';
 import services from '../services';
-interface sessionInterface {
-  user?: string
-}
 
 const home = async (ctx: Context) => {
   ctx.body = 'home';
@@ -25,55 +23,9 @@ const code = async (ctx: Context) => {
   ctx.body = ctx.$response(body);
 };
 
-const login = async (ctx: Context) => {
-  const { username, password } = ctx.request.body
-  if (!username) {
-    ctx.body = ctx.$response(null, '用户名不能为空', false, 400000)
-    return
-  }
-  if (!password) {
-    ctx.body = ctx.$response(null, '密码不能为空', false, 400000)
-    return
-  }
-  const dbInfo = {
-    username: 'zhusiqing',
-    password: '123456'
-  }
-  if (username === dbInfo.username && password === dbInfo.password) {
-    if (ctx.session) {
-      if (ctx.session.user) {
-        ctx.body = ctx.$response(null, '用户已登录')
-        return
-      }
-      ctx.session.user = username
-    }
-    ctx.body = ctx.$response({ username }, '登录成功')
-  } else {
-    ctx.body = ctx.$response(null, '用户名或者密码错误', false, 400000)
-  }
-}
-
-const logout = async (ctx: Context) => {
-  if (ctx.session) {
-    ctx.session.user = null
-  }
-  ctx.body = ctx.$response(null, '退出成功')
-}
-
-const userInfo = async (ctx: Context) => {
-  if (!ctx.session || !ctx.session.user ) {
-    ctx.body = ctx.$response(null, '未登录', false, 400000)
-    return
-  }
-  const session: sessionInterface = ctx.session.toJSON()
-  ctx.body = ctx.$response({ username: session.user })
-}
-
 export default {
   home,
   code,
-  login,
-  logout,
-  userInfo,
+  user,
   document
 }

+ 71 - 0
src/controllers/user.ts

@@ -0,0 +1,71 @@
+import { Context } from 'koa';
+import services from '../services';
+import CODE from '../utils/code';
+
+interface sessionInterface {
+  user?: string
+}
+
+export interface InterfaceRegisterParams {
+  username: string
+  password: string
+}
+
+const register = async (ctx: Context) => {
+  const { username, password } = ctx.request.body
+  if (!username) {
+    ctx.body = ctx.$response(null, '用户名不能为空', false, CODE.MISS_PARAMS)
+    return
+  }
+  if (!password) {
+    ctx.body = ctx.$response(null, '密码不能为空', false, CODE.MISS_PARAMS)
+    return
+  }
+  const params: InterfaceRegisterParams = {
+    username,
+    password
+  }
+  const data = await services.user.register(ctx, params)
+  ctx.body = ctx.$response(data.data, data.message, data.success, data.code)
+}
+
+const login = async (ctx: Context) => {
+  const { username, password } = ctx.request.body
+  if (!username) {
+    ctx.body = ctx.$response(null, '用户名不能为空', false, CODE.MISS_PARAMS)
+    return
+  }
+  if (!password) {
+    ctx.body = ctx.$response(null, '密码不能为空', false, CODE.MISS_PARAMS)
+    return
+  }
+  const params: InterfaceRegisterParams = {
+    username,
+    password
+  }
+  const data = await services.user.login(ctx, params)
+  ctx.body = ctx.$response(data.data, data.message, data.success, data.code)
+}
+
+const logout = async (ctx: Context) => {
+  if (ctx.session) {
+    ctx.session.user = null
+  }
+  ctx.body = ctx.$response(null, '退出成功')
+}
+
+const userInfo = async (ctx: Context) => {
+  if (!ctx.session || !ctx.session.user ) {
+    ctx.body = ctx.$response(null, '未登录', false, CODE.NOT_LOGIN)
+    return
+  }
+  const session: sessionInterface = ctx.session.toJSON()
+  ctx.body = ctx.$response(session.user)
+}
+
+export default {
+  register,
+  login,
+  logout,
+  userInfo
+}

+ 3 - 2
src/middlewares/auth.ts

@@ -1,8 +1,9 @@
 import { Context, Next } from 'koa';
+import CODE from '@utils/code';
 interface sessionInterface {
   user?: string
 }
-const noAuths = ['/api/login']
+const noAuths = ['/api/login', '/api/register']
 export default () => {
   return async (ctx: Context, next: Next) => {
     if (noAuths.includes(ctx.url)) {
@@ -13,7 +14,7 @@ export default () => {
       const session: sessionInterface = ctx.session.toJSON()
       if (!session.user) {
         ctx.session.user = null
-        ctx.body = ctx.$response(null, '未登录', false, 400000)
+        ctx.body = ctx.$response(null, '未登录', false, CODE.NOT_LOGIN)
         return
       }
       await next()

+ 2 - 2
src/middlewares/limit.ts

@@ -1,16 +1,16 @@
 import { Context, Next } from 'koa';
-import config from '../config';
+import config from '@config';
 export default () => {
   return async (ctx: Context, next: Next) => {
     const host: string = ctx.host
     const isMatch = config.checkHost.find(el => host.match(el))
-    console.log(host, isMatch);
     if (!isMatch) {
       // 域名不对
       ctx.throw(403, '请求出错')
     }
     const ip: string = ctx.ip;
     const key: string = `client_ip_limit_${ip}`;
+    const a = await ctx.$redis.get(key)
     const limit: number = Number(await ctx.$redis.get(key)) || 0;
     if (limit > config.limit.times) {
       ctx.throw(403, '请求频次过高');

+ 1 - 1
src/middlewares/localDb.ts

@@ -1,6 +1,6 @@
 import Nedb from 'nedb';
 import { resolve } from 'path';
-import config from '../config';
+import config from '@config';
 
 export interface InterfaceDB {
   user: Nedb

+ 1 - 1
src/middlewares/logs.ts

@@ -1,5 +1,5 @@
 import { Context, Next } from 'koa';
-import { middlewareLog } from '../utils/logger';
+import { middlewareLog } from '@utils/logger';
 
 export default () => {
   const loggerMiddleware = middlewareLog();

+ 9 - 8
src/middlewares/redis.ts

@@ -3,7 +3,7 @@ import redis from 'redis';
 import nodeCache from 'node-cache';
 import util from 'util';
 import consola from 'consola/dist/consola';
-import config from '../config';
+import config from '@config';
 
 // 是否使用redis
 let isUseRedis: boolean = true
@@ -56,18 +56,19 @@ client.on('end', () => {
 });
 
 // redis查询和设置await/async化
-const promisify = util.promisify
-const getClient = promisify(client.get).bind(client);
-const setClient = promisify(client.set).bind(client);
-const setexClient = promisify(client.setex).bind(client);
+const promisify = util.promisify;
+const getClient = (key) => promisify(client.get).bind(client, key);
+const setClient = (key, value) => promisify(client.set).bind(client, key, value);
+const setexClient = (key, ttl, value) => promisify(client.setex).bind(client, key, ttl, value);
 
 // 内存存储promise化,为了和redis使用方法保持一致
 const getCache = (key: string): Promise<string|undefined> => {
+  console.log(key);
   return new Promise(resolve => {
     resolve(cache.get(key))
   })
 }
-const setCache = (key: string, value: string, ttl: number = 0): Promise<boolean> => {
+const setCache = (key: string, ttl: number = 0, value: string): Promise<boolean> => {
   return new Promise((resolve, reject) => {
     const success = cache.set(key, value, ttl)
     if (success) {
@@ -77,7 +78,7 @@ const setCache = (key: string, value: string, ttl: number = 0): Promise<boolean>
     }
   })
 }
-const setexCache = (key: string, ttl: number, value: string, ) => setCache(key, value, ttl)
+const setexCache = (key: string, ttl: number, value: string, ) => setCache(key, ttl, value)
 
 // 初始化测试
 // client.setex('test', 30, '111111');
@@ -85,7 +86,7 @@ const setexCache = (key: string, ttl: number, value: string, ) => setCache(key,
 
 export default () => {
   return async (ctx, next) => {
-    console.log(isUseRedis);
+    consola.info('redis is available:', isUseRedis);
     if (isUseRedis) { // 如果redis连接失败,采用内存
       ctx.$redis = { set: setClient, get: getClient, setex: setexClient };
     } else {

+ 1 - 1
src/middlewares/response.ts

@@ -1,4 +1,4 @@
-import response from '../utils/response';
+import response from '@utils/response';
 export default () => {
   return async(ctx, next) => {
     ctx.$response = response;

+ 6 - 5
src/router.ts

@@ -1,14 +1,15 @@
 import { Context, DefaultState } from 'koa';
 import Router from 'koa-router';
-import controllers from './controllers';
+import controllers from '@controllers';
 
 // api 路由
 const apiRouter = new Router<DefaultState, Context>();
 apiRouter.get('/code', controllers.code);
-apiRouter.post('/login', controllers.login);
-apiRouter.get('/logout', controllers.logout);
-apiRouter.get('/user-info', controllers.userInfo);
-apiRouter.put('/document', controllers.document.putDocument)
+apiRouter.put('/register', controllers.user.register);
+apiRouter.post('/login', controllers.user.login);
+apiRouter.get('/logout', controllers.user.logout);
+apiRouter.get('/user-info', controllers.user.userInfo);
+apiRouter.put('/document', controllers.document.putDocument);
 // 主路由
 const router = new Router<DefaultState, Context>();
 router.get('/', controllers.home);

+ 10 - 8
src/services/document.ts

@@ -1,8 +1,9 @@
 import { Context } from 'koa';
-import { InterfaceResponseData } from '../utils/response';
-import { InterfaceDocument } from '../controllers/document';
+import { InterfaceResponseData } from '@utils/response';
+import { InterfaceDocument } from '@/controllers/document';
+import CODE from '@utils/code';
 
-export const putDocument = async (ctx: Context, params: InterfaceDocument) :Promise<InterfaceResponseData> => {
+const putDocument = async (ctx: Context, params: InterfaceDocument) :Promise<InterfaceResponseData> => {
   return new Promise(resolve => {
     const reData: InterfaceResponseData = {
       data: null,
@@ -10,27 +11,28 @@ export const putDocument = async (ctx: Context, params: InterfaceDocument) :Prom
       success: false,
       code: 400000
     }
-    ctx.$db.doc.findOne({ url: params.url }, (err, docs) => {
+    ctx.$db.doc.findOne({ url: params.url }, (err, findDoc) => {
       if (err) {
         ctx.$log.error(err)
         ctx.status = 500
         return
       }
-      if (docs) {
+      if (findDoc) {
         reData.message = 'url已存在'
+        reData.code = CODE.EXIST_PARAMS
         resolve(reData)
         return
       }
-      ctx.$db.doc.insert(params, (err, doc) => {
+      ctx.$db.doc.insert(params, (err, saveDoc) => {
         if (err) {
           ctx.$log.error(err)
           ctx.status = 500
           return
         }
-        reData.data = doc;
+        reData.data = saveDoc;
         reData.message = '保存成功'
         reData.success = true
-        reData.code = 200
+        reData.code = CODE.SUCCESS
         resolve(reData)
       })
     })

+ 3 - 1
src/services/index.ts

@@ -1,11 +1,13 @@
 import { Context } from 'koa';
+import user from './user';
 import document from './document';
-export const code = async (ctx: Context, params) => {
+const code = async (ctx: Context, params) => {
   const code = await ctx.$redis.get(`send_sms_verify_code_sms_${params.phone}`);
   return code || 'no code';
 };
 
 export default {
   code,
+  user,
   document
 }

+ 86 - 0
src/services/user.ts

@@ -0,0 +1,86 @@
+import { Context } from 'koa';
+import { InterfaceResponseData } from '@utils/response';
+import { InterfaceRegisterParams } from '@/controllers/user';
+import CODE from '@utils/code';
+
+const register = async (ctx: Context, params: InterfaceRegisterParams) :Promise<InterfaceResponseData> => {
+  const reData: InterfaceResponseData = {
+    data: null,
+    message: '',
+    success: false,
+    code: 400000
+  }
+  return new Promise(resolve => {
+    ctx.$db.user.findOne({ username: params.username }, (err, findUser) => {
+      if (err) {
+        ctx.$log.error(err)
+        ctx.status = 500
+        return
+      }
+      if (findUser) {
+        reData.message = '用户名已存在'
+        reData.code = CODE.EXIST_PARAMS
+        resolve(reData)
+        return
+      }
+      ctx.$db.user.insert(params, (err, saveUser) => {
+        if (err) {
+          ctx.$log.error(err)
+          ctx.status = 500
+          return
+        }
+        reData.data = saveUser;
+        reData.message = '注册成功'
+        reData.success = true
+        reData.code = CODE.SUCCESS
+        if (ctx.session) {
+          ctx.session.user = saveUser
+        }
+        resolve(reData)
+      })
+    })
+  })
+};
+
+const login = async (ctx: Context, params: InterfaceRegisterParams) :Promise<InterfaceResponseData> => {
+  const reData: InterfaceResponseData = {
+    data: null,
+    message: '',
+    success: false,
+    code: 400000
+  }
+  return new Promise(resolve => {
+    ctx.$db.user.findOne({ username: params.username }, (err, findUser) => {
+      if (err) {
+        ctx.$log.error(err)
+        ctx.status = 500
+        return
+      }
+      if (!findUser) {
+        reData.message = '用户名或密码错误'
+        reData.code = CODE.MISS_PARAMS
+        resolve(reData)
+        return
+      }
+      if (params.password === findUser.password) {
+        if (ctx.session) {
+          ctx.session.user = findUser
+        }
+        reData.data = findUser;
+        reData.message = '登录成功'
+        reData.success = true
+        reData.code = CODE.SUCCESS
+        resolve(reData)
+      } else {
+        reData.message = '用户名或密码错误'
+        reData.code = CODE.MISS_PARAMS
+        resolve(reData)
+      }
+    })
+  })
+};
+
+export default {
+  register,
+  login
+}

+ 1 - 1
src/typings/index.d.ts

@@ -1,7 +1,7 @@
 import { Application, Context, DefaultContext } from 'koa';
 import { IMiddleware } from 'koa-router';
 import { RedisClient } from 'redis';
-import response from '../utils/response';
+import response from '@utils/response';
 import { InterfaceDB } from '../middlewares/localDb';
 
 interface interfaceRedis {

+ 9 - 0
src/utils/code.ts

@@ -0,0 +1,9 @@
+const CODE = {
+  SUCCESS: 200, // 成功
+  NOT_LOGIN: 400001, // 未登录
+  LOGINING: 400002, // 已登录
+  MISS_PARAMS: 400400, //参数缺失
+  EXIST_PARAMS: 400401, // 唯一参数已存在
+  ERR_PARAMS: 400402 //参数错误
+}
+export default CODE;

+ 1 - 1
src/utils/logger.ts

@@ -1,7 +1,7 @@
 import log4js from 'log4js';
 import path from 'path';
 import { Context } from 'koa';
-import config from '../config';
+import config from '@config';
 
 // 输出配置
 const output = (ctx, message = '', commonInfo = {}) => {

+ 7 - 0
src/utils/mapping.ts

@@ -0,0 +1,7 @@
+export const mapDocMethod = {
+  [1]: 'GET',
+  [2]: 'POST',
+  [3]: 'PUT',
+  [4]: 'DELETE'
+}
+

+ 22 - 26
src/utils/observer.ts

@@ -1,71 +1,67 @@
+const objToString = (param: any) => Object.prototype.toString.call(param);
+
 class Observer {
-  public handle
+  private handle: Map<string, [Function?]>
   constructor() {
-    this.handle = {};
+    this.handle = new Map()
   };
   // 注册监听的事件
   on(type = '', callback: Function) {
     // 校验参数
     if (typeof type !== 'string') {
-      const msg = `on type must is string, now is ${Object.prototype.toString.call(type)}`;
+      const msg = `on type must is string, now is ${objToString(type)}`;
       throw new Error(msg);
     };
     if (typeof callback !== 'function') {
-      const msg = `callback must is function, now is ${Object.prototype.toString.call(callback)}`;
+      const msg = `callback must is function, now is ${objToString(callback)}`;
       throw new Error(msg);
     };
     // 添加监听的事件,同一个事件可以多个函数监听
-    if (this.handle[type] !== '[object Array]') {
-      this.handle[type] = [];
+    if (!this.handle.has(type)) {
+      this.handle.set(type, []);
     };
-    this.handle[type].push(callback);
+    this.handle.get(type)?.push(callback);
   };
   // 触发
-  emit(type = '', ...params) {
+  emit (type = '', ...params: []) {
     if (typeof type !== 'string') {
-      const msg = `emit type must is string, now is ${Object.prototype.toString.call(type)}`;
+      const msg = `emit type must is string, now is ${objToString(type)}`;
       throw new Error(msg);
     };
-    if (!this.handle[type]) {
+    if (!this.handle.get(type)?.length) {
       const msg = `${type} is not exist`;
       throw new Error(msg);
     };
-    const forHandle = (el) => {
-      if (Object.prototype.toString.call(el) === '[object Function]') {
-        el(...params);
+    const forHandle = (el: Function | undefined) => {
+      if (objToString(el) === '[object Function]') {
+        el && el(...params);
       } else {
         throw new Error(`${type} is not function`);
       };
     }
-    this.handle[type].forEach(forHandle.bind(this));
+    this.handle.get(type)?.forEach(forHandle.bind(this));
   }
   // 销毁监听的事件
   destory(type = '') {
     if (typeof type !== 'string') {
-      const msg = `emit type must is string, now is ${Object.prototype.toString.call(type)}`;
+      const msg = `emit type must is string, now is ${objToString(type)}`;
       throw new Error(msg);
     };
-    if (!this.handle[type] || !this.handle[type].length) {
+    if (!this.handle.get(type)?.length) {
       throw new Error(`${type} is not register`);
     };
-    delete this.handle[type];
+    this.handle.delete(type);
   }
   // 查询是否注册
   list(type = '') {
     if (!type) {
       throw new Error(`${type} is not exist`);
     };
-    if (this.handle[type]) {
-      return true;
+    if (this.handle.has(type)) {
+      return this.handle.get(type);
     };
     return null;
   };
 };
 
-// let observer
-
-// if (!observer) {
-//   observer =
-// }
-
-module.exports = new Observer();
+export default new Observer();

+ 2 - 1
src/utils/response.ts

@@ -1,3 +1,4 @@
+import CODE from '@utils/code';
 export interface InterfaceResponseData {
   data: any
   message?: string
@@ -8,7 +9,7 @@ export interface responseHandle {
   (responseData): { data: any, message: string, success: boolean, code: number }
 }
 
-const responseHandle: responseHandle = (data, message = 'success', success = true, code = 200) => {
+const responseHandle: responseHandle = (data, message = 'success', success = true, code = CODE.SUCCESS) => {
   return {
     data,
     message,

+ 9 - 1
tsconfig.json

@@ -27,7 +27,15 @@
     "inlineSourceMap": true,
     "skipLibCheck": true,
     "skipDefaultLibCheck": true,
-    "esModuleInterop": true
+    "esModuleInterop": true,
+    "baseUrl": "./src",
+    "paths": {
+      "@/*": ["./*"],
+      "@controllers": ["./controllers"],
+      "@services": ["./services"],
+      "@utils/*": ["./utils/*"],
+      "@config": ["./config"]
+    }
   },
   "include": [
     "./src"

+ 27 - 0
yarn.lock

@@ -178,6 +178,11 @@
   resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-1.8.0.tgz#682477dbbbd07cd032731cb3b0e7eaee3d026b69"
   integrity sha512-2aoSC4UUbHDj2uCsCxcG/vRMXey/m17bC7UwitVm5hn22nI8O8Y9iDpA76Orc+DWkQ4zZrOKEshCqR/jSuXAHA==
 
+"@types/json5@^0.0.29":
+  version "0.0.29"
+  resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
+  integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4=
+
 "@types/keygrip@*":
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/@types/keygrip/-/keygrip-1.0.2.tgz#513abfd256d7ad0bf1ee1873606317b33b1b2a72"
@@ -1458,6 +1463,13 @@ json-buffer@3.0.0:
   resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898"
   integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=
 
+json5@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe"
+  integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==
+  dependencies:
+    minimist "^1.2.0"
+
 jsonfile@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
@@ -2446,6 +2458,11 @@ strip-ansi@^6.0.0:
   dependencies:
     ansi-regex "^5.0.0"
 
+strip-bom@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
+  integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=
+
 strip-json-comments@~2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
@@ -2515,6 +2532,16 @@ ts-node@^9.0.0:
     source-map-support "^0.5.17"
     yn "3.1.1"
 
+tsconfig-paths@^3.9.0:
+  version "3.9.0"
+  resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b"
+  integrity sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==
+  dependencies:
+    "@types/json5" "^0.0.29"
+    json5 "^1.0.1"
+    minimist "^1.2.0"
+    strip-bom "^3.0.0"
+
 tslib@1.9.3:
   version "1.9.3"
   resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286"