Added Husky pre-commits checks

This commit is contained in:
Jyotirmoy Bandyopadhayaya 2022-07-05 12:34:35 +05:30
parent e116b71f12
commit 827cf321f5
22 changed files with 1082 additions and 1982 deletions

4
.husky/pre-commit Executable file
View File

@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx pretty-quick --staged

4
.prettierignore Normal file
View File

@ -0,0 +1,4 @@
*.key
yarn.lock
package-lock.json
*.log

6
.prettierrc Normal file
View File

@ -0,0 +1,6 @@
{
"trailingComma": "es5",
"tabWidth": 4,
"semi": false,
"singleQuote": true
}

View File

@ -1,11 +1,12 @@
# jwt-auth-nodejs
Template to get started with Backend auth module for REST API
# Setup
After pulling this project, create a file named .env in the root of the project and add below information. Change the values of below keys as per your requirement.
- .env example
- .env example
```
JWT_ACCESS_TIME=30s
@ -15,9 +16,9 @@ REDIS_PORT=6379
DB_CONN_STRING=mongodb://127.0.0.1:27017/nodejsjwtauth
```
- Deffi-Hellman key exchange setup
- Deffi-Hellman key exchange setup
```bash
ssh-keygen -t rsa -P "" -b 4096 -m PEM -f jwtRS256.key
ssh-keygen -e -m PEM -f jwtRS256.key > jwtRS256.key.pub
```
```

View File

@ -0,0 +1,153 @@
const User = require('../models/user.model')
const redis_client = require('../helpers/redis_connect')
const {
GenerateAccessToken,
GenerateRefreshToken,
} = require('../services/auth.service')
async function Register(req, res, next) {
// encrypt password
const checkIfUsernameExists = await User.findOne({
username: req.body.username,
}).exec()
const checkIfEmailExists = await User.findOne({
email: req.body.email,
}).exec()
if (checkIfUsernameExists !== null || checkIfEmailExists !== null)
throw new Error('Username or Email already exists')
const user = new User({
username: req.body.username,
first_name: req.body.first_name,
last_name: req.body.last_name,
email: req.body.email,
phone: req.body.phone,
address: {
street: req.body.address,
city: req.body.city,
state: req.body.state,
zip: req.body.zip,
country: req.body.country,
},
birthday: req.body.birthday,
})
user.setPassword(req.body.password)
try {
const saved_user = await user.save()
res.json({
status: true,
message: 'Registered successfully.',
data: saved_user,
})
} catch (error) {
// do logging in DB or file.
next(error)
}
}
async function Login(req, res, next) {
const username = req.body.username
try {
const user = await User.findOne({
username: username,
}).exec()
if (user === null || !user.validatePassword(req.body.password))
throw new Error('Username or Password is not valid')
const access_token = GenerateAccessToken(user._id)
const refresh_token = GenerateRefreshToken(user._id)
return res.json({
status: true,
message: 'Login Successfully.',
data: {
access_token,
refresh_token,
},
})
} catch (error) {
next(error)
}
}
async function Logout(req, res, next) {
try {
const user_id = req.userData.user
const token = req.token
await redis_client.del(user_id.toString())
await redis_client.set('BL_' + user_id.toString(), token)
return res.json({
status: true,
message: 'Successfully Logged out',
})
} catch (error) {
next(error)
}
}
async function checkUsernameAvaiblity(req, res, next) {
const username = req.params.username
try {
const user = await User.findOne({
username: username,
}).exec()
if (user === null)
return res.status(200).json({
status: true,
message: 'Username is available',
})
return res.status(200).json({
status: false,
message: 'Username is not available',
})
} catch (error) {
next(error)
}
}
async function GetOnlyNewAccessToken(req, res, _next) {
const userData = req.userData.user
const access_token = GenerateAccessToken(userData)
return res.json({
status: true,
message: 'Successfully generated new access token.',
data: {
access_token,
},
})
}
async function GetNewToken(req, res, _next) {
const userData = req.userData.user
const access_token = GenerateAccessToken(userData)
const refresh_token = GenerateRefreshToken(userData)
return res.json({
status: true,
message: 'Successfully generated new access token and refresh token.',
data: {
access_token,
refresh_token,
},
})
}
module.exports = {
Register,
Login,
Logout,
checkUsernameAvaiblity,
GetOnlyNewAccessToken,
GetNewToken,
}

View File

@ -1,138 +1,119 @@
const User = require("../models/user.model");
const jwt = require("jsonwebtoken");
const redis_client = require("../redis_connect");
const User = require('../models/user.model')
async function Register(req, res) {
// encrypt password
const user = new User({
username: req.body.username,
});
user.setPassword(req.body.password);
async function GetUserById(req, res, next) {
const user_id = req.params.user_id
try {
const saved_user = await user.save();
res.json({
status: true,
message: "Registered successfully.",
data: saved_user,
});
} catch (error) {
// do logging in DB or file.
res.status(400).json({
status: false,
message: "Something went wrong.",
data: error,
});
}
}
try {
const user = await User.findById(user_id).exec()
async function Login(req, res) {
const username = req.body.username;
if (user === null) throw new Error('User not found')
try {
const user = await User.findOne({
username: username,
}).exec();
if (user === null || !user.validatePassword(req.body.password))
return res.status(401).json({
status: false,
message: "Username or Password is not valid.",
});
// console.log("user", user);
const access_token = jwt.sign(
{
sub: user._id,
},
process.env.JWT_ACCESS_SECRET,
{
expiresIn: process.env.JWT_ACCESS_TIME,
}
);
// console.log("access_token", access_token);
const refresh_token = GenerateRefreshToken(user._id);
return res.json({
status: true,
message: "Login Successfully.",
data: {
access_token,
refresh_token,
},
});
} catch (error) {
return res.status(401).json({
status: true,
message: "Login Failiure.",
data: error,
});
}
}
async function Logout(req, res) {
const user_id = req.userData.sub;
const token = req.token;
await redis_client.del(user_id.toString());
await redis_client.set("BL_" + user_id.toString(), token);
return res.json({
status: true,
message: "Successfully Logged out.",
});
}
function GetAccessToken(req, res) {
const user_id = req.userData.sub;
const access_token = jwt.sign(
{
sub: user_id,
},
process.env.JWT_ACCESS_SECRET,
{
expiresIn: process.env.JWT_ACCESS_TIME,
return res.json({
status: true,
message: 'User found.',
data: user,
})
} catch (error) {
next(error)
}
);
const refresh_token = GenerateRefreshToken(user_id);
return res.json({
status: true,
message: "Success successfully Generated RefreshToken",
data: {
access_token,
refresh_token,
},
});
}
function GenerateRefreshToken(user_id) {
const refresh_token = jwt.sign(
{
sub: user_id,
},
process.env.JWT_REFRESH_SECRET,
{
expiresIn: process.env.JWT_REFRESH_TIME,
async function GetUserByUsername(req, res, next) {
const username = req.params.username
try {
const user = await User.findOne({
username: username,
}).exec()
if (user === null) throw new Error('User not found')
return res.json({
status: true,
message: 'User found.',
data: user,
})
} catch (error) {
next(error)
}
);
}
redis_client.get(user_id.toString(), (err, data) => {
if (err) throw err;
async function GetAllUsers(_req, res, next) {
try {
const users = await User.find().exec()
redis_client.set(
user_id.toString(),
JSON.stringify({
token: refresh_token,
})
);
});
if (users === null) throw new Error('User not found')
return refresh_token;
return res.json({
status: true,
message: 'Users found.',
data: users,
})
} catch (error) {
next(error)
}
}
async function UpdateUser(req, res, next) {
const user_id = req.userData.user
const userData = req.body
try {
const user = await User.findById(user_id).exec()
if (user === null) throw new Error('User not found')
const updatedUser = await User.findByIdAndUpdate(user_id, userData, {
new: true,
}).exec()
return res.json({
status: true,
message: 'User updated.',
data: updatedUser,
})
} catch (error) {
next(error)
}
}
async function Dashboard(req, res, next) {
const user_id = req.userData.user
try {
return res.json({
status: true,
message: 'Hello ' + user_id + ' from dashboard.',
})
} catch (error) {
next(error)
}
}
async function DeleteUser(req, res, next) {
const user_id = req.userData.user
try {
const user = await User.findById(user_id).exec()
if (user === null) throw new Error('User not found')
await User.findByIdAndDelete(user_id).exec()
return res.json({
status: true,
message: 'User deleted.',
})
} catch (error) {
next(error)
}
}
module.exports = {
Register,
Login,
Logout,
GetAccessToken,
};
GetUserById,
GetAllUsers,
GetUserByUsername,
UpdateUser,
Dashboard,
DeleteUser,
}

13
helpers/mongo_connect.js Normal file
View File

@ -0,0 +1,13 @@
const mongoose = require('mongoose')
module.exports = async () => {
await mongoose.connect(
process.env.DB_CONN_STRING,
{
useUnifiedTopology: true,
useNewUrlParser: true,
useFindAndModify: false,
},
() => console.log('Connected to MongoDB 🍀')
)
}

12
helpers/redis_connect.js Normal file
View File

@ -0,0 +1,12 @@
const redis = require('redis')
const redis_client = redis.createClient(
process.env.REDIS_PORT,
process.env.REDIS_HOST
)
redis_client.on('connect', function () {
console.log('Redis Client connected 🍒')
})
module.exports = redis_client

View File

@ -1,22 +1,39 @@
require("dotenv").config();
const express = require("express");
const app = express();
require('dotenv').config()
const express = require('express')
const fs = require('fs')
const morgan = require('morgan')
const cors = require('cors')
const mongoose = require("mongoose");
const app = express()
const connectMongo = require('./helpers/mongo_connect')
const checkENV = require('./utils/checkENV.utils')
mongoose.connect(
process.env.DB_CONN_STRING,
{ useUnifiedTopology: true, useNewUrlParser: true },
() => console.log("Connected to MongoDB 🍀")
);
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
app.use(morgan('tiny'))
app.use(cors())
;(async () => {
try {
await connectMongo()
await checkENV()
} catch (error) {
console.log(error.message)
process.exit(1)
}
})()
app.use(express.json());
const err_routes = require('./utils/errorHandler.utils')
const auth_routes = require("./routes/auth.route");
const user_routes = require("./routes/user.route");
console.log('Auto Loading routes 🎩 ...')
fs.readdirSync('./routes/').forEach(function (file) {
console.log('Loading route: /' + file.split('.')[0])
app.use(`/${file.split('.')[0]}`, require('./routes/' + file))
})
app.use("/auth", auth_routes);
app.use("/user", user_routes);
app.use('*', err_routes.notFound)
app.use(err_routes.logErrors)
app.use(err_routes.clientErrorHandler)
app.use(err_routes.errorHandler)
const port = process.env.PORT || 5000;
app.listen(port, () => console.log(`🤖 API Server is running at ${port} ...`));
const port = process.env.PORT || 5000
app.listen(port, () => console.log(`🤖 API Server is running at ${port} ...`))

11
jsconfig.json Normal file
View File

@ -0,0 +1,11 @@
{
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "Node",
"target": "ES2020",
"jsx": "preserve",
"strictNullChecks": true,
"strictFunctionTypes": true
},
"exclude": ["node_modules", "**/node_modules/*"]
}

View File

@ -1,75 +1,63 @@
const jwt = require("jsonwebtoken");
const redis_client = require("../redis_connect");
const jwt = require('jsonwebtoken')
const redis_client = require('../helpers/redis_connect')
const fs = require('fs')
const publicKey = fs.readFileSync('jwtRS256.key.pub')
function verifyToken(req, res, next) {
try {
// Bearer tokenstring
const token = req.headers.authorization.split(" ")[1];
function verifyToken(req, _res, next) {
try {
// Bearer tokenstring
if (req.headers.authorization === undefined)
throw new Error('Invalid request')
const decoded = jwt.verify(token, process.env.JWT_ACCESS_SECRET);
req.userData = decoded;
const token = req.headers.authorization.split(' ')[1]
req.token = token;
const decoded = jwt.verify(token, publicKey)
// varify blacklisted access token.
redis_client.get("BL_" + decoded.sub.toString(), (err, data) => {
if (err) throw err;
if (decoded.tokenType !== 'access_token')
throw new Error('Invalid token')
if (data === token)
return res.status(401).json({
status: false,
message: "blacklisted token.",
});
next();
});
} catch (error) {
return res.status(401).json({
status: false,
message: "Your session is not valid.",
data: error,
});
}
req.userData = decoded
req.token = token
// verify blacklisted access token.
redis_client.get('BL_' + decoded.user.toString(), (err, data) => {
if (err) throw err
if (data === token) throw new Error('Blacklisted token')
next()
})
} catch (error) {
next(error)
}
}
function verifyRefreshToken(req, res, next) {
const token = req.body.token;
function verifyRefreshToken(req, _res, next) {
const token = req.body.token
if (token === null)
return res.status(401).json({
status: false,
message: "Invalid request.",
});
try {
const decoded = jwt.verify(token, process.env.JWT_REFRESH_SECRET);
req.userData = decoded;
if (token === null) throw new Error('Invalid request')
try {
const decoded = jwt.verify(token, publicKey)
req.userData = decoded
if (decoded.tokenType !== 'refresh_token')
throw new Error('Invalid token.')
// verify if token is in store or not
redis_client.get(decoded.user.toString(), (err, data) => {
if (err) throw err
// verify if token is in store or not
redis_client.get(decoded.sub.toString(), (err, data) => {
if (err) throw err;
if (data === null)
throw new Error('Invalid request :- Token is not in store')
if (JSON.parse(data).token != token)
throw new Error('Invalid request :- Token is not in store')
if (data === null)
return res.status(401).json({
status: false,
message: "Invalid request. Token is not in store.",
});
if (JSON.parse(data).token != token)
return res.status(401).json({
status: false,
message: "Invalid request. Token is not same in store.",
});
next();
});
} catch (error) {
return res.status(401).json({
status: true,
message: "Your session is not valid.",
data: error,
});
}
next()
})
} catch (error) {
next(error)
}
}
module.exports = {
verifyToken,
verifyRefreshToken,
};
verifyToken,
verifyRefreshToken,
}

View File

@ -1,38 +1,97 @@
const mongoose = require("mongoose");
const crypto = require("crypto");
const mongoose = require('mongoose')
const crypto = require('crypto')
const { nanoid } = require('napi-nanoid')
const userSchema = new mongoose.Schema({
username: {
type: String,
require: true,
min: 6,
max: 255,
},
password: {
type: String,
require: true,
},
email: {
type: String,
require: true,
},
name: { type: String },
salt: { type: String },
hash: { type: String },
});
username: {
type: String,
require: true,
min: 6,
max: 255,
},
password: {
type: String,
require: true,
},
email: {
type: String,
require: true,
},
name: {
first_name: {
type: String,
require: true,
},
last_name: {
type: String,
require: true,
},
},
phone: {
type: String,
},
address: {
street: {
type: String,
},
city: {
type: String,
},
state: {
type: String,
},
zip: {
type: String,
},
country: {
type: String,
},
},
birthday: {
type: Date,
},
salt: {
type: String,
},
hash: {
type: String,
},
attributes: {
isVerified: {
type: Boolean,
default: false,
},
emailVerificationToken: {
type: String,
default: nanoid(),
},
isAdmin: {
type: Boolean,
default: false,
},
isActive: {
type: Boolean,
default: false,
},
isDeleted: {
type: Boolean,
default: false,
},
},
})
userSchema.methods.setPassword = function (password) {
this.salt = crypto.randomBytes(16).toString("hex");
this.hash = crypto
.pbkdf2Sync(password, this.salt, 10000, 512, "sha512")
.toString("hex");
};
this.salt = crypto.randomBytes(16).toString('hex')
this.hash = crypto
.pbkdf2Sync(password, this.salt, 10000, 512, 'sha512')
.toString('hex')
}
userSchema.methods.validatePassword = function (password) {
const hash = crypto
.pbkdf2Sync(password, this.salt, 10000, 512, "sha512")
.toString("hex");
return this.hash === hash;
};
const hash = crypto
.pbkdf2Sync(password, this.salt, 10000, 512, 'sha512')
.toString('hex')
return this.hash === hash
}
module.exports = mongoose.model("User", userSchema);
module.exports = mongoose.model('User', userSchema)

1693
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,25 +1,33 @@
{
"name": "jwt-auth-nodejs",
"version": "2.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node index.js",
"dev": "nodemon index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"crypto": "^1.0.1",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"jsonwebtoken": "^8.5.1",
"mongoose": "^5.11.17",
"redis": "^3.0.2"
},
"devDependencies": {
"nodemon": "^2.0.7"
}
"name": "jwt-auth-nodejs",
"version": "2.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node index.js",
"dev": "nodemon index.js",
"pretty": "prettier --write .",
"prepare": "husky install"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"cors": "^2.8.5",
"crypto": "^1.0.1",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"jsonwebtoken": "^8.5.1",
"mongoose": "^5.11.17",
"morgan": "^1.10.0",
"napi-nanoid": "^0.0.3",
"nodemailer": "^6.7.6",
"redis": "^3.0.2"
},
"devDependencies": {
"husky": "^8.0.0",
"nodemon": "^2.0.7",
"prettier": "^2.7.1",
"pretty-quick": "^3.1.3"
}
}

View File

@ -1,12 +0,0 @@
const redis = require("redis");
const redis_client = redis.createClient(
process.env.REDIS_PORT,
process.env.REDIS_HOST
);
redis_client.on("connect", function () {
console.log("Redis Client connected 🍒");
});
module.exports = redis_client;

View File

@ -1,12 +1,20 @@
const route = require('express').Router();
const user_controller = require('../controllers/user.controller');
const auth_middleware = require('../middlewares/auth.middleware');
const route = require('express').Router()
const user_controller = require('../controllers/auth.controller')
const auth_middleware = require('../middlewares/auth.middleware')
route.post('/register', user_controller.Register)
route.post('/login', user_controller.Login)
route.get('/status/:username', user_controller.checkUsernameAvaiblity)
route.post(
'/token',
auth_middleware.verifyRefreshToken,
user_controller.GetOnlyNewAccessToken
)
route.post(
'/token/reset',
auth_middleware.verifyRefreshToken,
user_controller.GetNewToken
)
route.get('/logout', auth_middleware.verifyToken, user_controller.Logout)
route.post('/register', user_controller.Register);
route.post('/login', user_controller.Login);
route.post('/token', auth_middleware.verifyRefreshToken, user_controller.GetAccessToken);
route.get('/logout', auth_middleware.verifyToken, user_controller.Logout);
module.exports = route;
module.exports = route

View File

@ -1,8 +1,20 @@
const route = require('express').Router();
const auth_middleware = require('../middlewares/auth.middleware');
const route = require('express').Router()
const auth_middleware = require('../middlewares/auth.middleware')
const user_controller = require('../controllers/user.controller')
route.get('/dashboard', auth_middleware.verifyToken, (req, res) => {
return res.json({status: true, message: "Hello from dashboard."});
});
route.get('/dashboard', auth_middleware.verifyToken, user_controller.Dashboard)
route.get('/all', auth_middleware.verifyToken, user_controller.GetAllUsers)
route.get(
'/:username',
auth_middleware.verifyToken,
user_controller.GetUserByUsername
)
route.get(
'/id/:user_id',
auth_middleware.verifyRefreshToken,
user_controller.GetUserById
)
route.patch('/', auth_middleware.verifyToken, user_controller.UpdateUser)
route.delete('/', auth_middleware.verifyToken, user_controller.DeleteUser)
module.exports = route;
module.exports = route

View File

@ -0,0 +1,40 @@
const jwt = require('jsonwebtoken')
const redis_client = require('../helpers/redis_connect')
const fs = require('fs')
const privateKey = fs.readFileSync('jwtRS256.key')
function GenerateAccessToken(user_id) {
let userData = {
tokenType: 'access_token',
user: user_id,
}
return jwt.sign(userData, privateKey, {
algorithm: 'RS256',
expiresIn: '1d',
})
}
function GenerateRefreshToken(user_id) {
const setReferesh = {
tokenType: 'refresh_token',
user: user_id,
}
const refresh_token = jwt.sign(setReferesh, privateKey, {
algorithm: 'RS256',
expiresIn: '7d',
})
redis_client.set(
setReferesh.user.toString(),
JSON.stringify({
token: refresh_token,
})
)
return refresh_token
}
module.exports = {
GenerateAccessToken,
GenerateRefreshToken,
}

34
services/mail.service.js Normal file
View File

@ -0,0 +1,34 @@
const nodemailer = require('nodemailer')
let mailClient = nodemailer.createTransport({
host: 'smtp.ethereal.email',
port: 587,
secure: false, // true for 465, false for other ports
auth: {
user: testAccount.user, // generated ethereal user
pass: testAccount.pass, // generated ethereal password
},
})
async function sendMail(body, next) {
const { to, subject, text } = body
const mailOptions = {
from: process.env.MAIL_FROM,
to: to,
subject: subject,
text: text,
}
try {
await mailClient.sendMail(mailOptions)
return res.json({
status: true,
message: 'Mail sent successfully.',
})
} catch (error) {
next(error)
}
}
module.exports = {
sendMail,
}

View File

@ -0,0 +1,33 @@
const { exists } = require('fs')
module.exports = async () => {
const notDiffVars = []
exists('jwtRS256.key', (chk) => {
if (!chk) {
throw new Error('Please generate jwtRS256.key ("Private Key")')
}
})
exists('jwtRS256.key.pub', (chk) => {
if (!chk) {
throw new Error('Please generate jwtRS256.key.pub ("Public Key")')
}
})
console.log('Development environment detected.')
;[
'JWT_ACCESS_TIME',
'JWT_REFRESH_TIME',
'REDIS_PORT',
'REDIS_HOST',
'DB_CONN_STRING',
].forEach((envVar) => {
if (process.env[envVar] === undefined) {
notDiffVars.push(envVar)
}
})
if (notDiffVars.length > 0) {
throw new Error(`${notDiffVars.join(', ')} is not defined`)
}
if (typeof fn === 'function') {
fn()
}
}

View File

@ -0,0 +1,89 @@
async function notFound(_req, _res, next) {
try {
throw new Error('Not Found')
} catch (err) {
next(err)
}
}
async function logErrors(err, _req, _res, next) {
console.error('\n')
console.log('------------Error Log------------')
console.error(err.stack)
console.log('---------------------------------')
next(err)
}
async function clientErrorHandler(err, req, res, next) {
if (req.xhr) {
res.send({
error: 'Something failed!',
})
} else {
next(err)
}
}
async function errorHandler(err, _req, res, _next) {
if (err.message === 'Invalid request') {
return res.status(400).send({
status: false,
message: err.message,
error: err.stack,
})
} else if (err.message === 'Not Found') {
return res.status(404).send({
status: false,
message: err.message,
})
} else if (err.message === 'Invalid token') {
return res.status(403).send({
status: false,
message: err.message,
error: err.stack,
})
} else if (err.message === 'Blacklisted token') {
return res.status(403).send({
status: false,
message: err.message,
error: err.stack,
})
} else if (err.message === 'Invalid token :- Token is not in store') {
return res.status(403).send({
status: false,
message: err.message,
error: err.stack,
})
} else if (err.message === 'User not found') {
return res.status(404).send({
status: false,
message: err.message,
error: err.stack,
})
} else if (err.message === 'Username or Email already exists') {
return res.status(400).send({
status: false,
message: err.message,
error: err.stack,
})
} else if (err.message === 'Username or Password is not valid') {
return res.status(401).send({
status: false,
message: err.message,
error: err.stack,
})
} else {
return res.status(500).send({
status: false,
message: err.message,
error: err.stack,
})
}
}
module.exports = {
notFound,
logErrors,
clientErrorHandler,
errorHandler,
}

336
yarn.lock
View File

@ -28,6 +28,11 @@
dependencies:
"@types/node" "*"
"@types/minimatch@^3.0.3":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40"
integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==
"@types/mongodb@^3.5.27":
version "3.6.20"
resolved "https://registry.yarnpkg.com/@types/mongodb/-/mongodb-3.6.20.tgz#b7c5c580644f6364002b649af1c06c3c0454e1d2"
@ -81,11 +86,26 @@ anymatch@~3.1.2:
normalize-path "^3.0.0"
picomatch "^2.0.4"
array-differ@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-3.0.0.tgz#3cbb3d0f316810eafcc47624734237d6aee4ae6b"
integrity sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==
array-flatten@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=
array-union@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
arrify@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa"
integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==
balanced-match@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
@ -96,6 +116,13 @@ base64-js@^1.3.1:
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
basic-auth@~2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a"
integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==
dependencies:
safe-buffer "5.1.2"
binary-extensions@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
@ -207,6 +234,14 @@ camelcase@^6.2.0:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
chalk@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4"
integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==
dependencies:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
chalk@^4.1.0:
version "4.1.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
@ -303,6 +338,23 @@ core-util-is@~1.0.0:
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==
cors@^2.8.5:
version "2.8.5"
resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29"
integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==
dependencies:
object-assign "^4"
vary "^1"
cross-spawn@^7.0.0:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
dependencies:
path-key "^3.1.0"
shebang-command "^2.0.0"
which "^2.0.1"
crypto-random-string@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5"
@ -361,6 +413,11 @@ depd@~1.1.2:
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
depd@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
destroy@~1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
@ -427,6 +484,21 @@ etag@~1.8.1:
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
execa@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a"
integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==
dependencies:
cross-spawn "^7.0.0"
get-stream "^5.0.0"
human-signals "^1.1.1"
is-stream "^2.0.0"
merge-stream "^2.0.0"
npm-run-path "^4.0.0"
onetime "^5.1.0"
signal-exit "^3.0.2"
strip-final-newline "^2.0.0"
express@^4.17.1:
version "4.17.2"
resolved "https://registry.yarnpkg.com/express/-/express-4.17.2.tgz#c18369f265297319beed4e5558753cc8c1364cb3"
@ -483,6 +555,14 @@ finalhandler@~1.1.2:
statuses "~1.5.0"
unpipe "~1.0.0"
find-up@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
dependencies:
locate-path "^5.0.0"
path-exists "^4.0.0"
forwarded@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
@ -505,7 +585,7 @@ get-stream@^4.1.0:
dependencies:
pump "^3.0.0"
get-stream@^5.1.0:
get-stream@^5.0.0, get-stream@^5.1.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3"
integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==
@ -579,6 +659,16 @@ http-errors@1.8.1:
statuses ">= 1.5.0 < 2"
toidentifier "1.0.1"
human-signals@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3"
integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==
husky@^8.0.0:
version "8.0.1"
resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.1.tgz#511cb3e57de3e3190514ae49ed50f6bc3f50b3e9"
integrity sha512-xs7/chUH/CKdOCs7Zy0Aev9e/dKOMZf3K1Az1nar3tzlv0jfqnYtu235bstsWTmXOR0EfINrPa97yy4Lz6RiKw==
iconv-lite@0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
@ -596,6 +686,11 @@ ignore-by-default@^1.0.1:
resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09"
integrity sha1-SMptcvbGo68Aqa1K5odr44ieKwk=
ignore@^5.1.4:
version "5.2.0"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"
integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==
import-lazy@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43"
@ -685,6 +780,11 @@ is-path-inside@^3.0.2:
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
is-stream@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077"
integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
is-typedarray@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
@ -700,6 +800,11 @@ isarray@~1.0.0:
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
isexe@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
json-buffer@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898"
@ -757,6 +862,13 @@ latest-version@^5.1.0:
dependencies:
package-json "^6.3.0"
locate-path@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
dependencies:
p-locate "^4.1.0"
lodash.includes@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
@ -831,6 +943,11 @@ merge-descriptors@1.0.1:
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=
merge-stream@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
methods@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
@ -853,6 +970,11 @@ mime@1.6.0:
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
mimic-fn@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
mimic-response@^1.0.0, mimic-response@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
@ -908,6 +1030,17 @@ mongoose@^5.11.17:
sift "13.5.2"
sliced "1.0.1"
morgan@^1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7"
integrity sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==
dependencies:
basic-auth "~2.0.1"
debug "2.6.9"
depd "~2.0.0"
on-finished "~2.3.0"
on-headers "~1.0.2"
mpath@0.8.4:
version "0.8.4"
resolved "https://registry.yarnpkg.com/mpath/-/mpath-0.8.4.tgz#6b566d9581621d9e931dd3b142ed3618e7599313"
@ -924,6 +1057,11 @@ mquery@3.2.5:
safe-buffer "5.1.2"
sliced "1.0.1"
mri@^1.1.5:
version "1.2.0"
resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b"
integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
@ -939,11 +1077,111 @@ ms@2.1.3, ms@^2.1.1:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
multimatch@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-4.0.0.tgz#8c3c0f6e3e8449ada0af3dd29efb491a375191b3"
integrity sha512-lDmx79y1z6i7RNx0ZGCPq1bzJ6ZoDDKbvh7jxr9SJcWLkShMzXrHbYVpTdnhNM5MXpDUxCQ4DgqVttVXlBgiBQ==
dependencies:
"@types/minimatch" "^3.0.3"
array-differ "^3.0.0"
array-union "^2.1.0"
arrify "^2.0.1"
minimatch "^3.0.4"
napi-nanoid-android-arm-eabi@0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/napi-nanoid-android-arm-eabi/-/napi-nanoid-android-arm-eabi-0.0.3.tgz#21f77a11e20e661a5a0643f9b06e70b216becbad"
integrity sha512-1EhZZCGQ/vqHKpYHugO3NqxV3B6gCjjwRW3wpWrBLRFo3qrzBQgpglIGuiNuDycFBJ6gCodNQ88qNgKRqRFSUQ==
napi-nanoid-android-arm64@0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/napi-nanoid-android-arm64/-/napi-nanoid-android-arm64-0.0.3.tgz#03909b64f0a48559f1083388e1648ca1c9d95172"
integrity sha512-dMENtTaYJJpCGomk8nKkmhmANnAJY8TPUMhfU4oTkJrO3Noa9RvkayVmihEtBFlqtUQ6FmW5Lb4ptxUGfDvHSg==
napi-nanoid-darwin-arm64@0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/napi-nanoid-darwin-arm64/-/napi-nanoid-darwin-arm64-0.0.3.tgz#8f4c087e1a7f83afd12cf0bf3c8702ce5c4fe1b3"
integrity sha512-HahsVYDHYPtV3l3uZcxTtT+VdgYp/eqnXH+OxvFLT6nyFTYI84jJQfTmTyXn5FmCBFNBxFE5M7INEz9JtJDokQ==
napi-nanoid-darwin-x64@0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/napi-nanoid-darwin-x64/-/napi-nanoid-darwin-x64-0.0.3.tgz#8ccc73a91c8d732edc72a6e16ada4fa24f345d3a"
integrity sha512-hdXoiDLzC1M2O4J/TDyJPdF0rpMCyyI54SJcLtjFDrqxjmhCO2LCNxEePUmzHkBGhwpw9TOoKLoK3RWvdXVoOg==
napi-nanoid-freebsd-x64@0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/napi-nanoid-freebsd-x64/-/napi-nanoid-freebsd-x64-0.0.3.tgz#cf8557ae270215b92e43c9feffd73a2569419a9d"
integrity sha512-8jNQndRbt6XzHkfF9KlAazkAu97jT94fQ+yziYNBTIJqi4bCurLTe40GO0pSRI98z6tAL4wvPaVVM1rPpmoZpw==
napi-nanoid-linux-arm-gnueabihf@0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/napi-nanoid-linux-arm-gnueabihf/-/napi-nanoid-linux-arm-gnueabihf-0.0.3.tgz#d86dbad4305ee6bff9bc148a5403c78b5ae24ede"
integrity sha512-5Rn72mOwX2fJ/VdoFtGZqSOgcI6JQxB8jpnZuddKIsd//YdQdQYU5Xq9D38jMEARTT6jox0fcvVrIrooxsg4Sw==
napi-nanoid-linux-arm64-gnu@0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/napi-nanoid-linux-arm64-gnu/-/napi-nanoid-linux-arm64-gnu-0.0.3.tgz#d1bbed20eb328261d8ae17e0453f98a07ca5c470"
integrity sha512-QXPKE1Owij/fkNxJgL2EyXGkehrfN8SnogMZKnjKbuiddFZrrszZovMvcD50pIfjFTiHXboCYLVC/l+b9QDKYA==
napi-nanoid-linux-arm64-musl@0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/napi-nanoid-linux-arm64-musl/-/napi-nanoid-linux-arm64-musl-0.0.3.tgz#bbb7ec3206fb3902758295ad8609111d3962485c"
integrity sha512-0FMUugyhY0vash3BcH5TiMHgig7DLMagfVnLp81jDqf+8e3Dd5y99KT2gSeFwQy99aY94SbvhpkEF5KncbZEHQ==
napi-nanoid-linux-x64-gnu@0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/napi-nanoid-linux-x64-gnu/-/napi-nanoid-linux-x64-gnu-0.0.3.tgz#a2b42e0cab65d2bfa14e19b51dc5b27e4e617630"
integrity sha512-nLwNirEghKUhRyAYrcSoqWp03AcljvqBFqf3/4+1RAWW1YMKHJb7/BNY5Be2v93oQBSYNH/SBs3GfYZMVkQSRg==
napi-nanoid-linux-x64-musl@0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/napi-nanoid-linux-x64-musl/-/napi-nanoid-linux-x64-musl-0.0.3.tgz#1e2688706ce84327b4440e2d586a61b0819c295a"
integrity sha512-YbzS7QVoZ+ko2aWjohJKatAmJytFYkXrCb3TElF+po9bw0tl6u+oyGmVRi934CxwruMe6T931A11KIGgU1Sh3A==
napi-nanoid-win32-arm64-msvc@0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/napi-nanoid-win32-arm64-msvc/-/napi-nanoid-win32-arm64-msvc-0.0.3.tgz#39b3890ffed2349f7670693de712d3b41d77617d"
integrity sha512-KBNfQxGVlHUNgdalZBvk1QehKSulul3AfXe56iojRZOA0wD8Nj5rdSufXkMkjpGgJwSNlK4V0d0v4Ibcd3K7WQ==
napi-nanoid-win32-ia32-msvc@0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/napi-nanoid-win32-ia32-msvc/-/napi-nanoid-win32-ia32-msvc-0.0.3.tgz#6e6cc4ebed9e90cc3ec3b46b75b3c5ad97b68989"
integrity sha512-L9fNtIitqu+1boXb13DzM0jT2JTmbb7WjN3PA5nbHhlAmoHV7FoTf8HRgQKKmNm+1brnbXEmui6KRoWfgYA+6Q==
napi-nanoid-win32-x64-msvc@0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/napi-nanoid-win32-x64-msvc/-/napi-nanoid-win32-x64-msvc-0.0.3.tgz#d411e82f630f38e1ba1ccad55e7b02a493066732"
integrity sha512-87wMFhBTDVi71Fl2B5JAnPQXCENaNLwWHXrZrj2s49Iextq9HX+FTC0MTMPda8a4VM1EaPZmUakqy9ab8HICMQ==
napi-nanoid@^0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/napi-nanoid/-/napi-nanoid-0.0.3.tgz#8783ccc372e27dfd330daaa09693dd1bfc0d92e2"
integrity sha512-wzt7u6oS1k24Ge3bbUmR5oNdzrNVNhKEot85pG+Fr2Ug2Z2+z71rRRtbnh3pc1XV1toJkRb27uFVQjxeK4F+WA==
optionalDependencies:
napi-nanoid-android-arm-eabi "0.0.3"
napi-nanoid-android-arm64 "0.0.3"
napi-nanoid-darwin-arm64 "0.0.3"
napi-nanoid-darwin-x64 "0.0.3"
napi-nanoid-freebsd-x64 "0.0.3"
napi-nanoid-linux-arm-gnueabihf "0.0.3"
napi-nanoid-linux-arm64-gnu "0.0.3"
napi-nanoid-linux-arm64-musl "0.0.3"
napi-nanoid-linux-x64-gnu "0.0.3"
napi-nanoid-linux-x64-musl "0.0.3"
napi-nanoid-win32-arm64-msvc "0.0.3"
napi-nanoid-win32-ia32-msvc "0.0.3"
napi-nanoid-win32-x64-msvc "0.0.3"
negotiator@0.6.3:
version "0.6.3"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
nodemailer@^6.7.6:
version "6.7.6"
resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.7.6.tgz#d3de8f644eaa0dad784d1be1375c596de492f3fc"
integrity sha512-/6KF/umU7r7X21Y648/yiRLrgkfz0dmpyuo4BfgYWIpnT/jCbkPTvegMfxCsDAu+O810p2L1BGXieMTPp3nJVA==
nodemon@^2.0.7:
version "2.0.15"
resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.15.tgz#504516ce3b43d9dc9a955ccd9ec57550a31a8d4e"
@ -977,6 +1215,18 @@ normalize-url@^4.1.0:
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a"
integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==
npm-run-path@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==
dependencies:
path-key "^3.0.0"
object-assign@^4:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
on-finished@~2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
@ -984,6 +1234,11 @@ on-finished@~2.3.0:
dependencies:
ee-first "1.1.1"
on-headers@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f"
integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==
once@^1.3.1, once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
@ -991,6 +1246,13 @@ once@^1.3.1, once@^1.4.0:
dependencies:
wrappy "1"
onetime@^5.1.0:
version "5.1.2"
resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
dependencies:
mimic-fn "^2.1.0"
optional-require@1.0.x:
version "1.0.3"
resolved "https://registry.yarnpkg.com/optional-require/-/optional-require-1.0.3.tgz#275b8e9df1dc6a17ad155369c2422a440f89cb07"
@ -1008,6 +1270,25 @@ p-cancelable@^1.0.0:
resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc"
integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==
p-limit@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
dependencies:
p-try "^2.0.0"
p-locate@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
dependencies:
p-limit "^2.2.0"
p-try@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
package-json@^6.3.0:
version "6.5.0"
resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0"
@ -1023,6 +1304,16 @@ parseurl@~1.3.3:
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
path-exists@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
path-key@^3.0.0, path-key@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
path-to-regexp@0.1.7:
version "0.1.7"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
@ -1038,6 +1329,23 @@ prepend-http@^2.0.0:
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
prettier@^2.7.1:
version "2.7.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64"
integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==
pretty-quick@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/pretty-quick/-/pretty-quick-3.1.3.tgz#15281108c0ddf446675157ca40240099157b638e"
integrity sha512-kOCi2FJabvuh1as9enxYmrnBC6tVMoVOenMaBqRfsvBHB0cbpYHjdQEpSglpASDFEXVwplpcGR4CLEaisYAFcA==
dependencies:
chalk "^3.0.0"
execa "^4.0.0"
find-up "^4.1.0"
ignore "^5.1.4"
mri "^1.1.5"
multimatch "^4.0.0"
process-nextick-args@~2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
@ -1259,6 +1567,18 @@ setprototypeof@1.2.0:
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
shebang-command@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
dependencies:
shebang-regex "^3.0.0"
shebang-regex@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
sift@13.5.2:
version "13.5.2"
resolved "https://registry.yarnpkg.com/sift/-/sift-13.5.2.tgz#24a715e13c617b086166cd04917d204a591c9da6"
@ -1309,6 +1629,11 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1:
dependencies:
ansi-regex "^5.0.1"
strip-final-newline@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
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"
@ -1426,11 +1751,18 @@ utils-merge@1.0.1:
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
vary@~1.1.2:
vary@^1, vary@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
which@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
dependencies:
isexe "^2.0.0"
widest-line@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca"