├── .gitignore ├── README.md ├── example.env ├── package.json ├── prisma ├── dev.db ├── migrations │ ├── 20230117090020_init │ │ └── migration.sql │ ├── 20230117090416_users │ │ └── migration.sql │ └── migration_lock.toml └── schema.prisma ├── public └── default.png ├── src ├── app.ts ├── controllers │ ├── auth.controller.ts │ └── user.controller.ts ├── middleware │ ├── deserializeUser.ts │ ├── requireUser.ts │ └── validate.ts ├── routes │ ├── auth.route.ts │ ├── session.route.ts │ └── user.route.ts ├── schema │ └── user.schema.ts ├── services │ └── session.service.ts └── utils │ └── prisma.ts ├── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | # Keep environment variables out of version control 3 | .env 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # How to Implement GitHub and Google OAuth in Node.js and Express 2 | 3 | In these articles, you'll learn how to implement Google and GitHub OAuth in Node.js applications. 4 | 5 | ## How to Implement Google OAuth2 in Node.js 6 | 7 | In this article, I'll walk you through the process of setting up Google OAuth2 in a Node.js application, including creating the OAuth project on the Google API console, configuring the OAuth Client ID and secret, and implementing the necessary code in the Node.js project. 8 | 9 | ![How to Implement Google OAuth2 in Node.js](https://codevoweb.com/wp-content/uploads/2023/01/How-to-Implement-Google-OAuth2-in-Node.js.webp) 10 | 11 | ### Topics Covered 12 | 13 | - Run the Node.js Google OAuth2 Project 14 | - Run the Node.js API with a React.js App 15 | - Setup the Node.js Project 16 | - Get the Google OAuth2 Credentials 17 | - Setup the Database with Prisma 18 | - Create the Validation Schemas 19 | - Get the Google OAuth Access Token and User's Info 20 | - Get the OAuth Access Token 21 | - Get the Google Account User 22 | - Implement the Google OAuth2 in Node.js 23 | - Register User Route Handler 24 | - Login User Route Handler 25 | - Logout User Route Handler 26 | - Authenticate with Google OAuth2 Route Handler 27 | - Create a User Route Handler 28 | - Create the Authentication Guards 29 | - Authentication Middleware 30 | - Require User Middleware 31 | - Create the API Routes 32 | - Setup CORS and Register the API Routers 33 | 34 | Read the entire article here: [https://codevoweb.com/how-to-implement-google-oauth2-in-nodejs/](https://codevoweb.com/how-to-implement-google-oauth2-in-nodejs/) 35 | 36 | 37 | ## How to Implement GitHub OAuth in Node.js 38 | 39 | In this tutorial, I'll walk you through the process of integrating GitHub OAuth into a Node.js application, including setting up the OAuth App on GitHub, retrieving the OAuth client ID and secret, and implementing the necessary code to handle the OAuth flow. 40 | 41 | ![How to Implement GitHub OAuth in Node.js](https://codevoweb.com/wp-content/uploads/2023/01/How-to-Implement-GitHub-OAuth-in-Node.js.webp) 42 | 43 | ### Topics Covered 44 | 45 | - Run the Node.js GitHub OAuth Project 46 | - Run the Node.js API with a React App 47 | - Setup the Node.js Project 48 | - Get the GitHub OAuth Credentials 49 | - Create the Database Model 50 | - Create the Validation Schemas 51 | - Obtain the GitHub OAuth Access Token and User's Info 52 | - Retrieve the OAuth Access Token 53 | - Retrieve the GitHub Account Information 54 | - Implement GitHub OAuth in Node.js 55 | - Account Registration Route Function 56 | - Account Login Route Function 57 | - Logout Route Function 58 | - Authenticate with GitHub OAuth Route Function 59 | - GetMe Route Function 60 | - Create the Authentication Middleware 61 | - Authentication Guard 62 | - Require User Middleware 63 | - Create the API Routes 64 | - Register the API Routes and Setup CORS 65 | 66 | Read the entire article here: [https://codevoweb.com/how-to-implement-github-oauth-in-nodejs/](https://codevoweb.com/how-to-implement-github-oauth-in-nodejs/) 67 | -------------------------------------------------------------------------------- /example.env: -------------------------------------------------------------------------------- 1 | DATABASE_URL="file:./dev.db" 2 | 3 | GOOGLE_OAUTH_CLIENT_ID= 4 | GOOGLE_OAUTH_CLIENT_SECRET= 5 | GOOGLE_OAUTH_REDIRECT=http://localhost:8000/api/sessions/oauth/google 6 | 7 | GITHUB_OAUTH_CLIENT_ID= 8 | GITHUB_OAUTH_CLIENT_SECRET= 9 | GITHUB_OAUTH_REDIRECT_URL=http://localhost:8000/api/sessions/oauth/github 10 | 11 | NODE_ENV=development 12 | JWT_SECRET=my_ultra_secure_secret 13 | TOKEN_EXPIRES_IN=60 14 | FRONTEND_ORIGIN=http://localhost:3000 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "google-github-oauth2-nodejs", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "scripts": { 7 | "start": "ts-node-dev --respawn --transpile-only src/app.ts", 8 | "db:migrate": "npx prisma migrate dev --name 'users' --create-only", 9 | "db:generate": " npx prisma generate", 10 | "db:push": "npx prisma db push" 11 | }, 12 | "devDependencies": { 13 | "@types/cookie-parser": "^1.4.3", 14 | "@types/cors": "^2.8.13", 15 | "@types/express": "^4.17.15", 16 | "@types/jsonwebtoken": "^9.0.1", 17 | "@types/morgan": "^1.9.4", 18 | "@types/node": "^18.11.18", 19 | "@types/qs": "^6.9.7", 20 | "morgan": "^1.10.0", 21 | "prisma": "^4.8.1", 22 | "ts-node-dev": "^2.0.0", 23 | "typescript": "^4.9.4" 24 | }, 25 | "dependencies": { 26 | "@prisma/client": "^4.8.1", 27 | "axios": "^1.2.2", 28 | "cookie-parser": "^1.4.6", 29 | "cors": "^2.8.5", 30 | "dotenv": "^16.0.3", 31 | "express": "^4.18.2", 32 | "jsonwebtoken": "^9.0.0", 33 | "qs": "^6.11.0", 34 | "zod": "^3.20.2" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /prisma/dev.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wpcodevo/google-github-oauth2-nodejs/e836cef5b2fcd09873f8af15c8a6fa1fdab43c48/prisma/dev.db -------------------------------------------------------------------------------- /prisma/migrations/20230117090020_init/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "User" ( 3 | "id" TEXT NOT NULL PRIMARY KEY, 4 | "name" TEXT NOT NULL, 5 | "email" TEXT NOT NULL, 6 | "password" TEXT NOT NULL, 7 | "role" TEXT NOT NULL DEFAULT 'user', 8 | "photo" TEXT NOT NULL DEFAULT 'default.png', 9 | "verified" BOOLEAN NOT NULL DEFAULT false, 10 | "provider" TEXT NOT NULL DEFAULT 'local', 11 | "createdAt" DATETIME NOT NULL, 12 | "updatedAt" DATETIME NOT NULL 13 | ); 14 | 15 | -- CreateIndex 16 | CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); 17 | -------------------------------------------------------------------------------- /prisma/migrations/20230117090416_users/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - You are about to drop the `User` table. If the table is not empty, all the data it contains will be lost. 5 | 6 | */ 7 | -- DropTable 8 | PRAGMA foreign_keys=off; 9 | DROP TABLE "User"; 10 | PRAGMA foreign_keys=on; 11 | 12 | -- CreateTable 13 | CREATE TABLE "users" ( 14 | "id" TEXT NOT NULL PRIMARY KEY, 15 | "name" TEXT NOT NULL, 16 | "email" TEXT NOT NULL, 17 | "password" TEXT NOT NULL, 18 | "role" TEXT NOT NULL DEFAULT 'user', 19 | "photo" TEXT NOT NULL DEFAULT 'default.png', 20 | "verified" BOOLEAN NOT NULL DEFAULT false, 21 | "provider" TEXT NOT NULL DEFAULT 'local', 22 | "createdAt" DATETIME NOT NULL, 23 | "updatedAt" DATETIME NOT NULL 24 | ); 25 | 26 | -- CreateIndex 27 | CREATE UNIQUE INDEX "users_email_key" ON "users"("email"); 28 | -------------------------------------------------------------------------------- /prisma/migrations/migration_lock.toml: -------------------------------------------------------------------------------- 1 | # Please do not edit this file manually 2 | # It should be added in your version-control system (i.e. Git) 3 | provider = "sqlite" -------------------------------------------------------------------------------- /prisma/schema.prisma: -------------------------------------------------------------------------------- 1 | // This is your Prisma schema file, 2 | // learn more about it in the docs: https://pris.ly/d/prisma-schema 3 | 4 | generator client { 5 | provider = "prisma-client-js" 6 | } 7 | 8 | datasource db { 9 | provider = "sqlite" 10 | url = env("DATABASE_URL") 11 | } 12 | 13 | model User { 14 | id String @id @default(uuid()) 15 | name String 16 | email String @unique 17 | password String 18 | role String @default("user") 19 | photo String @default("default.png") 20 | verified Boolean @default(false) 21 | provider String @default("local") 22 | 23 | createdAt DateTime 24 | updatedAt DateTime @updatedAt 25 | 26 | @@map(name: "users") 27 | } 28 | -------------------------------------------------------------------------------- /public/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wpcodevo/google-github-oauth2-nodejs/e836cef5b2fcd09873f8af15c8a6fa1fdab43c48/public/default.png -------------------------------------------------------------------------------- /src/app.ts: -------------------------------------------------------------------------------- 1 | require("dotenv").config(); 2 | import path from "path"; 3 | import express, { NextFunction, Request, Response } from "express"; 4 | import morgan from "morgan"; 5 | import cors from "cors"; 6 | import cookieParser from "cookie-parser"; 7 | import userRouter from "./routes/user.route"; 8 | import authRouter from "./routes/auth.route"; 9 | import sessionRouter from "./routes/session.route"; 10 | import connectDB from "./utils/prisma"; 11 | 12 | const app = express(); 13 | 14 | app.use(express.json({ limit: "10kb" })); 15 | app.use(cookieParser()); 16 | if (process.env.NODE_ENV === "development") app.use(morgan("dev")); 17 | app.use("/api/images", express.static(path.join(__dirname, "../public"))); 18 | 19 | const FRONTEND_ORIGIN = process.env.FRONTEND_ORIGIN as unknown as string; 20 | app.use( 21 | cors({ 22 | credentials: true, 23 | origin: [FRONTEND_ORIGIN], 24 | }) 25 | ); 26 | 27 | app.use("/api/users", userRouter); 28 | app.use("/api/auth", authRouter); 29 | app.use("/api/sessions", sessionRouter); 30 | 31 | app.get("/api/healthchecker", (req: Request, res: Response) => { 32 | res.status(200).json({ 33 | status: "success", 34 | message: "Implement OAuth in Node.js", 35 | }); 36 | }); 37 | 38 | // UnKnown Routes 39 | app.all("*", (req: Request, res: Response, next: NextFunction) => { 40 | const err = new Error(`Route ${req.originalUrl} not found`) as any; 41 | err.statusCode = 404; 42 | next(err); 43 | }); 44 | 45 | app.use((err: any, req: Request, res: Response, next: NextFunction) => { 46 | err.status = err.status || "error"; 47 | err.statusCode = err.statusCode || 500; 48 | 49 | res.status(err.statusCode).json({ 50 | status: err.status, 51 | message: err.message, 52 | }); 53 | }); 54 | 55 | const port = 8000; 56 | app.listen(port, () => { 57 | console.log(`✅ Server started on port: ${port}`); 58 | connectDB(); 59 | }); 60 | -------------------------------------------------------------------------------- /src/controllers/auth.controller.ts: -------------------------------------------------------------------------------- 1 | import { NextFunction, Request, Response } from "express"; 2 | import { CreateUserInput, LoginUserInput } from "../schema/user.schema"; 3 | import { 4 | getGithubOathToken, 5 | getGithubUser, 6 | getGoogleOauthToken, 7 | getGoogleUser, 8 | } from "../services/session.service"; 9 | import { prisma } from "../utils/prisma"; 10 | import jwt from "jsonwebtoken"; 11 | 12 | export function exclude( 13 | user: User, 14 | keys: Key[] 15 | ): Omit { 16 | for (let key of keys) { 17 | delete user[key]; 18 | } 19 | return user; 20 | } 21 | 22 | export const registerHandler = async ( 23 | req: Request<{}, {}, CreateUserInput>, 24 | res: Response, 25 | next: NextFunction 26 | ) => { 27 | try { 28 | const user = await prisma.user.create({ 29 | data: { 30 | name: req.body.name, 31 | email: req.body.email, 32 | password: req.body.password, 33 | createdAt: new Date(), 34 | }, 35 | }); 36 | 37 | res.status(201).json({ 38 | status: "success", 39 | data: { 40 | user: exclude(user, ["password"]), 41 | }, 42 | }); 43 | } catch (err: any) { 44 | if (err.code === "P2002") { 45 | return res.status(409).json({ 46 | status: "fail", 47 | message: "Email already exist", 48 | }); 49 | } 50 | next(err); 51 | } 52 | }; 53 | 54 | export const loginHandler = async ( 55 | req: Request<{}, {}, LoginUserInput>, 56 | res: Response, 57 | next: NextFunction 58 | ) => { 59 | try { 60 | const user = await prisma.user.findUnique({ 61 | where: { email: req.body.email }, 62 | }); 63 | 64 | if (!user) { 65 | return res.status(401).json({ 66 | status: "fail", 67 | message: "Invalid email or password", 68 | }); 69 | } 70 | 71 | if (user.provider === "Google" || user.provider === "GitHub") { 72 | return res.status(401).json({ 73 | status: "fail", 74 | message: `Use ${user.provider} OAuth2 instead`, 75 | }); 76 | } 77 | 78 | const TOKEN_EXPIRES_IN = process.env.TOKEN_EXPIRES_IN as unknown as number; 79 | const TOKEN_SECRET = process.env.JWT_SECRET as unknown as string; 80 | const token = jwt.sign({ sub: user.id }, TOKEN_SECRET, { 81 | expiresIn: `${TOKEN_EXPIRES_IN}m`, 82 | }); 83 | 84 | res.cookie("token", token, { 85 | expires: new Date(Date.now() + TOKEN_EXPIRES_IN * 60 * 1000), 86 | }); 87 | 88 | res.status(200).json({ 89 | status: "success", 90 | }); 91 | } catch (err: any) { 92 | next(err); 93 | } 94 | }; 95 | 96 | export const logoutHandler = async ( 97 | req: Request, 98 | res: Response, 99 | next: NextFunction 100 | ) => { 101 | try { 102 | res.cookie("token", "", { maxAge: -1 }); 103 | res.status(200).json({ status: "success" }); 104 | } catch (err: any) { 105 | next(err); 106 | } 107 | }; 108 | 109 | export const googleOauthHandler = async (req: Request, res: Response) => { 110 | const FRONTEND_ORIGIN = process.env.FRONTEND_ORIGIN as unknown as string; 111 | 112 | try { 113 | const code = req.query.code as string; 114 | const pathUrl = (req.query.state as string) || "/"; 115 | 116 | if (!code) { 117 | return res.status(401).json({ 118 | status: "fail", 119 | message: "Authorization code not provided!", 120 | }); 121 | } 122 | 123 | const { id_token, access_token } = await getGoogleOauthToken({ code }); 124 | 125 | const { name, verified_email, email, picture } = await getGoogleUser({ 126 | id_token, 127 | access_token, 128 | }); 129 | 130 | if (!verified_email) { 131 | return res.status(403).json({ 132 | status: "fail", 133 | message: "Google account not verified", 134 | }); 135 | } 136 | 137 | const user = await prisma.user.upsert({ 138 | where: { email }, 139 | create: { 140 | createdAt: new Date(), 141 | name, 142 | email, 143 | photo: picture, 144 | password: "", 145 | verified: true, 146 | provider: "Google", 147 | }, 148 | update: { name, email, photo: picture, provider: "Google" }, 149 | }); 150 | 151 | if (!user) return res.redirect(`${FRONTEND_ORIGIN}/oauth/error`); 152 | 153 | const TOKEN_EXPIRES_IN = process.env.TOKEN_EXPIRES_IN as unknown as number; 154 | const TOKEN_SECRET = process.env.JWT_SECRET as unknown as string; 155 | const token = jwt.sign({ sub: user.id }, TOKEN_SECRET, { 156 | expiresIn: `${TOKEN_EXPIRES_IN}m`, 157 | }); 158 | 159 | res.cookie("token", token, { 160 | expires: new Date(Date.now() + TOKEN_EXPIRES_IN * 60 * 1000), 161 | }); 162 | 163 | res.redirect(`${FRONTEND_ORIGIN}${pathUrl}`); 164 | } catch (err: any) { 165 | console.log("Failed to authorize Google User", err); 166 | return res.redirect(`${FRONTEND_ORIGIN}/oauth/error`); 167 | } 168 | }; 169 | 170 | export const githubOauthHandler = async (req: Request, res: Response) => { 171 | const FRONTEND_ORIGIN = process.env.FRONTEND_ORIGIN as unknown as string; 172 | 173 | try { 174 | const code = req.query.code as string; 175 | const pathUrl = (req.query.state as string) ?? "/"; 176 | 177 | if (req.query.error) { 178 | return res.redirect(`${FRONTEND_ORIGIN}/login`); 179 | } 180 | 181 | if (!code) { 182 | return res.status(401).json({ 183 | status: "error", 184 | message: "Authorization code not provided!", 185 | }); 186 | } 187 | 188 | const { access_token } = await getGithubOathToken({ code }); 189 | 190 | const { email, avatar_url, login } = await getGithubUser({ access_token }); 191 | 192 | const user = await prisma.user.upsert({ 193 | where: { email }, 194 | create: { 195 | createdAt: new Date(), 196 | name: login, 197 | email, 198 | photo: avatar_url, 199 | password: "", 200 | verified: true, 201 | provider: "GitHub", 202 | }, 203 | update: { name: login, email, photo: avatar_url, provider: "GitHub" }, 204 | }); 205 | 206 | if (!user) return res.redirect(`${FRONTEND_ORIGIN}/oauth/error`); 207 | 208 | const TOKEN_EXPIRES_IN = process.env.TOKEN_EXPIRES_IN as unknown as number; 209 | const TOKEN_SECRET = process.env.JWT_SECRET as unknown as string; 210 | const token = jwt.sign({ sub: user.id }, TOKEN_SECRET, { 211 | expiresIn: `${TOKEN_EXPIRES_IN}m`, 212 | }); 213 | 214 | res.cookie("token", token, { 215 | expires: new Date(Date.now() + TOKEN_EXPIRES_IN * 60 * 1000), 216 | }); 217 | 218 | res.redirect(`${FRONTEND_ORIGIN}${pathUrl}`); 219 | } catch (err: any) { 220 | return res.redirect(`${FRONTEND_ORIGIN}/oauth/error`); 221 | } 222 | }; 223 | -------------------------------------------------------------------------------- /src/controllers/user.controller.ts: -------------------------------------------------------------------------------- 1 | import { NextFunction, Request, Response } from "express"; 2 | 3 | export const getMeHandler = ( 4 | req: Request, 5 | res: Response, 6 | next: NextFunction 7 | ) => { 8 | try { 9 | const user = res.locals.user; 10 | res.status(200).json({ 11 | status: "success", 12 | data: { 13 | user, 14 | }, 15 | }); 16 | } catch (err: any) { 17 | next(err); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /src/middleware/deserializeUser.ts: -------------------------------------------------------------------------------- 1 | import { NextFunction, Request, Response } from "express"; 2 | import jwt from "jsonwebtoken"; 3 | import { exclude } from "../controllers/auth.controller"; 4 | import { prisma } from "../utils/prisma"; 5 | 6 | export const deserializeUser = async ( 7 | req: Request, 8 | res: Response, 9 | next: NextFunction 10 | ) => { 11 | try { 12 | let token; 13 | if ( 14 | req.headers.authorization && 15 | req.headers.authorization.startsWith("Bearer") 16 | ) { 17 | token = req.headers.authorization.split(" ")[1]; 18 | } else if (req.cookies.token) { 19 | token = req.cookies.token; 20 | } 21 | 22 | if (!token) { 23 | return res.status(401).json({ 24 | status: "fail", 25 | message: "You are not logged in", 26 | }); 27 | } 28 | 29 | const JWT_SECRET = process.env.JWT_SECRET as unknown as string; 30 | const decoded = jwt.verify(token, JWT_SECRET); 31 | 32 | if (!decoded) { 33 | return res.status(401).json({ 34 | status: "fail", 35 | message: "Invalid token or user doesn't exist", 36 | }); 37 | } 38 | 39 | const user = await prisma.user.findUnique({ 40 | where: { id: String(decoded.sub) }, 41 | }); 42 | 43 | if (!user) { 44 | return res.status(401).json({ 45 | status: "fail", 46 | message: "User with that token no longer exist", 47 | }); 48 | } 49 | 50 | res.locals.user = exclude(user, ["password"]); 51 | 52 | next(); 53 | } catch (err: any) { 54 | next(err); 55 | } 56 | }; 57 | -------------------------------------------------------------------------------- /src/middleware/requireUser.ts: -------------------------------------------------------------------------------- 1 | import { NextFunction, Request, Response } from "express"; 2 | 3 | export const requireUser = ( 4 | req: Request, 5 | res: Response, 6 | next: NextFunction 7 | ) => { 8 | try { 9 | const user = res.locals.user; 10 | if (!user) { 11 | return res.status(401).json({ 12 | status: "fail", 13 | message: "Invalid token or session has expired", 14 | }); 15 | } 16 | 17 | next(); 18 | } catch (err: any) { 19 | next(err); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /src/middleware/validate.ts: -------------------------------------------------------------------------------- 1 | import { NextFunction, Request, Response } from "express"; 2 | import { AnyZodObject, ZodError } from "zod"; 3 | 4 | export const validate = 5 | (schema: AnyZodObject) => 6 | (req: Request, res: Response, next: NextFunction) => { 7 | try { 8 | schema.parse({ 9 | params: req.params, 10 | query: req.query, 11 | body: req.body, 12 | }); 13 | 14 | next(); 15 | } catch (err: any) { 16 | if (err instanceof ZodError) { 17 | return res.status(400).json({ 18 | status: "fail", 19 | error: err.errors, 20 | }); 21 | } 22 | next(err); 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /src/routes/auth.route.ts: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import { 3 | loginHandler, 4 | logoutHandler, 5 | registerHandler, 6 | } from "../controllers/auth.controller"; 7 | import { deserializeUser } from "../middleware/deserializeUser"; 8 | import { requireUser } from "../middleware/requireUser"; 9 | import { validate } from "../middleware/validate"; 10 | import { createUserSchema, loginUserSchema } from "../schema/user.schema"; 11 | 12 | const router = express.Router(); 13 | 14 | // Register user route 15 | router.post("/register", validate(createUserSchema), registerHandler); 16 | 17 | // Login user route 18 | router.post("/login", validate(loginUserSchema), loginHandler); 19 | 20 | // Logout User 21 | router.get("/logout", deserializeUser, requireUser, logoutHandler); 22 | 23 | export default router; 24 | -------------------------------------------------------------------------------- /src/routes/session.route.ts: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import { 3 | githubOauthHandler, 4 | googleOauthHandler, 5 | } from '../controllers/auth.controller'; 6 | 7 | const router = express.Router(); 8 | 9 | router.get('/oauth/google', googleOauthHandler); 10 | router.get('/oauth/github', githubOauthHandler); 11 | 12 | export default router; 13 | -------------------------------------------------------------------------------- /src/routes/user.route.ts: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import { getMeHandler } from "../controllers/user.controller"; 3 | import { deserializeUser } from "../middleware/deserializeUser"; 4 | import { requireUser } from "../middleware/requireUser"; 5 | 6 | const router = express.Router(); 7 | 8 | router.use(deserializeUser, requireUser); 9 | 10 | // Get my info route 11 | router.get("/me", getMeHandler); 12 | 13 | export default router; 14 | -------------------------------------------------------------------------------- /src/schema/user.schema.ts: -------------------------------------------------------------------------------- 1 | import { object, string, TypeOf } from 'zod'; 2 | 3 | export const createUserSchema = object({ 4 | body: object({ 5 | name: string({ required_error: 'Name is required' }), 6 | email: string({ required_error: 'Email is required' }).email( 7 | 'Invalid email' 8 | ), 9 | password: string({ required_error: 'Password is required' }) 10 | .min(8, 'Password must be more than 8 characters') 11 | .max(32, 'Password must be less than 32 characters'), 12 | passwordConfirm: string({ required_error: 'Please confirm your password' }), 13 | }).refine((data) => data.password === data.passwordConfirm, { 14 | path: ['passwordConfirm'], 15 | message: 'Passwords do not match', 16 | }), 17 | }); 18 | 19 | export const loginUserSchema = object({ 20 | body: object({ 21 | email: string({ required_error: 'Email is required' }).email( 22 | 'Invalid email or password' 23 | ), 24 | password: string({ required_error: 'Password is required' }).min( 25 | 8, 26 | 'Invalid email or password' 27 | ), 28 | }), 29 | }); 30 | 31 | export type CreateUserInput = TypeOf['body']; 32 | export type LoginUserInput = TypeOf['body']; 33 | -------------------------------------------------------------------------------- /src/services/session.service.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import qs from "qs"; 3 | 4 | const GOOGLE_OAUTH_CLIENT_ID = process.env 5 | .GOOGLE_OAUTH_CLIENT_ID as unknown as string; 6 | const GOOGLE_OAUTH_CLIENT_SECRET = process.env 7 | .GOOGLE_OAUTH_CLIENT_SECRET as unknown as string; 8 | const GOOGLE_OAUTH_REDIRECT = process.env 9 | .GOOGLE_OAUTH_REDIRECT as unknown as string; 10 | 11 | const GITHUB_OAUTH_CLIENT_ID = process.env 12 | .GITHUB_OAUTH_CLIENT_ID as unknown as string; 13 | const GITHUB_OAUTH_CLIENT_SECRET = process.env 14 | .GITHUB_OAUTH_CLIENT_SECRET as unknown as string; 15 | 16 | interface GoogleOauthToken { 17 | access_token: string; 18 | id_token: string; 19 | expires_in: number; 20 | refresh_token: string; 21 | token_type: string; 22 | scope: string; 23 | } 24 | 25 | export const getGoogleOauthToken = async ({ 26 | code, 27 | }: { 28 | code: string; 29 | }): Promise => { 30 | const rootURl = "https://oauth2.googleapis.com/token"; 31 | 32 | const options = { 33 | code, 34 | client_id: GOOGLE_OAUTH_CLIENT_ID, 35 | client_secret: GOOGLE_OAUTH_CLIENT_SECRET, 36 | redirect_uri: GOOGLE_OAUTH_REDIRECT, 37 | grant_type: "authorization_code", 38 | }; 39 | try { 40 | const { data } = await axios.post( 41 | rootURl, 42 | qs.stringify(options), 43 | { 44 | headers: { 45 | "Content-Type": "application/x-www-form-urlencoded", 46 | }, 47 | } 48 | ); 49 | 50 | return data; 51 | } catch (err: any) { 52 | console.log("Failed to fetch Google Oauth Tokens"); 53 | throw new Error(err); 54 | } 55 | }; 56 | 57 | interface GoogleUserResult { 58 | id: string; 59 | email: string; 60 | verified_email: boolean; 61 | name: string; 62 | given_name: string; 63 | family_name: string; 64 | picture: string; 65 | locale: string; 66 | } 67 | 68 | export async function getGoogleUser({ 69 | id_token, 70 | access_token, 71 | }: { 72 | id_token: string; 73 | access_token: string; 74 | }): Promise { 75 | try { 76 | const { data } = await axios.get( 77 | `https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token=${access_token}`, 78 | { 79 | headers: { 80 | Authorization: `Bearer ${id_token}`, 81 | }, 82 | } 83 | ); 84 | 85 | return data; 86 | } catch (err: any) { 87 | console.log(err); 88 | throw Error(err); 89 | } 90 | } 91 | 92 | // 👇 GitHub OAuth 93 | 94 | type GitHubOauthToken = { 95 | access_token: string; 96 | }; 97 | 98 | interface GitHubUser { 99 | login: string; 100 | id: number; 101 | node_id: string; 102 | avatar_url: string; 103 | gravatar_id: string; 104 | url: string; 105 | html_url: string; 106 | followers_url: string; 107 | following_url: string; 108 | gists_url: string; 109 | starred_url: string; 110 | subscriptions_url: string; 111 | organizations_url: string; 112 | repos_url: string; 113 | events_url: string; 114 | received_events_url: string; 115 | type: string; 116 | site_admin: boolean; 117 | name: string; 118 | company: string; 119 | blog: string; 120 | location: null; 121 | email: string; 122 | hireable: boolean; 123 | bio: string; 124 | twitter_username: string; 125 | public_repos: number; 126 | public_gists: number; 127 | followers: number; 128 | following: number; 129 | created_at: Date; 130 | updated_at: Date; 131 | } 132 | 133 | export const getGithubOathToken = async ({ 134 | code, 135 | }: { 136 | code: string; 137 | }): Promise => { 138 | const rootUrl = "https://github.com/login/oauth/access_token"; 139 | const options = { 140 | client_id: GITHUB_OAUTH_CLIENT_ID, 141 | client_secret: GITHUB_OAUTH_CLIENT_SECRET, 142 | code, 143 | }; 144 | 145 | const queryString = qs.stringify(options); 146 | 147 | try { 148 | const { data } = await axios.post(`${rootUrl}?${queryString}`, { 149 | headers: { 150 | "Content-Type": "application/x-www-form-urlencoded", 151 | }, 152 | }); 153 | 154 | const decoded = qs.parse(data) as GitHubOauthToken; 155 | 156 | return decoded; 157 | } catch (err: any) { 158 | throw Error(err); 159 | } 160 | }; 161 | 162 | export const getGithubUser = async ({ 163 | access_token, 164 | }: { 165 | access_token: string; 166 | }): Promise => { 167 | try { 168 | const { data } = await axios.get( 169 | "https://api.github.com/user", 170 | { 171 | headers: { 172 | Authorization: `Bearer ${access_token}`, 173 | }, 174 | } 175 | ); 176 | 177 | return data; 178 | } catch (err: any) { 179 | throw Error(err); 180 | } 181 | }; 182 | -------------------------------------------------------------------------------- /src/utils/prisma.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from "@prisma/client"; 2 | 3 | export const prisma = new PrismaClient(); 4 | 5 | async function connectDB() { 6 | try { 7 | await prisma.$connect(); 8 | console.log("🚀 Database connected successfully"); 9 | } catch (error) { 10 | console.log(error); 11 | process.exit(1); 12 | } finally { 13 | await prisma.$disconnect(); 14 | } 15 | } 16 | 17 | export default connectDB; 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2016", 4 | "experimentalDecorators": true, 5 | "emitDecoratorMetadata": true, 6 | "module": "commonjs", 7 | "esModuleInterop": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "strict": true, 10 | "strictPropertyInitialization": false, 11 | "skipLibCheck": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@cspotcode/source-map-support@^0.8.0": 6 | version "0.8.1" 7 | resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" 8 | integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== 9 | dependencies: 10 | "@jridgewell/trace-mapping" "0.3.9" 11 | 12 | "@jridgewell/resolve-uri@^3.0.3": 13 | version "3.1.0" 14 | resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" 15 | integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== 16 | 17 | "@jridgewell/sourcemap-codec@^1.4.10": 18 | version "1.4.14" 19 | resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" 20 | integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== 21 | 22 | "@jridgewell/trace-mapping@0.3.9": 23 | version "0.3.9" 24 | resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" 25 | integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== 26 | dependencies: 27 | "@jridgewell/resolve-uri" "^3.0.3" 28 | "@jridgewell/sourcemap-codec" "^1.4.10" 29 | 30 | "@prisma/client@^4.8.1": 31 | version "4.8.1" 32 | resolved "https://registry.yarnpkg.com/@prisma/client/-/client-4.8.1.tgz#51c16488dfac4e74a275a2753bf20262a65f2a2b" 33 | integrity sha512-d4xhZhETmeXK/yZ7K0KcVOzEfI5YKGGEr4F5SBV04/MU4ncN/HcE28sy3e4Yt8UFW0ZuImKFQJE+9rWt9WbGSQ== 34 | dependencies: 35 | "@prisma/engines-version" "4.8.0-61.d6e67a83f971b175a593ccc12e15c4a757f93ffe" 36 | 37 | "@prisma/engines-version@4.8.0-61.d6e67a83f971b175a593ccc12e15c4a757f93ffe": 38 | version "4.8.0-61.d6e67a83f971b175a593ccc12e15c4a757f93ffe" 39 | resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-4.8.0-61.d6e67a83f971b175a593ccc12e15c4a757f93ffe.tgz#30401aba1029e7d32e3cb717e705a7c92ccc211e" 40 | integrity sha512-MHSOSexomRMom8QN4t7bu87wPPD+pa+hW9+71JnVcF3DqyyO/ycCLhRL1we3EojRpZxKvuyGho2REQsMCvxcJw== 41 | 42 | "@prisma/engines@4.8.1": 43 | version "4.8.1" 44 | resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-4.8.1.tgz#8428f7dcd7912c6073024511376595017630dc85" 45 | integrity sha512-93tctjNXcIS+i/e552IO6tqw17sX8liivv8WX9lDMCpEEe3ci+nT9F+1oHtAafqruXLepKF80i/D20Mm+ESlOw== 46 | 47 | "@tsconfig/node10@^1.0.7": 48 | version "1.0.9" 49 | resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" 50 | integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== 51 | 52 | "@tsconfig/node12@^1.0.7": 53 | version "1.0.11" 54 | resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" 55 | integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== 56 | 57 | "@tsconfig/node14@^1.0.0": 58 | version "1.0.3" 59 | resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" 60 | integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== 61 | 62 | "@tsconfig/node16@^1.0.2": 63 | version "1.0.3" 64 | resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" 65 | integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== 66 | 67 | "@types/body-parser@*": 68 | version "1.19.2" 69 | resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" 70 | integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== 71 | dependencies: 72 | "@types/connect" "*" 73 | "@types/node" "*" 74 | 75 | "@types/connect@*": 76 | version "3.4.35" 77 | resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" 78 | integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== 79 | dependencies: 80 | "@types/node" "*" 81 | 82 | "@types/cookie-parser@^1.4.3": 83 | version "1.4.3" 84 | resolved "https://registry.yarnpkg.com/@types/cookie-parser/-/cookie-parser-1.4.3.tgz#3a01df117c5705cf89a84c876b50c5a1fd427a21" 85 | integrity sha512-CqSKwFwefj4PzZ5n/iwad/bow2hTCh0FlNAeWLtQM3JA/NX/iYagIpWG2cf1bQKQ2c9gU2log5VUCrn7LDOs0w== 86 | dependencies: 87 | "@types/express" "*" 88 | 89 | "@types/cors@^2.8.13": 90 | version "2.8.13" 91 | resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.13.tgz#b8ade22ba455a1b8cb3b5d3f35910fd204f84f94" 92 | integrity sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA== 93 | dependencies: 94 | "@types/node" "*" 95 | 96 | "@types/express-serve-static-core@^4.17.31": 97 | version "4.17.32" 98 | resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.32.tgz#93dda387f5516af616d8d3f05f2c4c79d81e1b82" 99 | integrity sha512-aI5h/VOkxOF2Z1saPy0Zsxs5avets/iaiAJYznQFm5By/pamU31xWKL//epiF4OfUA2qTOc9PV6tCUjhO8wlZA== 100 | dependencies: 101 | "@types/node" "*" 102 | "@types/qs" "*" 103 | "@types/range-parser" "*" 104 | 105 | "@types/express@*", "@types/express@^4.17.15": 106 | version "4.17.15" 107 | resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.15.tgz#9290e983ec8b054b65a5abccb610411953d417ff" 108 | integrity sha512-Yv0k4bXGOH+8a+7bELd2PqHQsuiANB+A8a4gnQrkRWzrkKlb6KHaVvyXhqs04sVW/OWlbPyYxRgYlIXLfrufMQ== 109 | dependencies: 110 | "@types/body-parser" "*" 111 | "@types/express-serve-static-core" "^4.17.31" 112 | "@types/qs" "*" 113 | "@types/serve-static" "*" 114 | 115 | "@types/jsonwebtoken@^9.0.1": 116 | version "9.0.1" 117 | resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.1.tgz#29b1369c4774200d6d6f63135bf3d1ba3ef997a4" 118 | integrity sha512-c5ltxazpWabia/4UzhIoaDcIza4KViOQhdbjRlfcIGVnsE3c3brkz9Z+F/EeJIECOQP7W7US2hNE930cWWkPiw== 119 | dependencies: 120 | "@types/node" "*" 121 | 122 | "@types/mime@*": 123 | version "3.0.1" 124 | resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" 125 | integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== 126 | 127 | "@types/morgan@^1.9.4": 128 | version "1.9.4" 129 | resolved "https://registry.yarnpkg.com/@types/morgan/-/morgan-1.9.4.tgz#99965ad2bdc7c5cee28d8ce95cfa7300b19ea562" 130 | integrity sha512-cXoc4k+6+YAllH3ZHmx4hf7La1dzUk6keTR4bF4b4Sc0mZxU/zK4wO7l+ZzezXm/jkYj/qC+uYGZrarZdIVvyQ== 131 | dependencies: 132 | "@types/node" "*" 133 | 134 | "@types/node@*", "@types/node@^18.11.18": 135 | version "18.11.18" 136 | resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.18.tgz#8dfb97f0da23c2293e554c5a50d61ef134d7697f" 137 | integrity sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA== 138 | 139 | "@types/qs@*", "@types/qs@^6.9.7": 140 | version "6.9.7" 141 | resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" 142 | integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== 143 | 144 | "@types/range-parser@*": 145 | version "1.2.4" 146 | resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" 147 | integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== 148 | 149 | "@types/serve-static@*": 150 | version "1.15.0" 151 | resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.0.tgz#c7930ff61afb334e121a9da780aac0d9b8f34155" 152 | integrity sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg== 153 | dependencies: 154 | "@types/mime" "*" 155 | "@types/node" "*" 156 | 157 | "@types/strip-bom@^3.0.0": 158 | version "3.0.0" 159 | resolved "https://registry.yarnpkg.com/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2" 160 | integrity sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ== 161 | 162 | "@types/strip-json-comments@0.0.30": 163 | version "0.0.30" 164 | resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" 165 | integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ== 166 | 167 | accepts@~1.3.8: 168 | version "1.3.8" 169 | resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" 170 | integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== 171 | dependencies: 172 | mime-types "~2.1.34" 173 | negotiator "0.6.3" 174 | 175 | acorn-walk@^8.1.1: 176 | version "8.2.0" 177 | resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" 178 | integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== 179 | 180 | acorn@^8.4.1: 181 | version "8.8.1" 182 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73" 183 | integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA== 184 | 185 | anymatch@~3.1.2: 186 | version "3.1.3" 187 | resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" 188 | integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== 189 | dependencies: 190 | normalize-path "^3.0.0" 191 | picomatch "^2.0.4" 192 | 193 | arg@^4.1.0: 194 | version "4.1.3" 195 | resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" 196 | integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== 197 | 198 | array-flatten@1.1.1: 199 | version "1.1.1" 200 | resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" 201 | integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== 202 | 203 | asynckit@^0.4.0: 204 | version "0.4.0" 205 | resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" 206 | integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== 207 | 208 | axios@^1.2.2: 209 | version "1.2.2" 210 | resolved "https://registry.yarnpkg.com/axios/-/axios-1.2.2.tgz#72681724c6e6a43a9fea860fc558127dbe32f9f1" 211 | integrity sha512-bz/J4gS2S3I7mpN/YZfGFTqhXTYzRho8Ay38w2otuuDR322KzFIWm/4W2K6gIwvWaws5n+mnb7D1lN9uD+QH6Q== 212 | dependencies: 213 | follow-redirects "^1.15.0" 214 | form-data "^4.0.0" 215 | proxy-from-env "^1.1.0" 216 | 217 | balanced-match@^1.0.0: 218 | version "1.0.2" 219 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" 220 | integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== 221 | 222 | basic-auth@~2.0.1: 223 | version "2.0.1" 224 | resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" 225 | integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg== 226 | dependencies: 227 | safe-buffer "5.1.2" 228 | 229 | binary-extensions@^2.0.0: 230 | version "2.2.0" 231 | resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" 232 | integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== 233 | 234 | body-parser@1.20.1: 235 | version "1.20.1" 236 | resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" 237 | integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== 238 | dependencies: 239 | bytes "3.1.2" 240 | content-type "~1.0.4" 241 | debug "2.6.9" 242 | depd "2.0.0" 243 | destroy "1.2.0" 244 | http-errors "2.0.0" 245 | iconv-lite "0.4.24" 246 | on-finished "2.4.1" 247 | qs "6.11.0" 248 | raw-body "2.5.1" 249 | type-is "~1.6.18" 250 | unpipe "1.0.0" 251 | 252 | brace-expansion@^1.1.7: 253 | version "1.1.11" 254 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 255 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 256 | dependencies: 257 | balanced-match "^1.0.0" 258 | concat-map "0.0.1" 259 | 260 | braces@~3.0.2: 261 | version "3.0.2" 262 | resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" 263 | integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== 264 | dependencies: 265 | fill-range "^7.0.1" 266 | 267 | buffer-equal-constant-time@1.0.1: 268 | version "1.0.1" 269 | resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" 270 | integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== 271 | 272 | buffer-from@^1.0.0: 273 | version "1.1.2" 274 | resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" 275 | integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== 276 | 277 | bytes@3.1.2: 278 | version "3.1.2" 279 | resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" 280 | integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== 281 | 282 | call-bind@^1.0.0: 283 | version "1.0.2" 284 | resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" 285 | integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== 286 | dependencies: 287 | function-bind "^1.1.1" 288 | get-intrinsic "^1.0.2" 289 | 290 | chokidar@^3.5.1: 291 | version "3.5.3" 292 | resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" 293 | integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== 294 | dependencies: 295 | anymatch "~3.1.2" 296 | braces "~3.0.2" 297 | glob-parent "~5.1.2" 298 | is-binary-path "~2.1.0" 299 | is-glob "~4.0.1" 300 | normalize-path "~3.0.0" 301 | readdirp "~3.6.0" 302 | optionalDependencies: 303 | fsevents "~2.3.2" 304 | 305 | combined-stream@^1.0.8: 306 | version "1.0.8" 307 | resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" 308 | integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== 309 | dependencies: 310 | delayed-stream "~1.0.0" 311 | 312 | concat-map@0.0.1: 313 | version "0.0.1" 314 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 315 | integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== 316 | 317 | content-disposition@0.5.4: 318 | version "0.5.4" 319 | resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" 320 | integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== 321 | dependencies: 322 | safe-buffer "5.2.1" 323 | 324 | content-type@~1.0.4: 325 | version "1.0.4" 326 | resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" 327 | integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== 328 | 329 | cookie-parser@^1.4.6: 330 | version "1.4.6" 331 | resolved "https://registry.yarnpkg.com/cookie-parser/-/cookie-parser-1.4.6.tgz#3ac3a7d35a7a03bbc7e365073a26074824214594" 332 | integrity sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA== 333 | dependencies: 334 | cookie "0.4.1" 335 | cookie-signature "1.0.6" 336 | 337 | cookie-signature@1.0.6: 338 | version "1.0.6" 339 | resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" 340 | integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== 341 | 342 | cookie@0.4.1: 343 | version "0.4.1" 344 | resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" 345 | integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== 346 | 347 | cookie@0.5.0: 348 | version "0.5.0" 349 | resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" 350 | integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== 351 | 352 | cors@^2.8.5: 353 | version "2.8.5" 354 | resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" 355 | integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== 356 | dependencies: 357 | object-assign "^4" 358 | vary "^1" 359 | 360 | create-require@^1.1.0: 361 | version "1.1.1" 362 | resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" 363 | integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== 364 | 365 | debug@2.6.9: 366 | version "2.6.9" 367 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" 368 | integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== 369 | dependencies: 370 | ms "2.0.0" 371 | 372 | delayed-stream@~1.0.0: 373 | version "1.0.0" 374 | resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" 375 | integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== 376 | 377 | depd@2.0.0, depd@~2.0.0: 378 | version "2.0.0" 379 | resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" 380 | integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== 381 | 382 | destroy@1.2.0: 383 | version "1.2.0" 384 | resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" 385 | integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== 386 | 387 | diff@^4.0.1: 388 | version "4.0.2" 389 | resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" 390 | integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== 391 | 392 | dotenv@^16.0.3: 393 | version "16.0.3" 394 | resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07" 395 | integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ== 396 | 397 | dynamic-dedupe@^0.3.0: 398 | version "0.3.0" 399 | resolved "https://registry.yarnpkg.com/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz#06e44c223f5e4e94d78ef9db23a6515ce2f962a1" 400 | integrity sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ== 401 | dependencies: 402 | xtend "^4.0.0" 403 | 404 | ecdsa-sig-formatter@1.0.11: 405 | version "1.0.11" 406 | resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" 407 | integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== 408 | dependencies: 409 | safe-buffer "^5.0.1" 410 | 411 | ee-first@1.1.1: 412 | version "1.1.1" 413 | resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" 414 | integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== 415 | 416 | encodeurl@~1.0.2: 417 | version "1.0.2" 418 | resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" 419 | integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== 420 | 421 | escape-html@~1.0.3: 422 | version "1.0.3" 423 | resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" 424 | integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== 425 | 426 | etag@~1.8.1: 427 | version "1.8.1" 428 | resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" 429 | integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== 430 | 431 | express@^4.18.2: 432 | version "4.18.2" 433 | resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" 434 | integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== 435 | dependencies: 436 | accepts "~1.3.8" 437 | array-flatten "1.1.1" 438 | body-parser "1.20.1" 439 | content-disposition "0.5.4" 440 | content-type "~1.0.4" 441 | cookie "0.5.0" 442 | cookie-signature "1.0.6" 443 | debug "2.6.9" 444 | depd "2.0.0" 445 | encodeurl "~1.0.2" 446 | escape-html "~1.0.3" 447 | etag "~1.8.1" 448 | finalhandler "1.2.0" 449 | fresh "0.5.2" 450 | http-errors "2.0.0" 451 | merge-descriptors "1.0.1" 452 | methods "~1.1.2" 453 | on-finished "2.4.1" 454 | parseurl "~1.3.3" 455 | path-to-regexp "0.1.7" 456 | proxy-addr "~2.0.7" 457 | qs "6.11.0" 458 | range-parser "~1.2.1" 459 | safe-buffer "5.2.1" 460 | send "0.18.0" 461 | serve-static "1.15.0" 462 | setprototypeof "1.2.0" 463 | statuses "2.0.1" 464 | type-is "~1.6.18" 465 | utils-merge "1.0.1" 466 | vary "~1.1.2" 467 | 468 | fill-range@^7.0.1: 469 | version "7.0.1" 470 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" 471 | integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== 472 | dependencies: 473 | to-regex-range "^5.0.1" 474 | 475 | finalhandler@1.2.0: 476 | version "1.2.0" 477 | resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" 478 | integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== 479 | dependencies: 480 | debug "2.6.9" 481 | encodeurl "~1.0.2" 482 | escape-html "~1.0.3" 483 | on-finished "2.4.1" 484 | parseurl "~1.3.3" 485 | statuses "2.0.1" 486 | unpipe "~1.0.0" 487 | 488 | follow-redirects@^1.15.0: 489 | version "1.15.2" 490 | resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" 491 | integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== 492 | 493 | form-data@^4.0.0: 494 | version "4.0.0" 495 | resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" 496 | integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== 497 | dependencies: 498 | asynckit "^0.4.0" 499 | combined-stream "^1.0.8" 500 | mime-types "^2.1.12" 501 | 502 | forwarded@0.2.0: 503 | version "0.2.0" 504 | resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" 505 | integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== 506 | 507 | fresh@0.5.2: 508 | version "0.5.2" 509 | resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" 510 | integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== 511 | 512 | fs.realpath@^1.0.0: 513 | version "1.0.0" 514 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 515 | integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== 516 | 517 | fsevents@~2.3.2: 518 | version "2.3.2" 519 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" 520 | integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== 521 | 522 | function-bind@^1.1.1: 523 | version "1.1.1" 524 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" 525 | integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== 526 | 527 | get-intrinsic@^1.0.2: 528 | version "1.1.3" 529 | resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385" 530 | integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A== 531 | dependencies: 532 | function-bind "^1.1.1" 533 | has "^1.0.3" 534 | has-symbols "^1.0.3" 535 | 536 | glob-parent@~5.1.2: 537 | version "5.1.2" 538 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" 539 | integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== 540 | dependencies: 541 | is-glob "^4.0.1" 542 | 543 | glob@^7.1.3: 544 | version "7.2.3" 545 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" 546 | integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== 547 | dependencies: 548 | fs.realpath "^1.0.0" 549 | inflight "^1.0.4" 550 | inherits "2" 551 | minimatch "^3.1.1" 552 | once "^1.3.0" 553 | path-is-absolute "^1.0.0" 554 | 555 | has-symbols@^1.0.3: 556 | version "1.0.3" 557 | resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" 558 | integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== 559 | 560 | has@^1.0.3: 561 | version "1.0.3" 562 | resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" 563 | integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== 564 | dependencies: 565 | function-bind "^1.1.1" 566 | 567 | http-errors@2.0.0: 568 | version "2.0.0" 569 | resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" 570 | integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== 571 | dependencies: 572 | depd "2.0.0" 573 | inherits "2.0.4" 574 | setprototypeof "1.2.0" 575 | statuses "2.0.1" 576 | toidentifier "1.0.1" 577 | 578 | iconv-lite@0.4.24: 579 | version "0.4.24" 580 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" 581 | integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== 582 | dependencies: 583 | safer-buffer ">= 2.1.2 < 3" 584 | 585 | inflight@^1.0.4: 586 | version "1.0.6" 587 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 588 | integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== 589 | dependencies: 590 | once "^1.3.0" 591 | wrappy "1" 592 | 593 | inherits@2, inherits@2.0.4: 594 | version "2.0.4" 595 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 596 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 597 | 598 | ipaddr.js@1.9.1: 599 | version "1.9.1" 600 | resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" 601 | integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== 602 | 603 | is-binary-path@~2.1.0: 604 | version "2.1.0" 605 | resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" 606 | integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== 607 | dependencies: 608 | binary-extensions "^2.0.0" 609 | 610 | is-core-module@^2.9.0: 611 | version "2.11.0" 612 | resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" 613 | integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== 614 | dependencies: 615 | has "^1.0.3" 616 | 617 | is-extglob@^2.1.1: 618 | version "2.1.1" 619 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" 620 | integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== 621 | 622 | is-glob@^4.0.1, is-glob@~4.0.1: 623 | version "4.0.3" 624 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" 625 | integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== 626 | dependencies: 627 | is-extglob "^2.1.1" 628 | 629 | is-number@^7.0.0: 630 | version "7.0.0" 631 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" 632 | integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== 633 | 634 | jsonwebtoken@^9.0.0: 635 | version "9.0.0" 636 | resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz#d0faf9ba1cc3a56255fe49c0961a67e520c1926d" 637 | integrity sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw== 638 | dependencies: 639 | jws "^3.2.2" 640 | lodash "^4.17.21" 641 | ms "^2.1.1" 642 | semver "^7.3.8" 643 | 644 | jwa@^1.4.1: 645 | version "1.4.1" 646 | resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" 647 | integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== 648 | dependencies: 649 | buffer-equal-constant-time "1.0.1" 650 | ecdsa-sig-formatter "1.0.11" 651 | safe-buffer "^5.0.1" 652 | 653 | jws@^3.2.2: 654 | version "3.2.2" 655 | resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" 656 | integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== 657 | dependencies: 658 | jwa "^1.4.1" 659 | safe-buffer "^5.0.1" 660 | 661 | lodash@^4.17.21: 662 | version "4.17.21" 663 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" 664 | integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== 665 | 666 | lru-cache@^6.0.0: 667 | version "6.0.0" 668 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" 669 | integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== 670 | dependencies: 671 | yallist "^4.0.0" 672 | 673 | make-error@^1.1.1: 674 | version "1.3.6" 675 | resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" 676 | integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== 677 | 678 | media-typer@0.3.0: 679 | version "0.3.0" 680 | resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" 681 | integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== 682 | 683 | merge-descriptors@1.0.1: 684 | version "1.0.1" 685 | resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" 686 | integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== 687 | 688 | methods@~1.1.2: 689 | version "1.1.2" 690 | resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" 691 | integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== 692 | 693 | mime-db@1.52.0: 694 | version "1.52.0" 695 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" 696 | integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== 697 | 698 | mime-types@^2.1.12, mime-types@~2.1.24, mime-types@~2.1.34: 699 | version "2.1.35" 700 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" 701 | integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== 702 | dependencies: 703 | mime-db "1.52.0" 704 | 705 | mime@1.6.0: 706 | version "1.6.0" 707 | resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" 708 | integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== 709 | 710 | minimatch@^3.1.1: 711 | version "3.1.2" 712 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" 713 | integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== 714 | dependencies: 715 | brace-expansion "^1.1.7" 716 | 717 | minimist@^1.2.6: 718 | version "1.2.7" 719 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" 720 | integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== 721 | 722 | mkdirp@^1.0.4: 723 | version "1.0.4" 724 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" 725 | integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== 726 | 727 | morgan@^1.10.0: 728 | version "1.10.0" 729 | resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7" 730 | integrity sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ== 731 | dependencies: 732 | basic-auth "~2.0.1" 733 | debug "2.6.9" 734 | depd "~2.0.0" 735 | on-finished "~2.3.0" 736 | on-headers "~1.0.2" 737 | 738 | ms@2.0.0: 739 | version "2.0.0" 740 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 741 | integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== 742 | 743 | ms@2.1.3, ms@^2.1.1: 744 | version "2.1.3" 745 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" 746 | integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== 747 | 748 | negotiator@0.6.3: 749 | version "0.6.3" 750 | resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" 751 | integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== 752 | 753 | normalize-path@^3.0.0, normalize-path@~3.0.0: 754 | version "3.0.0" 755 | resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" 756 | integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== 757 | 758 | object-assign@^4: 759 | version "4.1.1" 760 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 761 | integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== 762 | 763 | object-inspect@^1.9.0: 764 | version "1.12.3" 765 | resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" 766 | integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== 767 | 768 | on-finished@2.4.1: 769 | version "2.4.1" 770 | resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" 771 | integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== 772 | dependencies: 773 | ee-first "1.1.1" 774 | 775 | on-finished@~2.3.0: 776 | version "2.3.0" 777 | resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" 778 | integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== 779 | dependencies: 780 | ee-first "1.1.1" 781 | 782 | on-headers@~1.0.2: 783 | version "1.0.2" 784 | resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" 785 | integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== 786 | 787 | once@^1.3.0: 788 | version "1.4.0" 789 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 790 | integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== 791 | dependencies: 792 | wrappy "1" 793 | 794 | parseurl@~1.3.3: 795 | version "1.3.3" 796 | resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" 797 | integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== 798 | 799 | path-is-absolute@^1.0.0: 800 | version "1.0.1" 801 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 802 | integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== 803 | 804 | path-parse@^1.0.7: 805 | version "1.0.7" 806 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" 807 | integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== 808 | 809 | path-to-regexp@0.1.7: 810 | version "0.1.7" 811 | resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" 812 | integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== 813 | 814 | picomatch@^2.0.4, picomatch@^2.2.1: 815 | version "2.3.1" 816 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" 817 | integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== 818 | 819 | prisma@^4.8.1: 820 | version "4.8.1" 821 | resolved "https://registry.yarnpkg.com/prisma/-/prisma-4.8.1.tgz#ef93cd908809b7d02e9f4bead5eea7733ba50c68" 822 | integrity sha512-ZMLnSjwulIeYfaU1O6/LF6PEJzxN5par5weykxMykS9Z6ara/j76JH3Yo2AH3bgJbPN4Z6NeCK9s5fDkzf33cg== 823 | dependencies: 824 | "@prisma/engines" "4.8.1" 825 | 826 | proxy-addr@~2.0.7: 827 | version "2.0.7" 828 | resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" 829 | integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== 830 | dependencies: 831 | forwarded "0.2.0" 832 | ipaddr.js "1.9.1" 833 | 834 | proxy-from-env@^1.1.0: 835 | version "1.1.0" 836 | resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" 837 | integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== 838 | 839 | qs@6.11.0, qs@^6.11.0: 840 | version "6.11.0" 841 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" 842 | integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== 843 | dependencies: 844 | side-channel "^1.0.4" 845 | 846 | range-parser@~1.2.1: 847 | version "1.2.1" 848 | resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" 849 | integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== 850 | 851 | raw-body@2.5.1: 852 | version "2.5.1" 853 | resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" 854 | integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== 855 | dependencies: 856 | bytes "3.1.2" 857 | http-errors "2.0.0" 858 | iconv-lite "0.4.24" 859 | unpipe "1.0.0" 860 | 861 | readdirp@~3.6.0: 862 | version "3.6.0" 863 | resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" 864 | integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== 865 | dependencies: 866 | picomatch "^2.2.1" 867 | 868 | resolve@^1.0.0: 869 | version "1.22.1" 870 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" 871 | integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== 872 | dependencies: 873 | is-core-module "^2.9.0" 874 | path-parse "^1.0.7" 875 | supports-preserve-symlinks-flag "^1.0.0" 876 | 877 | rimraf@^2.6.1: 878 | version "2.7.1" 879 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" 880 | integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== 881 | dependencies: 882 | glob "^7.1.3" 883 | 884 | safe-buffer@5.1.2: 885 | version "5.1.2" 886 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" 887 | integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== 888 | 889 | safe-buffer@5.2.1, safe-buffer@^5.0.1: 890 | version "5.2.1" 891 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" 892 | integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== 893 | 894 | "safer-buffer@>= 2.1.2 < 3": 895 | version "2.1.2" 896 | resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" 897 | integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== 898 | 899 | semver@^7.3.8: 900 | version "7.3.8" 901 | resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" 902 | integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== 903 | dependencies: 904 | lru-cache "^6.0.0" 905 | 906 | send@0.18.0: 907 | version "0.18.0" 908 | resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" 909 | integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== 910 | dependencies: 911 | debug "2.6.9" 912 | depd "2.0.0" 913 | destroy "1.2.0" 914 | encodeurl "~1.0.2" 915 | escape-html "~1.0.3" 916 | etag "~1.8.1" 917 | fresh "0.5.2" 918 | http-errors "2.0.0" 919 | mime "1.6.0" 920 | ms "2.1.3" 921 | on-finished "2.4.1" 922 | range-parser "~1.2.1" 923 | statuses "2.0.1" 924 | 925 | serve-static@1.15.0: 926 | version "1.15.0" 927 | resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" 928 | integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== 929 | dependencies: 930 | encodeurl "~1.0.2" 931 | escape-html "~1.0.3" 932 | parseurl "~1.3.3" 933 | send "0.18.0" 934 | 935 | setprototypeof@1.2.0: 936 | version "1.2.0" 937 | resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" 938 | integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== 939 | 940 | side-channel@^1.0.4: 941 | version "1.0.4" 942 | resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" 943 | integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== 944 | dependencies: 945 | call-bind "^1.0.0" 946 | get-intrinsic "^1.0.2" 947 | object-inspect "^1.9.0" 948 | 949 | source-map-support@^0.5.12: 950 | version "0.5.21" 951 | resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" 952 | integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== 953 | dependencies: 954 | buffer-from "^1.0.0" 955 | source-map "^0.6.0" 956 | 957 | source-map@^0.6.0: 958 | version "0.6.1" 959 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" 960 | integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== 961 | 962 | statuses@2.0.1: 963 | version "2.0.1" 964 | resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" 965 | integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== 966 | 967 | strip-bom@^3.0.0: 968 | version "3.0.0" 969 | resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" 970 | integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== 971 | 972 | strip-json-comments@^2.0.0: 973 | version "2.0.1" 974 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" 975 | integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== 976 | 977 | supports-preserve-symlinks-flag@^1.0.0: 978 | version "1.0.0" 979 | resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" 980 | integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== 981 | 982 | to-regex-range@^5.0.1: 983 | version "5.0.1" 984 | resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" 985 | integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== 986 | dependencies: 987 | is-number "^7.0.0" 988 | 989 | toidentifier@1.0.1: 990 | version "1.0.1" 991 | resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" 992 | integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== 993 | 994 | tree-kill@^1.2.2: 995 | version "1.2.2" 996 | resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" 997 | integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== 998 | 999 | ts-node-dev@^2.0.0: 1000 | version "2.0.0" 1001 | resolved "https://registry.yarnpkg.com/ts-node-dev/-/ts-node-dev-2.0.0.tgz#bdd53e17ab3b5d822ef519928dc6b4a7e0f13065" 1002 | integrity sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w== 1003 | dependencies: 1004 | chokidar "^3.5.1" 1005 | dynamic-dedupe "^0.3.0" 1006 | minimist "^1.2.6" 1007 | mkdirp "^1.0.4" 1008 | resolve "^1.0.0" 1009 | rimraf "^2.6.1" 1010 | source-map-support "^0.5.12" 1011 | tree-kill "^1.2.2" 1012 | ts-node "^10.4.0" 1013 | tsconfig "^7.0.0" 1014 | 1015 | ts-node@^10.4.0: 1016 | version "10.9.1" 1017 | resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" 1018 | integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== 1019 | dependencies: 1020 | "@cspotcode/source-map-support" "^0.8.0" 1021 | "@tsconfig/node10" "^1.0.7" 1022 | "@tsconfig/node12" "^1.0.7" 1023 | "@tsconfig/node14" "^1.0.0" 1024 | "@tsconfig/node16" "^1.0.2" 1025 | acorn "^8.4.1" 1026 | acorn-walk "^8.1.1" 1027 | arg "^4.1.0" 1028 | create-require "^1.1.0" 1029 | diff "^4.0.1" 1030 | make-error "^1.1.1" 1031 | v8-compile-cache-lib "^3.0.1" 1032 | yn "3.1.1" 1033 | 1034 | tsconfig@^7.0.0: 1035 | version "7.0.0" 1036 | resolved "https://registry.yarnpkg.com/tsconfig/-/tsconfig-7.0.0.tgz#84538875a4dc216e5c4a5432b3a4dec3d54e91b7" 1037 | integrity sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw== 1038 | dependencies: 1039 | "@types/strip-bom" "^3.0.0" 1040 | "@types/strip-json-comments" "0.0.30" 1041 | strip-bom "^3.0.0" 1042 | strip-json-comments "^2.0.0" 1043 | 1044 | type-is@~1.6.18: 1045 | version "1.6.18" 1046 | resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" 1047 | integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== 1048 | dependencies: 1049 | media-typer "0.3.0" 1050 | mime-types "~2.1.24" 1051 | 1052 | typescript@^4.9.4: 1053 | version "4.9.4" 1054 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.4.tgz#a2a3d2756c079abda241d75f149df9d561091e78" 1055 | integrity sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg== 1056 | 1057 | unpipe@1.0.0, unpipe@~1.0.0: 1058 | version "1.0.0" 1059 | resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" 1060 | integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== 1061 | 1062 | utils-merge@1.0.1: 1063 | version "1.0.1" 1064 | resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" 1065 | integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== 1066 | 1067 | v8-compile-cache-lib@^3.0.1: 1068 | version "3.0.1" 1069 | resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" 1070 | integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== 1071 | 1072 | vary@^1, vary@~1.1.2: 1073 | version "1.1.2" 1074 | resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" 1075 | integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== 1076 | 1077 | wrappy@1: 1078 | version "1.0.2" 1079 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 1080 | integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== 1081 | 1082 | xtend@^4.0.0: 1083 | version "4.0.2" 1084 | resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" 1085 | integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== 1086 | 1087 | yallist@^4.0.0: 1088 | version "4.0.0" 1089 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" 1090 | integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== 1091 | 1092 | yn@3.1.1: 1093 | version "3.1.1" 1094 | resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" 1095 | integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== 1096 | 1097 | zod@^3.20.2: 1098 | version "3.20.2" 1099 | resolved "https://registry.yarnpkg.com/zod/-/zod-3.20.2.tgz#068606642c8f51b3333981f91c0a8ab37dfc2807" 1100 | integrity sha512-1MzNQdAvO+54H+EaK5YpyEy0T+Ejo/7YLHS93G3RnYWh5gaotGHwGeN/ZO687qEDU2y4CdStQYXVHIgrUl5UVQ== 1101 | --------------------------------------------------------------------------------