From 9cfbf9ba1784e3c921a7029b51f42784e24e7fe4 Mon Sep 17 00:00:00 2001 From: "krish@sentientgeeks.com" Date: Wed, 3 Mar 2021 17:33:59 +0530 Subject: [PATCH] initial commit --- Dockerfile | 14 + README.md | 153 + deploy.sh | 12 + docker-compose.dev.yml | 6 + docker-compose.prod.yml | 6 + docker-compose.test.yml | 6 + docker-compose.yml | 17 + package.json | 100 + src/api/controllers/auth.controller.js | 146 + src/api/controllers/user.controller.js | 102 + src/api/middlewares/auth.js | 54 + src/api/middlewares/error.js | 62 + src/api/models/passwordResetToken.model.js | 57 + src/api/models/refreshToken.model.js | 54 + src/api/models/user.model.js | 236 + src/api/routes/v1/auth.route.js | 150 + src/api/routes/v1/index.js | 20 + src/api/routes/v1/user.route.js | 193 + src/api/services/authProviders.js | 35 + src/api/services/emails/emailProvider.js | 77 + .../services/emails/passwordChange/html.pug | 407 ++ .../emails/passwordChange/subject.pug | 1 + .../services/emails/passwordReset/html.pug | 436 ++ .../services/emails/passwordReset/subject.pug | 1 + src/api/tests/integration/auth.test.js | 528 ++ src/api/tests/integration/user.test.js | 550 ++ src/api/utils/APIError.js | 46 + src/api/utils/passwordResetEmailTemplate.html | 584 ++ src/api/validations/auth.validation.js | 69 + src/api/validations/user.validation.js | 52 + src/config/express.js | 58 + src/config/logger.js | 32 + src/config/mongoose.js | 36 + src/config/passport.js | 35 + src/config/vars.js | 24 + src/index.js | 17 + yarn.lock | 5533 +++++++++++++++++ 37 files changed, 9909 insertions(+) create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 deploy.sh create mode 100644 docker-compose.dev.yml create mode 100644 docker-compose.prod.yml create mode 100644 docker-compose.test.yml create mode 100644 docker-compose.yml create mode 100644 package.json create mode 100644 src/api/controllers/auth.controller.js create mode 100644 src/api/controllers/user.controller.js create mode 100644 src/api/middlewares/auth.js create mode 100644 src/api/middlewares/error.js create mode 100644 src/api/models/passwordResetToken.model.js create mode 100644 src/api/models/refreshToken.model.js create mode 100644 src/api/models/user.model.js create mode 100644 src/api/routes/v1/auth.route.js create mode 100644 src/api/routes/v1/index.js create mode 100644 src/api/routes/v1/user.route.js create mode 100644 src/api/services/authProviders.js create mode 100644 src/api/services/emails/emailProvider.js create mode 100644 src/api/services/emails/passwordChange/html.pug create mode 100644 src/api/services/emails/passwordChange/subject.pug create mode 100644 src/api/services/emails/passwordReset/html.pug create mode 100644 src/api/services/emails/passwordReset/subject.pug create mode 100644 src/api/tests/integration/auth.test.js create mode 100644 src/api/tests/integration/user.test.js create mode 100644 src/api/utils/APIError.js create mode 100644 src/api/utils/passwordResetEmailTemplate.html create mode 100644 src/api/validations/auth.validation.js create mode 100644 src/api/validations/user.validation.js create mode 100644 src/config/express.js create mode 100644 src/config/logger.js create mode 100644 src/config/mongoose.js create mode 100644 src/config/passport.js create mode 100644 src/config/vars.js create mode 100644 src/index.js create mode 100644 yarn.lock diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..0147226 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,14 @@ +FROM node:8-alpine + +EXPOSE 3000 + +ARG NODE_ENV +ENV NODE_ENV $NODE_ENV + +RUN mkdir /app +WORKDIR /app +ADD package.json yarn.lock /app/ +RUN yarn --pure-lockfile +ADD . /app + +CMD ["yarn", "docker:start"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..cb79406 --- /dev/null +++ b/README.md @@ -0,0 +1,153 @@ +# Express ES2017 REST API Boilerplate + + +Boilerplate/Generator/Starter Project for building RESTful APIs and microservices using Node.js, Express and MongoDB + +## Features + + - No transpilers, just vanilla javascript + - ES2017 latest features like Async/Await + - CORS enabled + - Uses [yarn](https://yarnpkg.com) + - Express + MongoDB ([Mongoose](http://mongoosejs.com/)) + - Consistent coding styles with [editorconfig](http://editorconfig.org) + - [Docker](https://www.docker.com/) support + - Uses [helmet](https://github.com/helmetjs/helmet) to set some HTTP headers for security + - Load environment variables from .env files with [dotenv](https://github.com/rolodato/dotenv-safe) + - Request validation with [joi](https://github.com/hapijs/joi) + - Gzip compression with [compression](https://github.com/expressjs/compression) + - Linting with [eslint](http://eslint.org) + - Tests with [mocha](https://mochajs.org), [chai](http://chaijs.com) and [sinon](http://sinonjs.org) + - Code coverage with [istanbul](https://istanbul.js.org) and [coveralls](https://coveralls.io) + - Git hooks with [husky](https://github.com/typicode/husky) + - Logging with [morgan](https://github.com/expressjs/morgan) + - Authentication and Authorization with [passport](http://passportjs.org) + - API documentation generation with [apidoc](http://apidocjs.com) + - Continuous integration support with [travisCI](https://travis-ci.org) + - Monitoring with [pm2](https://github.com/Unitech/pm2) + +## Requirements + + - [Node v7.6+](https://nodejs.org/en/download/current/) or [Docker](https://www.docker.com/) + - [Yarn](https://yarnpkg.com/en/docs/install) + +## Getting Started + +#### Clone the repo and make it yours: + +```bash +git clone --depth 1 https://github.com/krishfsousa/sg-node-express-rest-api +cd sg-node-express-rest-api +rm -rf .git +``` + +#### Install dependencies: + +```bash +yarn +``` + +#### Set environment variables: + +```bash +cp .env.example .env +``` + +## Running Locally + +```bash +yarn dev +``` + +## Running in Production + +```bash +yarn start +``` + +## Lint + +```bash +# lint code with ESLint +yarn lint + +# try to fix ESLint errors +yarn lint:fix + +# lint and watch for changes +yarn lint:watch +``` + +## Test + +```bash +# run all tests with Mocha +yarn test + +# run unit tests +yarn test:unit + +# run integration tests +yarn test:integration + +# run all tests and watch for changes +yarn test:watch + +# open nyc test coverage reports +yarn coverage +``` + +## Validate + +```bash +# run lint and tests +yarn validate +``` + +## Logs + +```bash +# show logs in production +pm2 logs +``` + +## Documentation + +```bash +# generate and open api documentation +yarn docs +``` + +## Docker + +```bash +# run container locally +yarn docker:dev + +# run container in production +yarn docker:prod + +# run tests +yarn docker:test +``` + +## Deploy + +Set your server ip: + +```bash +DEPLOY_SERVER=127.0.0.1 +``` + +Replace my Docker username with yours: + +```bash +nano deploy.sh +``` + +Run deploy script: + +```bash +yarn deploy +``` + diff --git a/deploy.sh b/deploy.sh new file mode 100644 index 0000000..ea1501b --- /dev/null +++ b/deploy.sh @@ -0,0 +1,12 @@ +#!/bin/bash +docker build -t krishfsousa/sg-node-express-rest-api . +docker push krishfsousa/sg-node-express-rest-api + +ssh deploy@$DEPLOY_SERVER << EOF +docker pull krishfsousa/sg-node-express-rest-api +docker stop api-boilerplate || true +docker rm api-boilerplate || true +docker rmi krishfsousa/sg-node-express-rest-api:current || true +docker tag krishfsousa/sg-node-express-rest-api:latest krishfsousa/sg-node-express-rest-api:current +docker run -d --restart always --name api-boilerplate -p 3000:3000 krishfsousa/sg-node-express-rest-api:current +EOF diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 0000000..8c637dc --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,6 @@ +version: "2" +services: + boilerplate-api: + command: yarn dev -- -L + environment: + - NODE_ENV=development diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..2134f5b --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,6 @@ +version: "2" +services: + boilerplate-api: + command: yarn start + environment: + - NODE_ENV=production diff --git a/docker-compose.test.yml b/docker-compose.test.yml new file mode 100644 index 0000000..c742346 --- /dev/null +++ b/docker-compose.test.yml @@ -0,0 +1,6 @@ +version: "2" +services: + boilerplate-api: + command: yarn test + environment: + - NODE_ENV=test diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..eb91a80 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,17 @@ +version: "2" +services: + boilerplate-api: + build: . + environment: + - MONGO_URI=mongodb://mongodb:27017/sg-node-express-rest-api + volumes: + - .:/app + ports: + - "3000:3000" + depends_on: + - mongodb + + mongodb: + image: mongo + ports: + - "27017:27017" diff --git a/package.json b/package.json new file mode 100644 index 0000000..7be602c --- /dev/null +++ b/package.json @@ -0,0 +1,100 @@ +{ + "name": "sg-node-express-rest-api", + "version": "0.0.2", + "description": "Sentientgeeks Boilerplate Node Express RESTApi ES2017", + "author": "sentientgeeks", + "main": "src/index.js", + "engines": { + "node": ">=8", + "yarn": "*" + }, + "scripts": { + "precommit": "yarn lint", + "prestart": "yarn docs", + "start": "cross-env NODE_ENV=production pm2 start ./src/index.js", + "dev": "nodemon ./src/index.js", + "lint": "eslint ./src/ --ignore-path .gitignore --ignore-pattern internals/scripts", + "lint:fix": "yarn lint --fix", + "lint:watch": "yarn lint --watch", + "test": "cross-env NODE_ENV=test nyc --reporter=html --reporter=text mocha --timeout 20000 --exit --recursive src/api/tests", + "test:unit": "cross-env NODE_ENV=test mocha src/api/tests/unit", + "test:integration": "cross-env NODE_ENV=test mocha --timeout 20000 --exit src/api/tests/integration", + "test:watch": "cross-env NODE_ENV=test mocha --watch src/api/tests/unit", + "coverage": "nyc report --reporter=text-lcov | coveralls", + "postcoverage": "open-cli coverage/lcov-report/index.html", + "validate": "yarn lint && yarn test", + "postpublish": "git push --tags", + "deploy": "sh ./deploy.sh", + "docs": "apidoc -i src -o docs", + "postdocs": "open-cli docs/index.html", + "docker:start": "cross-env NODE_ENV=production pm2-docker start ./src/index.js", + "docker:prod": "docker-compose -f docker-compose.yml -f docker-compose.prod.yml up", + "docker:dev": "docker-compose -f docker-compose.yml -f docker-compose.dev.yml up", + "docker:test": "docker-compose -f docker-compose.yml -f docker-compose.test.yml up --abort-on-container-exit" + }, + "repository": { + "type": "git", + "url": "https://krishbhatt@bitbucket.org/krishbhatt/sg-node-express-rest-api.git" + }, + "keywords": [ + "express", + "node", + "node.js", + "mongodb", + "mongoose", + "passport", + "es6", + "es7", + "es8", + "es2017", + "mocha", + "istanbul", + "nyc", + "eslint", + "Travis CI", + "coveralls", + "REST", + "API", + "boilerplate", + "generator", + "starter project" + ], + "dependencies": { + "axios": "^0.19.0", + "bcryptjs": "2.4.3", + "bluebird": "^3.5.0", + "body-parser": "^1.17.0", + "compression": "^1.6.2", + "cors": "^2.8.3", + "cross-env": "^6.0.3", + "dotenv-safe": "^6.0.0", + "email-templates": "^6.0.3", + "express": "^4.15.2", + "express-validation": "^1.0.2", + "nodemailer": "^6.3.1", + "passport": "^0.4.0", + "passport-http-bearer": "^1.0.1", + "passport-jwt": "4.0.0", + "pm2": "^4.2.0", + "pug": "^2.0.4", + "uuid": "^3.1.0", + "winston": "^3.1.0" + }, + "devDependencies": { + "apidoc": "^0.17.5", + "chai": "^4.1.0", + "chai-as-promised": "^7.1.1", + "coveralls": "^3.0.0", + "eslint": "^6.4.0", + "eslint-config-airbnb-base": "^12.0.1", + "eslint-plugin-import": "^2.2.0", + "husky": "^3.0.7", + "mocha": "^6.2.2", + "nodemon": "^2.0.1", + "nyc": "^14.1.1", + "opn-cli": "^5.0.0", + "sinon": "^7.5.0", + "sinon-chai": "^3.0.0", + "supertest": "^4.0.2" + } +} diff --git a/src/api/controllers/auth.controller.js b/src/api/controllers/auth.controller.js new file mode 100644 index 0000000..377380d --- /dev/null +++ b/src/api/controllers/auth.controller.js @@ -0,0 +1,146 @@ +const httpStatus = require('http-status'); +const User = require('../models/user.model'); +const RefreshToken = require('../models/refreshToken.model'); +const PasswordResetToken = require('../models/passwordResetToken.model'); +const moment = require('moment-timezone'); +const { jwtExpirationInterval } = require('../../config/vars'); +const { omit } = require('lodash'); +const APIError = require('../utils/APIError'); +const emailProvider = require('../services/emails/emailProvider'); + +/** + * Returns a formated object with tokens + * @private + */ +function generateTokenResponse(user, accessToken) { + const tokenType = 'Bearer'; + const refreshToken = RefreshToken.generate(user).token; + const expiresIn = moment().add(jwtExpirationInterval, 'minutes'); + return { + tokenType, + accessToken, + refreshToken, + expiresIn, + }; +} + +/** + * Returns jwt token if registration was successful + * @public + */ +exports.register = async (req, res, next) => { + try { + const userData = omit(req.body, 'role'); + const user = await new User(userData).save(); + const userTransformed = user.transform(); + const token = generateTokenResponse(user, user.token()); + res.status(httpStatus.CREATED); + return res.json({ token, user: userTransformed }); + } catch (error) { + return next(User.checkDuplicateEmail(error)); + } +}; + +/** + * Returns jwt token if valid username and password is provided + * @public + */ +exports.login = async (req, res, next) => { + try { + const { user, accessToken } = await User.findAndGenerateToken(req.body); + const token = generateTokenResponse(user, accessToken); + const userTransformed = user.transform(); + return res.json({ token, user: userTransformed }); + } catch (error) { + return next(error); + } +}; + +/** + * login with an existing user or creates a new one if valid accessToken token + * Returns jwt token + * @public + */ +exports.oAuth = async (req, res, next) => { + try { + const { user } = req; + const accessToken = user.token(); + const token = generateTokenResponse(user, accessToken); + const userTransformed = user.transform(); + return res.json({ token, user: userTransformed }); + } catch (error) { + return next(error); + } +}; + +/** + * Returns a new jwt when given a valid refresh token + * @public + */ +exports.refresh = async (req, res, next) => { + try { + const { email, refreshToken } = req.body; + const refreshObject = await RefreshToken.findOneAndRemove({ + userEmail: email, + token: refreshToken, + }); + const { user, accessToken } = await User.findAndGenerateToken({ email, refreshObject }); + const response = generateTokenResponse(user, accessToken); + return res.json(response); + } catch (error) { + return next(error); + } +}; + +exports.sendPasswordReset = async (req, res, next) => { + try { + const { email } = req.body; + const user = await User.findOne({ email }).exec(); + + if (user) { + const passwordResetObj = await PasswordResetToken.generate(user); + emailProvider.sendPasswordReset(passwordResetObj); + res.status(httpStatus.OK); + return res.json('success'); + } + throw new APIError({ + status: httpStatus.UNAUTHORIZED, + message: 'No account found with that email', + }); + } catch (error) { + return next(error); + } +}; + +exports.resetPassword = async (req, res, next) => { + try { + const { email, password, resetToken } = req.body; + const resetTokenObject = await PasswordResetToken.findOneAndRemove({ + userEmail: email, + resetToken, + }); + + const err = { + status: httpStatus.UNAUTHORIZED, + isPublic: true, + }; + if (!resetTokenObject) { + err.message = 'Cannot find matching reset token'; + throw new APIError(err); + } + if (moment().isAfter(resetTokenObject.expires)) { + err.message = 'Reset token is expired'; + throw new APIError(err); + } + + const user = await User.findOne({ email: resetTokenObject.userEmail }).exec(); + user.password = password; + await user.save(); + emailProvider.sendPasswordChangeEmail(user); + + res.status(httpStatus.OK); + return res.json('Password Updated'); + } catch (error) { + return next(error); + } +}; diff --git a/src/api/controllers/user.controller.js b/src/api/controllers/user.controller.js new file mode 100644 index 0000000..f14c8f2 --- /dev/null +++ b/src/api/controllers/user.controller.js @@ -0,0 +1,102 @@ +const httpStatus = require('http-status'); +const { omit } = require('lodash'); +const User = require('../models/user.model'); + +/** + * Load user and append to req. + * @public + */ +exports.load = async (req, res, next, id) => { + try { + const user = await User.get(id); + req.locals = { user }; + return next(); + } catch (error) { + return next(error); + } +}; + +/** + * Get user + * @public + */ +exports.get = (req, res) => res.json(req.locals.user.transform()); + +/** + * Get logged in user info + * @public + */ +exports.loggedIn = (req, res) => res.json(req.user.transform()); + +/** + * Create new user + * @public + */ +exports.create = async (req, res, next) => { + try { + const user = new User(req.body); + const savedUser = await user.save(); + res.status(httpStatus.CREATED); + res.json(savedUser.transform()); + } catch (error) { + next(User.checkDuplicateEmail(error)); + } +}; + +/** + * Replace existing user + * @public + */ +exports.replace = async (req, res, next) => { + try { + const { user } = req.locals; + const newUser = new User(req.body); + const ommitRole = user.role !== 'admin' ? 'role' : ''; + const newUserObject = omit(newUser.toObject(), '_id', ommitRole); + + + res.json(savedUser.transform()); + } catch (error) { + next(User.checkDuplicateEmail(error)); + } +}; + +/** + * Update existing user + * @public + */ +exports.update = (req, res, next) => { + const ommitRole = req.locals.user.role !== 'admin' ? 'role' : ''; + const updatedUser = omit(req.body, ommitRole); + const user = Object.assign(req.locals.user, updatedUser); + + user.save() + .then(savedUser => res.json(savedUser.transform())) + .catch(e => next(User.checkDuplicateEmail(e))); +}; + +/** + * Get user list + * @public + */ +exports.list = async (req, res, next) => { + try { + const users = await User.list(req.query); + const transformedUsers = users.map(user => user.transform()); + res.json(transformedUsers); + } catch (error) { + next(error); + } +}; + +/** + * Delete user + * @public + */ +exports.remove = (req, res, next) => { + const { user } = req.locals; + + user.remove() + .then(() => res.status(httpStatus.NO_CONTENT).end()) + .catch(e => next(e)); +}; diff --git a/src/api/middlewares/auth.js b/src/api/middlewares/auth.js new file mode 100644 index 0000000..bbb24b0 --- /dev/null +++ b/src/api/middlewares/auth.js @@ -0,0 +1,54 @@ +const httpStatus = require('http-status'); +const passport = require('passport'); +const User = require('../models/user.model'); +const APIError = require('../utils/APIError'); + +const ADMIN = 'admin'; +const LOGGED_USER = '_loggedUser'; + +const handleJWT = (req, res, next, roles) => async (err, user, info) => { + const error = err || info; + const logIn = Promise.promisify(req.logIn); + const apiError = new APIError({ + message: error ? error.message : 'Unauthorized', + status: httpStatus.UNAUTHORIZED, + stack: error ? error.stack : undefined, + }); + + try { + if (error || !user) throw error; + await logIn(user, { session: false }); + } catch (e) { + return next(apiError); + } + + if (roles === LOGGED_USER) { + if (user.role !== 'admin' && req.params.userId !== user._id.toString()) { + apiError.status = httpStatus.FORBIDDEN; + apiError.message = 'Forbidden'; + return next(apiError); + } + } else if (!roles.includes(user.role)) { + apiError.status = httpStatus.FORBIDDEN; + apiError.message = 'Forbidden'; + return next(apiError); + } else if (err || !user) { + return next(apiError); + } + + req.user = user; + + return next(); +}; + +exports.ADMIN = ADMIN; +exports.LOGGED_USER = LOGGED_USER; + +exports.authorize = (roles = User.roles) => (req, res, next) => + passport.authenticate( + 'jwt', { session: false }, + handleJWT(req, res, next, roles), + )(req, res, next); + +exports.oAuth = service => + passport.authenticate(service, { session: false }); diff --git a/src/api/middlewares/error.js b/src/api/middlewares/error.js new file mode 100644 index 0000000..8e81a62 --- /dev/null +++ b/src/api/middlewares/error.js @@ -0,0 +1,62 @@ +const httpStatus = require('http-status'); +const expressValidation = require('express-validation'); +const APIError = require('../utils/APIError'); +const { env } = require('../../config/vars'); + +/** + * Error handler. Send stacktrace only during development + * @public + */ +const handler = (err, req, res, next) => { + const response = { + code: err.status, + message: err.message || httpStatus[err.status], + errors: err.errors, + stack: err.stack, + }; + + if (env !== 'development') { + delete response.stack; + } + + res.status(err.status); + res.json(response); +}; +exports.handler = handler; + +/** + * If error is not an instanceOf APIError, convert it. + * @public + */ +exports.converter = (err, req, res, next) => { + let convertedError = err; + + if (err instanceof expressValidation.ValidationError) { + convertedError = new APIError({ + message: 'Validation Error', + errors: err.errors, + status: err.status, + stack: err.stack, + }); + } else if (!(err instanceof APIError)) { + convertedError = new APIError({ + message: err.message, + status: err.status, + stack: err.stack, + }); + } + + return handler(convertedError, req, res); +}; + +/** + * Catch 404 and forward to error handler + * @public + */ +exports.notFound = (req, res, next) => { + const err = new APIError({ + message: 'Not found', + status: httpStatus.NOT_FOUND, + }); + return handler(err, req, res); +}; diff --git a/src/api/models/passwordResetToken.model.js b/src/api/models/passwordResetToken.model.js new file mode 100644 index 0000000..78b1912 --- /dev/null +++ b/src/api/models/passwordResetToken.model.js @@ -0,0 +1,57 @@ +const mongoose = require('mongoose'); +const crypto = require('crypto'); +const moment = require('moment-timezone'); + +/** + * Refresh Token Schema + * @private + */ +const passwordResetTokenSchema = new mongoose.Schema({ + resetToken: { + type: String, + required: true, + index: true, + }, + userId: { + type: mongoose.Schema.Types.ObjectId, + ref: 'User', + required: true, + }, + userEmail: { + type: 'String', + ref: 'User', + required: true, + }, + expires: { type: Date }, +}); + +passwordResetTokenSchema.statics = { + /** + * Generate a reset token object and saves it into the database + * + * @param {User} user + * @returns {ResetToken} + */ + async generate(user) { + const userId = user._id; + const userEmail = user.email; + const resetToken = `${userId}.${crypto.randomBytes(40).toString('hex')}`; + const expires = moment() + .add(2, 'hours') + .toDate(); + const ResetTokenObject = new PasswordResetToken({ + resetToken, + userId, + userEmail, + expires, + }); + await ResetTokenObject.save(); + return ResetTokenObject; + }, +}; + +/** + * @typedef RefreshToken + */ +const PasswordResetToken = mongoose.model('PasswordResetToken', passwordResetTokenSchema); +module.exports = PasswordResetToken; diff --git a/src/api/models/refreshToken.model.js b/src/api/models/refreshToken.model.js new file mode 100644 index 0000000..08fc70b --- /dev/null +++ b/src/api/models/refreshToken.model.js @@ -0,0 +1,54 @@ +const mongoose = require('mongoose'); +const crypto = require('crypto'); +const moment = require('moment-timezone'); + +/** + * Refresh Token Schema + * @private + */ +const refreshTokenSchema = new mongoose.Schema({ + token: { + type: String, + required: true, + index: true, + }, + userId: { + type: mongoose.Schema.Types.ObjectId, + ref: 'User', + required: true, + }, + userEmail: { + type: 'String', + ref: 'User', + required: true, + }, + expires: { type: Date }, +}); + +refreshTokenSchema.statics = { + + /** + * Generate a refresh token object and saves it into the database + * + * @param {User} user + * @returns {RefreshToken} + */ + generate(user) { + const userId = user._id; + const userEmail = user.email; + const token = `${userId}.${crypto.randomBytes(40).toString('hex')}`; + const expires = moment().add(30, 'days').toDate(); + const tokenObject = new RefreshToken({ + token, userId, userEmail, expires, + }); + tokenObject.save(); + return tokenObject; + }, + +}; + +/** + * @typedef RefreshToken + */ +const RefreshToken = mongoose.model('RefreshToken', refreshTokenSchema); +module.exports = RefreshToken; diff --git a/src/api/models/user.model.js b/src/api/models/user.model.js new file mode 100644 index 0000000..e195957 --- /dev/null +++ b/src/api/models/user.model.js @@ -0,0 +1,236 @@ +const mongoose = require('mongoose'); +const httpStatus = require('http-status'); +const { omitBy, isNil } = require('lodash'); +const bcrypt = require('bcryptjs'); +const moment = require('moment-timezone'); +const jwt = require('jwt-simple'); +const uuidv4 = require('uuid/v4'); +const APIError = require('../utils/APIError'); +const { env, jwtSecret, jwtExpirationInterval } = require('../../config/vars'); + +/** +* User Roles +*/ +const roles = ['user', 'admin']; + +/** + * User Schema + * @private + */ +const userSchema = new mongoose.Schema({ + email: { + type: String, + match: /^\S+@\S+\.\S+$/, + required: true, + unique: true, + trim: true, + lowercase: true, + }, + password: { + type: String, + required: true, + minlength: 6, + maxlength: 128, + }, + name: { + type: String, + maxlength: 128, + index: true, + trim: true, + }, + services: { + facebook: String, + google: String, + }, + role: { + type: String, + enum: roles, + default: 'user', + }, + picture: { + type: String, + trim: true, + }, +}, { + timestamps: true, +}); + +/** + * Add your + * - pre-save hooks + * - validations + * - virtuals + */ +userSchema.pre('save', async function save(next) { + try { + if (!this.isModified('password')) return next(); + + const rounds = env === 'test' ? 1 : 10; + + const hash = await bcrypt.hash(this.password, rounds); + this.password = hash; + + return next(); + } catch (error) { + return next(error); + } +}); + +/** + * Methods + */ +userSchema.method({ + transform() { + const transformed = {}; + const fields = ['id', 'name', 'email', 'picture', 'role', 'createdAt']; + + fields.forEach((field) => { + transformed[field] = this[field]; + }); + + return transformed; + }, + + token() { + const payload = { + exp: moment().add(jwtExpirationInterval, 'minutes').unix(), + iat: moment().unix(), + sub: this._id, + }; + return jwt.encode(payload, jwtSecret); + }, + + async passwordMatches(password) { + return bcrypt.compare(password, this.password); + }, +}); + +/** + * Statics + */ +userSchema.statics = { + + roles, + + /** + * Get user + * + * @param {ObjectId} id - The objectId of user. + * @returns {Promise} + */ + async get(id) { + try { + let user; + + if (mongoose.Types.ObjectId.isValid(id)) { + user = await this.findById(id).exec(); + } + if (user) { + return user; + } + + throw new APIError({ + message: 'User does not exist', + status: httpStatus.NOT_FOUND, + }); + } catch (error) { + throw error; + } + }, + + /** + * Find user by email and tries to generate a JWT token + * + * @param {ObjectId} id - The objectId of user. + * @returns {Promise} + */ + async findAndGenerateToken(options) { + const { email, password, refreshObject } = options; + if (!email) throw new APIError({ message: 'An email is required to generate a token' }); + + const user = await this.findOne({ email }).exec(); + const err = { + status: httpStatus.UNAUTHORIZED, + isPublic: true, + }; + if (password) { + if (user && await user.passwordMatches(password)) { + return { user, accessToken: user.token() }; + } + err.message = 'Incorrect email or password'; + } else if (refreshObject && refreshObject.userEmail === email) { + if (moment(refreshObject.expires).isBefore()) { + err.message = 'Invalid refresh token.'; + } else { + return { user, accessToken: user.token() }; + } + } else { + err.message = 'Incorrect email or refreshToken'; + } + throw new APIError(err); + }, + + /** + * List users in descending order of 'createdAt' timestamp. + * + * @param {number} skip - Number of users to be skipped. + * @param {number} limit - Limit number of users to be returned. + * @returns {Promise} + */ + list({ + page = 1, perPage = 30, name, email, role, + }) { + const options = omitBy({ name, email, role }, isNil); + + return this.find(options) + .sort({ createdAt: -1 }) + .skip(perPage * (page - 1)) + .limit(perPage) + .exec(); + }, + + /** + * Return new validation error + * if error is a mongoose duplicate key error + * + * @param {Error} error + * @returns {Error|APIError} + */ + checkDuplicateEmail(error) { + if (error.name === 'MongoError' && error.code === 11000) { + return new APIError({ + message: 'Validation Error', + errors: [{ + field: 'email', + location: 'body', + messages: ['"email" already exists'], + }], + status: httpStatus.CONFLICT, + isPublic: true, + stack: error.stack, + }); + } + return error; + }, + + async oAuthLogin({ + service, id, email, name, picture, + }) { + const user = await this.findOne({ $or: [{ [`services.${service}`]: id }, { email }] }); + if (user) { + user.services[service] = id; + if (!user.name) user.name = name; + if (!user.picture) user.picture = picture; + return user.save(); + } + const password = uuidv4(); + return this.create({ + services: { [service]: id }, email, password, name, picture, + }); + }, +}; + +/** + * @typedef User + */ +module.exports = mongoose.model('User', userSchema); diff --git a/src/api/routes/v1/auth.route.js b/src/api/routes/v1/auth.route.js new file mode 100644 index 0000000..ecb94ff --- /dev/null +++ b/src/api/routes/v1/auth.route.js @@ -0,0 +1,150 @@ +const express = require('express'); +const validate = require('express-validation'); +const controller = require('../../controllers/auth.controller'); +const oAuthLogin = require('../../middlewares/auth').oAuth; +const { + login, + register, + oAuth, + refresh, + sendPasswordReset, + passwordReset, +} = require('../../validations/auth.validation'); + +const router = express.Router(); + +/** + * @api {post} v1/auth/register Register + * @apiDescription Register a new user + * @apiVersion 1.0.0 + * @apiName Register + * @apiGroup Auth + * @apiPermission public + * + * @apiParam {String} email User's email + * @apiParam {String{6..128}} password User's password + * + * @apiSuccess (Created 201) {String} token.tokenType Access Token's type + * @apiSuccess (Created 201) {String} token.accessToken Authorization Token + * @apiSuccess (Created 201) {String} token.refreshToken Token to get a new accessToken + * after expiration time + * @apiSuccess (Created 201) {Number} token.expiresIn Access Token's expiration time + * in miliseconds + * @apiSuccess (Created 201) {String} token.timezone The server's Timezone + * + * @apiSuccess (Created 201) {String} user.id User's id + * @apiSuccess (Created 201) {String} user.name User's name + * @apiSuccess (Created 201) {String} user.email User's email + * @apiSuccess (Created 201) {String} user.role User's role + * @apiSuccess (Created 201) {Date} user.createdAt Timestamp + * + * @apiError (Bad Request 400) ValidationError Some parameters may contain invalid values + */ +router.route('/register') + .post(validate(register), controller.register); + + +/** + * @api {post} v1/auth/login Login + * @apiDescription Get an accessToken + * @apiVersion 1.0.0 + * @apiName Login + * @apiGroup Auth + * @apiPermission public + * + * @apiParam {String} email User's email + * @apiParam {String{..128}} password User's password + * + * @apiSuccess {String} token.tokenType Access Token's type + * @apiSuccess {String} token.accessToken Authorization Token + * @apiSuccess {String} token.refreshToken Token to get a new accessToken + * after expiration time + * @apiSuccess {Number} token.expiresIn Access Token's expiration time + * in miliseconds + * + * @apiSuccess {String} user.id User's id + * @apiSuccess {String} user.name User's name + * @apiSuccess {String} user.email User's email + * @apiSuccess {String} user.role User's role + * @apiSuccess {Date} user.createdAt Timestamp + * + * @apiError (Bad Request 400) ValidationError Some parameters may contain invalid values + * @apiError (Unauthorized 401) Unauthorized Incorrect email or password + */ +router.route('/login') + .post(validate(login), controller.login); + + +/** + * @api {post} v1/auth/refresh-token Refresh Token + * @apiDescription Refresh expired accessToken + * @apiVersion 1.0.0 + * @apiName RefreshToken + * @apiGroup Auth + * @apiPermission public + * + * @apiParam {String} email User's email + * @apiParam {String} refreshToken Refresh token aquired when user logged in + * + * @apiSuccess {String} tokenType Access Token's type + * @apiSuccess {String} accessToken Authorization Token + * @apiSuccess {String} refreshToken Token to get a new accessToken after expiration time + * @apiSuccess {Number} expiresIn Access Token's expiration time in miliseconds + * + * @apiError (Bad Request 400) ValidationError Some parameters may contain invalid values + * @apiError (Unauthorized 401) Unauthorized Incorrect email or refreshToken + */ +router.route('/refresh-token') + .post(validate(refresh), controller.refresh); + + +router.route('/send-password-reset') + .post(validate(sendPasswordReset), controller.sendPasswordReset); + +router.route('/reset-password') + .post(validate(passwordReset), controller.resetPassword); + +/** + * @api {post} v1/auth/facebook Facebook Login + * @apiDescription Login with facebook. Creates a new user if it does not exist + * @apiVersion 1.0.0 + * @apiName FacebookLogin + * @apiGroup Auth + * @apiPermission public + * + * @apiParam {String} access_token Facebook's access_token + * + * @apiSuccess {String} tokenType Access Token's type + * @apiSuccess {String} accessToken Authorization Token + * @apiSuccess {String} refreshToken Token to get a new accessToken after expiration time + * @apiSuccess {Number} expiresIn Access Token's expiration time in miliseconds + * + * @apiError (Bad Request 400) ValidationError Some parameters may contain invalid values + * @apiError (Unauthorized 401) Unauthorized Incorrect access_token + */ +router.route('/facebook') + .post(validate(oAuth), oAuthLogin('facebook'), controller.oAuth); + +/** + * @api {post} v1/auth/google Google Login + * @apiDescription Login with google. Creates a new user if it does not exist + * @apiVersion 1.0.0 + * @apiName GoogleLogin + * @apiGroup Auth + * @apiPermission public + * + * @apiParam {String} access_token Google's access_token + * + * @apiSuccess {String} tokenType Access Token's type + * @apiSuccess {String} accessToken Authorization Token + * @apiSuccess {String} refreshToken Token to get a new accpessToken after expiration time + * @apiSuccess {Number} expiresIn Access Token's expiration time in miliseconds + * + * @apiError (Bad Request 400) ValidationError Some parameters may contain invalid values + * @apiError (Unauthorized 401) Unauthorized Incorrect access_token + */ +router.route('/google') + .post(validate(oAuth), oAuthLogin('google'), controller.oAuth); + + +module.exports = router; diff --git a/src/api/routes/v1/index.js b/src/api/routes/v1/index.js new file mode 100644 index 0000000..40a552e --- /dev/null +++ b/src/api/routes/v1/index.js @@ -0,0 +1,20 @@ +const express = require('express'); +const userRoutes = require('./user.route'); +const authRoutes = require('./auth.route'); + +const router = express.Router(); + +/** + * GET v1/status + */ +router.get('/status', (req, res) => res.send('OK')); + +/** + * GET v1/docs + */ +router.use('/docs', express.static('docs')); + +router.use('/users', userRoutes); +router.use('/auth', authRoutes); + +module.exports = router; diff --git a/src/api/routes/v1/user.route.js b/src/api/routes/v1/user.route.js new file mode 100644 index 0000000..31aa226 --- /dev/null +++ b/src/api/routes/v1/user.route.js @@ -0,0 +1,193 @@ +const express = require('express'); +const validate = require('express-validation'); +const controller = require('../../controllers/user.controller'); +const { authorize, ADMIN, LOGGED_USER } = require('../../middlewares/auth'); +const { + listUsers, + createUser, + replaceUser, + updateUser, +} = require('../../validations/user.validation'); + +const router = express.Router(); + +/** + * Load user when API with userId route parameter is hit + */ +router.param('userId', controller.load); + + +router + .route('/') + /** + * @api {get} v1/users List Users + * @apiDescription Get a list of users + * @apiVersion 1.0.0 + * @apiName ListUsers + * @apiGroup User + * @apiPermission admin + * + * @apiHeader {String} Authorization User's access token + * + * @apiParam {Number{1-}} [page=1] List page + * @apiParam {Number{1-100}} [perPage=1] Users per page + * @apiParam {String} [name] User's name + * @apiParam {String} [email] User's email + * @apiParam {String=user,admin} [role] User's role + * + * @apiSuccess {Object[]} users List of users. + * + * @apiError (Unauthorized 401) Unauthorized Only authenticated users can access the data + * @apiError (Forbidden 403) Forbidden Only admins can access the data + */ + .get(authorize(ADMIN), validate(listUsers), controller.list) + /** + * @api {post} v1/users Create User + * @apiDescription Create a new user + * @apiVersion 1.0.0 + * @apiName CreateUser + * @apiGroup User + * @apiPermission admin + * + * @apiHeader {String} Authorization User's access token + * + * @apiParam {String} email User's email + * @apiParam {String{6..128}} password User's password + * @apiParam {String{..128}} [name] User's name + * @apiParam {String=user,admin} [role] User's role + * + * @apiSuccess (Created 201) {String} id User's id + * @apiSuccess (Created 201) {String} name User's name + * @apiSuccess (Created 201) {String} email User's email + * @apiSuccess (Created 201) {String} role User's role + * @apiSuccess (Created 201) {Date} createdAt Timestamp + * + * @apiError (Bad Request 400) ValidationError Some parameters may contain invalid values + * @apiError (Unauthorized 401) Unauthorized Only authenticated users can create the data + * @apiError (Forbidden 403) Forbidden Only admins can create the data + */ + .post(authorize(ADMIN), validate(createUser), controller.create); + + +router + .route('/profile') + /** + * @api {get} v1/users/profile User Profile + * @apiDescription Get logged in user profile information + * @apiVersion 1.0.0 + * @apiName UserProfile + * @apiGroup User + * @apiPermission user + * + * @apiHeader {String} Authorization User's access token + * + * @apiSuccess {String} id User's id + * @apiSuccess {String} name User's name + * @apiSuccess {String} email User's email + * @apiSuccess {String} role User's role + * @apiSuccess {Date} createdAt Timestamp + * + * @apiError (Unauthorized 401) Unauthorized Only authenticated Users can access the data + */ + .get(authorize(), controller.loggedIn); + + +router + .route('/:userId') + /** + * @api {get} v1/users/:id Get User + * @apiDescription Get user information + * @apiVersion 1.0.0 + * @apiName GetUser + * @apiGroup User + * @apiPermission user + * + * @apiHeader {String} Authorization User's access token + * + * @apiSuccess {String} id User's id + * @apiSuccess {String} name User's name + * @apiSuccess {String} email User's email + * @apiSuccess {String} role User's role + * @apiSuccess {Date} createdAt Timestamp + * + * @apiError (Unauthorized 401) Unauthorized Only authenticated users can access the data + * @apiError (Forbidden 403) Forbidden Only user with same id or admins can access the data + * @apiError (Not Found 404) NotFound User does not exist + */ + .get(authorize(LOGGED_USER), controller.get) + /** + * @api {put} v1/users/:id Replace User + * @apiDescription Replace the whole user document with a new one + * @apiVersion 1.0.0 + * @apiName ReplaceUser + * @apiGroup User + * @apiPermission user + * + * @apiHeader {String} Authorization User's access token + * + * @apiParam {String} email User's email + * @apiParam {String{6..128}} password User's password + * @apiParam {String{..128}} [name] User's name + * @apiParam {String=user,admin} [role] User's role + * (You must be an admin to change the user's role) + * + * @apiSuccess {String} id User's id + * @apiSuccess {String} name User's name + * @apiSuccess {String} email User's email + * @apiSuccess {String} role User's role + * @apiSuccess {Date} createdAt Timestamp + * + * @apiError (Bad Request 400) ValidationError Some parameters may contain invalid values + * @apiError (Unauthorized 401) Unauthorized Only authenticated users can modify the data + * @apiError (Forbidden 403) Forbidden Only user with same id or admins can modify the data + * @apiError (Not Found 404) NotFound User does not exist + */ + .put(authorize(LOGGED_USER), validate(replaceUser), controller.replace) + /** + * @api {patch} v1/users/:id Update User + * @apiDescription Update some fields of a user document + * @apiVersion 1.0.0 + * @apiName UpdateUser + * @apiGroup User + * @apiPermission user + * + * @apiHeader {String} Authorization User's access token + * + * @apiParam {String} email User's email + * @apiParam {String{6..128}} password User's password + * @apiParam {String{..128}} [name] User's name + * @apiParam {String=user,admin} [role] User's role + * (You must be an admin to change the user's role) + * + * @apiSuccess {String} id User's id + * @apiSuccess {String} name User's name + * @apiSuccess {String} email User's email + * @apiSuccess {String} role User's role + * @apiSuccess {Date} createdAt Timestamp + * + * @apiError (Bad Request 400) ValidationError Some parameters may contain invalid values + * @apiError (Unauthorized 401) Unauthorized Only authenticated users can modify the data + * @apiError (Forbidden 403) Forbidden Only user with same id or admins can modify the data + * @apiError (Not Found 404) NotFound User does not exist + */ + .patch(authorize(LOGGED_USER), validate(updateUser), controller.update) + /** + * @api {patch} v1/users/:id Delete User + * @apiDescription Delete a user + * @apiVersion 1.0.0 + * @apiName DeleteUser + * @apiGroup User + * @apiPermission user + * + * @apiHeader {String} Authorization User's access token + * + * @apiSuccess (No Content 204) Successfully deleted + * + * @apiError (Unauthorized 401) Unauthorized Only authenticated users can delete the data + * @apiError (Forbidden 403) Forbidden Only user with same id or admins can delete the data + * @apiError (Not Found 404) NotFound User does not exist + */ + .delete(authorize(LOGGED_USER), controller.remove); + + +module.exports = router; diff --git a/src/api/services/authProviders.js b/src/api/services/authProviders.js new file mode 100644 index 0000000..d4abdbe --- /dev/null +++ b/src/api/services/authProviders.js @@ -0,0 +1,35 @@ +/* eslint-disable camelcase */ +const axios = require('axios'); + +exports.facebook = async (access_token) => { + const fields = 'id, name, email, picture'; + const url = 'https://graph.facebook.com/me'; + const params = { access_token, fields }; + const response = await axios.get(url, { params }); + const { + id, name, email, picture, + } = response.data; + return { + service: 'facebook', + picture: picture.data.url, + id, + name, + email, + }; +}; + +exports.google = async (access_token) => { + const url = 'https://www.googleapis.com/oauth2/v3/userinfo'; + const params = { access_token }; + const response = await axios.get(url, { params }); + const { + sub, name, email, picture, + } = response.data; + return { + service: 'google', + picture, + id: sub, + name, + email, + }; +}; diff --git a/src/api/services/emails/emailProvider.js b/src/api/services/emails/emailProvider.js new file mode 100644 index 0000000..3e2fe7c --- /dev/null +++ b/src/api/services/emails/emailProvider.js @@ -0,0 +1,77 @@ +const nodemailer = require('nodemailer'); +const { emailConfig } = require('../../../config/vars'); +const Email = require('email-templates'); + +// SMTP is the main transport in Nodemailer for delivering messages. +// SMTP is also the protocol used between almost all email hosts, so its truly universal. +// if you dont want to use SMTP you can create your own transport here +// such as an email service API or nodemailer-sendgrid-transport + +const transporter = nodemailer.createTransport({ + port: emailConfig.port, + host: emailConfig.host, + auth: { + user: emailConfig.username, + pass: emailConfig.password, + }, + secure: false, // upgrades later with STARTTLS -- change this based on the PORT +}); + +// verify connection configuration +transporter.verify((error) => { + if (error) { + console.log('error with email connection'); + } +}); + +exports.sendPasswordReset = async (passwordResetObject) => { + const email = new Email({ + views: { root: __dirname }, + message: { + from: 'support@your-app.com', + }, + // uncomment below to send emails in development/test env: + send: true, + transport: transporter, + }); + + email + .send({ + template: 'passwordReset', + message: { + to: passwordResetObject.userEmail, + }, + locals: { + productName: 'Test App', + // passwordResetUrl should be a URL to your app that displays a view where they + // can enter a new password along with passing the resetToken in the params + passwordResetUrl: `https://your-app/new-password/view?resetToken=${passwordResetObject.resetToken}`, + }, + }) + .catch(() => console.log('error sending password reset email')); +}; + +exports.sendPasswordChangeEmail = async (user) => { + const email = new Email({ + views: { root: __dirname }, + message: { + from: 'support@your-app.com', + }, + // uncomment below to send emails in development/test env: + send: true, + transport: transporter, + }); + + email + .send({ + template: 'passwordChange', + message: { + to: user.email, + }, + locals: { + productName: 'Test App', + name: user.name, + }, + }) + .catch(() => console.log('error sending change password email')); +}; diff --git a/src/api/services/emails/passwordChange/html.pug b/src/api/services/emails/passwordChange/html.pug new file mode 100644 index 0000000..807e5c1 --- /dev/null +++ b/src/api/services/emails/passwordChange/html.pug @@ -0,0 +1,407 @@ +doctype transitional +head + meta(name='viewport' content='width=device-width, initial-scale=1.0') + meta(name='x-apple-disable-message-reformatting') + meta(http-equiv='Content-Type' content='text/html; charset=UTF-8') + title + style(type='text/css' rel='stylesheet' media='all'). + /* Base ------------------------------ */ + @import url("https://fonts.googleapis.com/css?family=Nunito+Sans:400,700&display=swap"); + body { + width: 100% !important; + height: 100%; + margin: 0; + -webkit-text-size-adjust: none; + } + a { + color: #3869D4; + } + a img { + border: none; + } + td { + word-break: break-word; + } + .preheader { + display: none !important; + visibility: hidden; + mso-hide: all; + font-size: 1px; + line-height: 1px; + max-height: 0; + max-width: 0; + opacity: 0; + overflow: hidden; + } + /* Type ------------------------------ */ + body, + td, + th { + font-family: "Nunito Sans", Helvetica, Arial, sans-serif; + } + h1 { + margin-top: 0; + color: #333333; + font-size: 22px; + font-weight: bold; + text-align: left; + } + h2 { + margin-top: 0; + color: #333333; + font-size: 16px; + font-weight: bold; + text-align: left; + } + h3 { + margin-top: 0; + color: #333333; + font-size: 14px; + font-weight: bold; + text-align: left; + } + td, + th { + font-size: 16px; + } + p, + ul, + ol, + blockquote { + margin: .4em 0 1.1875em; + font-size: 16px; + line-height: 1.625; + } + p.sub { + font-size: 13px; + } + /* Utilities ------------------------------ */ + .align-right { + text-align: right; + } + .align-left { + text-align: left; + } + .align-center { + text-align: center; + } + /* Buttons ------------------------------ */ + .button { + background-color: #3869D4; + border-top: 10px solid #3869D4; + border-right: 18px solid #3869D4; + border-bottom: 10px solid #3869D4; + border-left: 18px solid #3869D4; + display: inline-block; + color: #FFF; + text-decoration: none; + border-radius: 3px; + box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16); + -webkit-text-size-adjust: none; + box-sizing: border-box; + } + .button--green { + background-color: #22BC66; + border-top: 10px solid #22BC66; + border-right: 18px solid #22BC66; + border-bottom: 10px solid #22BC66; + border-left: 18px solid #22BC66; + } + .button--red { + background-color: #FF6136; + border-top: 10px solid #FF6136; + border-right: 18px solid #FF6136; + border-bottom: 10px solid #FF6136; + border-left: 18px solid #FF6136; + } + @media only screen and (max-width: 500px) { + .button { + width: 100% !important; + text-align: center !important; + } + } + /* Attribute list ------------------------------ */ + .attributes { + margin: 0 0 21px; + } + .attributes_content { + background-color: #F4F4F7; + padding: 16px; + } + .attributes_item { + padding: 0; + } + /* Related Items ------------------------------ */ + .related { + width: 100%; + margin: 0; + padding: 25px 0 0 0; + -premailer-width: 100%; + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + } + .related_item { + padding: 10px 0; + color: #CBCCCF; + font-size: 15px; + line-height: 18px; + } + .related_item-title { + display: block; + margin: .5em 0 0; + } + .related_item-thumb { + display: block; + padding-bottom: 10px; + } + .related_heading { + border-top: 1px solid #CBCCCF; + text-align: center; + padding: 25px 0 10px; + } + /* Discount Code ------------------------------ */ + .discount { + width: 100%; + margin: 0; + padding: 24px; + -premailer-width: 100%; + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + background-color: #F4F4F7; + border: 2px dashed #CBCCCF; + } + .discount_heading { + text-align: center; + } + .discount_body { + text-align: center; + font-size: 15px; + } + /* Social Icons ------------------------------ */ + .social { + width: auto; + } + .social td { + padding: 0; + width: auto; + } + .social_icon { + height: 20px; + margin: 0 8px 10px 8px; + padding: 0; + } + /* Data table ------------------------------ */ + .purchase { + width: 100%; + margin: 0; + padding: 35px 0; + -premailer-width: 100%; + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + } + .purchase_content { + width: 100%; + margin: 0; + padding: 25px 0 0 0; + -premailer-width: 100%; + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + } + .purchase_item { + padding: 10px 0; + color: #51545E; + font-size: 15px; + line-height: 18px; + } + .purchase_heading { + padding-bottom: 8px; + border-bottom: 1px solid #EAEAEC; + } + .purchase_heading p { + margin: 0; + color: #85878E; + font-size: 12px; + } + .purchase_footer { + padding-top: 15px; + border-top: 1px solid #EAEAEC; + } + .purchase_total { + margin: 0; + text-align: right; + font-weight: bold; + color: #333333; + } + .purchase_total--label { + padding: 0 15px 0 0; + } + body { + background-color: #FFF; + color: #333; + } + p { + color: #333; + } + .email-wrapper { + width: 100%; + margin: 0; + padding: 0; + -premailer-width: 100%; + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + } + .email-content { + width: 100%; + margin: 0; + padding: 0; + -premailer-width: 100%; + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + } + /* Masthead ----------------------- */ + .email-masthead { + padding: 25px 0; + text-align: center; + } + .email-masthead_logo { + width: 94px; + } + .email-masthead_name { + font-size: 16px; + font-weight: bold; + color: #A8AAAF; + text-decoration: none; + text-shadow: 0 1px 0 white; + } + /* Body ------------------------------ */ + .email-body { + width: 100%; + margin: 0; + padding: 0; + -premailer-width: 100%; + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + } + .email-body_inner { + width: 570px; + margin: 0 auto; + padding: 0; + -premailer-width: 570px; + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + } + .email-footer { + width: 570px; + margin: 0 auto; + padding: 0; + -premailer-width: 570px; + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + text-align: center; + } + .email-footer p { + color: #A8AAAF; + } + .body-action { + width: 100%; + margin: 30px auto; + padding: 0; + -premailer-width: 100%; + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + text-align: center; + } + .body-sub { + margin-top: 25px; + padding-top: 25px; + border-top: 1px solid #EAEAEC; + } + .content-cell { + padding: 35px; + } + /*Media Queries ------------------------------ */ + @media only screen and (max-width: 600px) { + .email-body_inner, + .email-footer { + width: 100% !important; + } + } + @media (prefers-color-scheme: dark) { + body { + background-color: #333333 !important; + color: #FFF !important; + } + p, + ul, + ol, + blockquote, + h1, + h2, + h3 { + color: #FFF !important; + } + .attributes_content, + .discount { + background-color: #222 !important; + } + .email-masthead_name { + text-shadow: none !important; + } + } + //if mso + style(type='text/css'). + .f-fallback { + font-family: Arial, sans-serif; + } + style(type='text/css' rel='stylesheet' media='all'). + body { + width: 100% !important; + height: 100%; + margin: 0; + -webkit-text-size-adjust: none; + } + body { + font-family: "Nunito Sans", Helvetica, Arial, sans-serif; + } + body { + background-color: #FFF; + color: #333; + } +span.preheader(style='display: none !important; visibility: hidden; mso-hide: all; font-size: 1px; line-height: 1px; max-height: 0; max-width: 0; opacity: 0; overflow: hidden;') + | Hello #{name}, +table.email-wrapper(width='100%' cellpadding='0' cellspacing='0' role='presentation' style='width: 100%; -premailer-width: 100%; -premailer-cellpadding: 0; -premailer-cellspacing: 0; margin: 0; padding: 0;') + tr + td(align='center' style='word-break: break-word; font-family: "Nunito Sans", Helvetica, Arial, sans-serif; font-size: 16px;') + table.email-content(width='100%' cellpadding='0' cellspacing='0' role='presentation' style='width: 100%; -premailer-width: 100%; -premailer-cellpadding: 0; -premailer-cellspacing: 0; margin: 0; padding: 0;') + tr + td.email-masthead(style='word-break: break-word; font-family: "Nunito Sans", Helvetica, Arial, sans-serif; font-size: 16px; text-align: center; padding: 25px 0;' align='center') + a.f-fallback.email-masthead_name(href='https://example.com' style='color: #A8AAAF; font-size: 16px; font-weight: bold; text-decoration: none; text-shadow: 0 1px 0 white;') + | #{productName} + // Email Body + tr + td.email-body(width='570' cellpadding='0' cellspacing='0' style='word-break: break-word; margin: 0; padding: 0; font-family: "Nunito Sans", Helvetica, Arial, sans-serif; font-size: 16px; width: 100%; -premailer-width: 100%; -premailer-cellpadding: 0; -premailer-cellspacing: 0;') + table.email-body_inner(align='center' width='570' cellpadding='0' cellspacing='0' role='presentation' style='width: 570px; -premailer-width: 570px; -premailer-cellpadding: 0; -premailer-cellspacing: 0; margin: 0 auto; padding: 0;') + // Body content + tr + td.content-cell(style='word-break: break-word; font-family: "Nunito Sans", Helvetica, Arial, sans-serif; font-size: 16px; padding: 35px;') + .f-fallback + h1(style='margin-top: 0; color: #333333; font-size: 22px; font-weight: bold; text-align: left;' align='left') Hello, + p(style='font-size: 16px; line-height: 1.625; color: #333; margin: .4em 0 1.1875em;') + | Your password for #{productName} has been changed successfully. You can now login with your new password + // Action + table.body-action(align='center' width='100%' cellpadding='0' cellspacing='0' role='presentation' style='width: 100%; -premailer-width: 100%; -premailer-cellpadding: 0; -premailer-cellspacing: 0; text-align: center; margin: 30px auto; padding: 0;') + tr + td(align='center' style='word-break: break-word; font-family: "Nunito Sans", Helvetica, Arial, sans-serif; font-size: 16px;') + // + Border based button + https://litmus.com/blog/a-guide-to-bulletproof-buttons-in-email-design + table(width='100%' border='0' cellspacing='0' cellpadding='0' role='presentation') + tr + td(align='center' style='word-break: break-word; font-family: "Nunito Sans", Helvetica, Arial, sans-serif; font-size: 16px;') + + tr + td(style='word-break: break-word; font-family: "Nunito Sans", Helvetica, Arial, sans-serif; font-size: 16px;') + table.email-footer(align='center' width='570' cellpadding='0' cellspacing='0' role='presentation' style='width: 570px; -premailer-width: 570px; -premailer-cellpadding: 0; -premailer-cellspacing: 0; text-align: center; margin: 0 auto; padding: 0;') + tr + td.content-cell(align='center' style='word-break: break-word; font-family: "Nunito Sans", Helvetica, Arial, sans-serif; font-size: 16px; padding: 35px;') + p.f-fallback.sub.align-center(style='font-size: 13px; line-height: 1.625; text-align: center; color: #A8AAAF; margin: .4em 0 1.1875em;' align='center') © 2019 #{productName}. All rights reserved. diff --git a/src/api/services/emails/passwordChange/subject.pug b/src/api/services/emails/passwordChange/subject.pug new file mode 100644 index 0000000..8ce127d --- /dev/null +++ b/src/api/services/emails/passwordChange/subject.pug @@ -0,0 +1 @@ += `${productName} Password Changed` \ No newline at end of file diff --git a/src/api/services/emails/passwordReset/html.pug b/src/api/services/emails/passwordReset/html.pug new file mode 100644 index 0000000..ea22d6b --- /dev/null +++ b/src/api/services/emails/passwordReset/html.pug @@ -0,0 +1,436 @@ +doctype transitional +head + meta(name='viewport' content='width=device-width, initial-scale=1.0') + meta(name='x-apple-disable-message-reformatting') + meta(http-equiv='Content-Type' content='text/html; charset=UTF-8') + title + style(type='text/css' rel='stylesheet' media='all'). + /* Base ------------------------------ */ + @import url("https://fonts.googleapis.com/css?family=Nunito+Sans:400,700&display=swap"); + body { + width: 100% !important; + height: 100%; + margin: 0; + -webkit-text-size-adjust: none; + } + a { + color: #3869D4; + } + a img { + border: none; + } + td { + word-break: break-word; + } + .preheader { + display: none !important; + visibility: hidden; + mso-hide: all; + font-size: 1px; + line-height: 1px; + max-height: 0; + max-width: 0; + opacity: 0; + overflow: hidden; + } + /* Type ------------------------------ */ + body, + td, + th { + font-family: "Nunito Sans", Helvetica, Arial, sans-serif; + } + h1 { + margin-top: 0; + color: #333333; + font-size: 22px; + font-weight: bold; + text-align: left; + } + h2 { + margin-top: 0; + color: #333333; + font-size: 16px; + font-weight: bold; + text-align: left; + } + h3 { + margin-top: 0; + color: #333333; + font-size: 14px; + font-weight: bold; + text-align: left; + } + td, + th { + font-size: 16px; + } + p, + ul, + ol, + blockquote { + margin: .4em 0 1.1875em; + font-size: 16px; + line-height: 1.625; + } + p.sub { + font-size: 13px; + } + /* Utilities ------------------------------ */ + .align-right { + text-align: right; + } + .align-left { + text-align: left; + } + .align-center { + text-align: center; + } + /* Buttons ------------------------------ */ + .button { + background-color: #3869D4; + border-top: 10px solid #3869D4; + border-right: 18px solid #3869D4; + border-bottom: 10px solid #3869D4; + border-left: 18px solid #3869D4; + display: inline-block; + color: #FFF; + text-decoration: none; + border-radius: 3px; + box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16); + -webkit-text-size-adjust: none; + box-sizing: border-box; + } + .button--green { + background-color: #22BC66; + border-top: 10px solid #22BC66; + border-right: 18px solid #22BC66; + border-bottom: 10px solid #22BC66; + border-left: 18px solid #22BC66; + } + .button--red { + background-color: #FF6136; + border-top: 10px solid #FF6136; + border-right: 18px solid #FF6136; + border-bottom: 10px solid #FF6136; + border-left: 18px solid #FF6136; + } + @media only screen and (max-width: 500px) { + .button { + width: 100% !important; + text-align: center !important; + } + } + /* Attribute list ------------------------------ */ + .attributes { + margin: 0 0 21px; + } + .attributes_content { + background-color: #F4F4F7; + padding: 16px; + } + .attributes_item { + padding: 0; + } + /* Related Items ------------------------------ */ + .related { + width: 100%; + margin: 0; + padding: 25px 0 0 0; + -premailer-width: 100%; + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + } + .related_item { + padding: 10px 0; + color: #CBCCCF; + font-size: 15px; + line-height: 18px; + } + .related_item-title { + display: block; + margin: .5em 0 0; + } + .related_item-thumb { + display: block; + padding-bottom: 10px; + } + .related_heading { + border-top: 1px solid #CBCCCF; + text-align: center; + padding: 25px 0 10px; + } + /* Discount Code ------------------------------ */ + .discount { + width: 100%; + margin: 0; + padding: 24px; + -premailer-width: 100%; + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + background-color: #F4F4F7; + border: 2px dashed #CBCCCF; + } + .discount_heading { + text-align: center; + } + .discount_body { + text-align: center; + font-size: 15px; + } + /* Social Icons ------------------------------ */ + .social { + width: auto; + } + .social td { + padding: 0; + width: auto; + } + .social_icon { + height: 20px; + margin: 0 8px 10px 8px; + padding: 0; + } + /* Data table ------------------------------ */ + .purchase { + width: 100%; + margin: 0; + padding: 35px 0; + -premailer-width: 100%; + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + } + .purchase_content { + width: 100%; + margin: 0; + padding: 25px 0 0 0; + -premailer-width: 100%; + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + } + .purchase_item { + padding: 10px 0; + color: #51545E; + font-size: 15px; + line-height: 18px; + } + .purchase_heading { + padding-bottom: 8px; + border-bottom: 1px solid #EAEAEC; + } + .purchase_heading p { + margin: 0; + color: #85878E; + font-size: 12px; + } + .purchase_footer { + padding-top: 15px; + border-top: 1px solid #EAEAEC; + } + .purchase_total { + margin: 0; + text-align: right; + font-weight: bold; + color: #333333; + } + .purchase_total--label { + padding: 0 15px 0 0; + } + body { + background-color: #FFF; + color: #333; + } + p { + color: #333; + } + .email-wrapper { + width: 100%; + margin: 0; + padding: 0; + -premailer-width: 100%; + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + } + .email-content { + width: 100%; + margin: 0; + padding: 0; + -premailer-width: 100%; + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + } + /* Masthead ----------------------- */ + .email-masthead { + padding: 25px 0; + text-align: center; + } + .email-masthead_logo { + width: 94px; + } + .email-masthead_name { + font-size: 16px; + font-weight: bold; + color: #A8AAAF; + text-decoration: none; + text-shadow: 0 1px 0 white; + } + /* Body ------------------------------ */ + .email-body { + width: 100%; + margin: 0; + padding: 0; + -premailer-width: 100%; + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + } + .email-body_inner { + width: 570px; + margin: 0 auto; + padding: 0; + -premailer-width: 570px; + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + } + .email-footer { + width: 570px; + margin: 0 auto; + padding: 0; + -premailer-width: 570px; + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + text-align: center; + } + .email-footer p { + color: #A8AAAF; + } + .body-action { + width: 100%; + margin: 30px auto; + padding: 0; + -premailer-width: 100%; + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + text-align: center; + } + .body-sub { + margin-top: 25px; + padding-top: 25px; + border-top: 1px solid #EAEAEC; + } + .content-cell { + padding: 35px; + } + /*Media Queries ------------------------------ */ + @media only screen and (max-width: 600px) { + .email-body_inner, + .email-footer { + width: 100% !important; + } + } + @media (prefers-color-scheme: dark) { + body { + background-color: #333333 !important; + color: #FFF !important; + } + p, + ul, + ol, + blockquote, + h1, + h2, + h3 { + color: #FFF !important; + } + .attributes_content, + .discount { + background-color: #222 !important; + } + .email-masthead_name { + text-shadow: none !important; + } + } + //if mso + style(type='text/css'). + .f-fallback { + font-family: Arial, sans-serif; + } + style(type='text/css' rel='stylesheet' media='all'). + body { + width: 100% !important; + height: 100%; + margin: 0; + -webkit-text-size-adjust: none; + } + body { + font-family: "Nunito Sans", Helvetica, Arial, sans-serif; + } + body { + background-color: #FFF; + color: #333; + } +span.preheader(style='display: none !important; visibility: hidden; mso-hide: all; font-size: 1px; line-height: 1px; max-height: 0; max-width: 0; opacity: 0; overflow: hidden;') + | Use + | this link to reset your password. The link is only valid for 2 hours. +table.email-wrapper(width='100%' cellpadding='0' cellspacing='0' role='presentation' style='width: 100%; -premailer-width: 100%; -premailer-cellpadding: 0; -premailer-cellspacing: 0; margin: 0; padding: 0;') + tr + td(align='center' style='word-break: break-word; font-family: "Nunito Sans", Helvetica, Arial, sans-serif; font-size: 16px;') + table.email-content(width='100%' cellpadding='0' cellspacing='0' role='presentation' style='width: 100%; -premailer-width: 100%; -premailer-cellpadding: 0; -premailer-cellspacing: 0; margin: 0; padding: 0;') + tr + td.email-masthead(style='word-break: break-word; font-family: "Nunito Sans", Helvetica, Arial, sans-serif; font-size: 16px; text-align: center; padding: 25px 0;' align='center') + a.f-fallback.email-masthead_name(href='https://example.com' style='color: #A8AAAF; font-size: 16px; font-weight: bold; text-decoration: none; text-shadow: 0 1px 0 white;') + #{productName} + // Email Body + tr + td.email-body(width='570' cellpadding='0' cellspacing='0' style='word-break: break-word; margin: 0; padding: 0; font-family: "Nunito Sans", Helvetica, Arial, sans-serif; font-size: 16px; width: 100%; -premailer-width: 100%; -premailer-cellpadding: 0; -premailer-cellspacing: 0;') + table.email-body_inner(align='center' width='570' cellpadding='0' cellspacing='0' role='presentation' style='width: 570px; -premailer-width: 570px; -premailer-cellpadding: 0; -premailer-cellspacing: 0; margin: 0 auto; padding: 0;') + // Body content + tr + td.content-cell(style='word-break: break-word; font-family: "Nunito Sans", Helvetica, Arial, sans-serif; font-size: 16px; padding: 35px;') + .f-fallback + h1(style='margin-top: 0; color: #333333; font-size: 22px; font-weight: bold; text-align: left;' align='left') Hi, + p(style='font-size: 16px; line-height: 1.625; color: #333; margin: .4em 0 1.1875em;') + | You recently requested to reset your password for your #{productName} + | account. Use the button below to reset it. + strong + | This password reset + | is only valid for the next 2 hours. + // Action + table.body-action(align='center' width='100%' cellpadding='0' cellspacing='0' role='presentation' style='width: 100%; -premailer-width: 100%; -premailer-cellpadding: 0; -premailer-cellspacing: 0; text-align: center; margin: 30px auto; padding: 0;') + tr + td(align='center' style='word-break: break-word; font-family: "Nunito Sans", Helvetica, Arial, sans-serif; font-size: 16px;') + // + Border based button + https://litmus.com/blog/a-guide-to-bulletproof-buttons-in-email-design + table(width='100%' border='0' cellspacing='0' cellpadding='0' role='presentation') + tr + td(align='center' style='word-break: break-word; font-family: "Nunito Sans", Helvetica, Arial, sans-serif; font-size: 16px;') + a.f-fallback.button.button--green(href='#{passwordResetUrl}' target='_blank' style='color: #FFF; border-color: #22bc66; border-style: solid; border-width: 10px 18px; background-color: #22BC66; display: inline-block; text-decoration: none; border-radius: 3px; box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16); -webkit-text-size-adjust: none; box-sizing: border-box;') + | Reset + | your password + p(style='font-size: 16px; line-height: 1.625; color: #333; margin: .4em 0 1.1875em;') + | If you did not request a password reset, + | please ignore this email. + p(style='font-size: 16px; line-height: 1.625; color: #333; margin: .4em 0 1.1875em;') + | Thanks, + br + | The #{productName} Team + // Sub copy + table.body-sub(role='presentation' style='margin-top: 25px; padding-top: 25px; border-top-width: 1px; border-top-color: #EAEAEC; border-top-style: solid;') + tr + td(style='word-break: break-word; font-family: "Nunito Sans", Helvetica, Arial, sans-serif; font-size: 16px;') + p.f-fallback.sub(style='font-size: 13px; line-height: 1.625; color: #333; margin: .4em 0 1.1875em;') + | If you’re having trouble with the button above, copy and + | paste the URL below into your web browser. + p.f-fallback.sub(style='font-size: 13px; line-height: 1.625; color: #333; margin: .4em 0 1.1875em;') + | #{passwordResetUrl} + tr + td(style='word-break: break-word; font-family: "Nunito Sans", Helvetica, Arial, sans-serif; font-size: 16px;') + table.email-footer(align='center' width='570' cellpadding='0' cellspacing='0' role='presentation' style='width: 570px; -premailer-width: 570px; -premailer-cellpadding: 0; -premailer-cellspacing: 0; text-align: center; margin: 0 auto; padding: 0;') + tr + td.content-cell(align='center' style='word-break: break-word; font-family: "Nunito Sans", Helvetica, Arial, sans-serif; font-size: 16px; padding: 35px;') + p.f-fallback.sub.align-center(style='font-size: 13px; line-height: 1.625; text-align: center; color: #A8AAAF; margin: .4em 0 1.1875em;' align='center') © 2019 [Product Name]. All rights reserved. + p.f-fallback.sub.align-center(style='font-size: 13px; line-height: 1.625; text-align: center; color: #A8AAAF; margin: .4em 0 1.1875em;' align='center') + | [Company Name, LLC] + br + | 1234 Street Rd. + br + | Suite 1234 diff --git a/src/api/services/emails/passwordReset/subject.pug b/src/api/services/emails/passwordReset/subject.pug new file mode 100644 index 0000000..0884072 --- /dev/null +++ b/src/api/services/emails/passwordReset/subject.pug @@ -0,0 +1 @@ += `${productName} Password Reset` \ No newline at end of file diff --git a/src/api/tests/integration/auth.test.js b/src/api/tests/integration/auth.test.js new file mode 100644 index 0000000..3a24c85 --- /dev/null +++ b/src/api/tests/integration/auth.test.js @@ -0,0 +1,528 @@ +/* eslint-disable arrow-body-style */ +const request = require('supertest'); +const httpStatus = require('http-status'); +const { expect } = require('chai'); +const sinon = require('sinon'); +const moment = require('moment-timezone'); +const app = require('../../../index'); +const User = require('../../models/user.model'); +const RefreshToken = require('../../models/refreshToken.model'); +const PasswordResetToken = require('../../models/passwordResetToken.model'); +const authProviders = require('../../services/authProviders'); +const emailProvider = require('../../services/emails/emailProvider'); + +const sandbox = sinon.createSandbox(); + +const fakeOAuthRequest = () => + Promise.resolve({ + service: 'facebook', + id: '123', + name: 'user', + email: 'test@test.com', + picture: 'test.jpg', + }); + +describe('Authentication API', () => { + let dbUser; + let user; + let refreshToken; + let resetToken; + let expiredRefreshToken; + let expiredResetToken; + + beforeEach(async () => { + dbUser = { + email: 'branstark@gmail.com', + password: 'mypassword', + name: 'Bran Stark', + role: 'admin', + }; + + user = { + email: 'krish@gmail.com', + password: '123456', + name: 'krish Sousa', + }; + + refreshToken = { + token: + '5947397b323ae82d8c3a333b.c69d0435e62c9f4953af912442a3d064e20291f0d228c0552ed4be473e7d191ba40b18c2c47e8b9d', + userId: '5947397b323ae82d8c3a333b', + userEmail: dbUser.email, + expires: moment() + .add(1, 'day') + .toDate(), + }; + + resetToken = { + resetToken: + '5947397b323ae82d8c3a333b.c69d0435e62c9f4953af912442a3d064e20291f0d228c0552ed4be473e7d191ba40b18c2c47e8b9d', + userId: '5947397b323ae82d8c3a333b', + userEmail: dbUser.email, + expires: moment() + .add(2, 'hours') + .toDate(), + }; + + expiredRefreshToken = { + token: + '5947397b323ae82d8c3a333b.c69d0435e62c9f4953af912442a3d064e20291f0d228c0552ed4be473e7d191ba40b18c2c47e8b9d', + userId: '5947397b323ae82d8c3a333b', + userEmail: dbUser.email, + expires: moment() + .subtract(1, 'day') + .toDate(), + }; + + expiredResetToken = { + resetToken: + '5947397b323ae82d8c3a333b.c69d0435e62c9f4953af912442a3d064e20291f0d228c0552ed4be473e7d191ba40b18c2c47e8b9d', + userId: '5947397b323ae82d8c3a333b', + userEmail: dbUser.email, + expires: moment() + .subtract(2, 'hours') + .toDate(), + }; + + await User.deleteMany({}); + await User.create(dbUser); + await RefreshToken.deleteMany({}); + await PasswordResetToken.deleteMany({}); + }); + + afterEach(() => sandbox.restore()); + + describe('POST /v1/auth/register', () => { + it('should register a new user when request is ok', () => { + return request(app) + .post('/v1/auth/register') + .send(user) + .expect(httpStatus.CREATED) + .then((res) => { + delete user.password; + expect(res.body.token).to.have.a.property('accessToken'); + expect(res.body.token).to.have.a.property('refreshToken'); + expect(res.body.token).to.have.a.property('expiresIn'); + expect(res.body.user).to.include(user); + }); + }); + + it('should report error when email already exists', () => { + return request(app) + .post('/v1/auth/register') + .send(dbUser) + .expect(httpStatus.CONFLICT) + .then((res) => { + const { field } = res.body.errors[0]; + const { location } = res.body.errors[0]; + const { messages } = res.body.errors[0]; + expect(field).to.be.equal('email'); + expect(location).to.be.equal('body'); + expect(messages).to.include('"email" already exists'); + }); + }); + + it('should report error when the email provided is not valid', () => { + user.email = 'this_is_not_an_email'; + return request(app) + .post('/v1/auth/register') + .send(user) + .expect(httpStatus.BAD_REQUEST) + .then((res) => { + const { field } = res.body.errors[0]; + const { location } = res.body.errors[0]; + const { messages } = res.body.errors[0]; + expect(field).to.be.equal('email'); + expect(location).to.be.equal('body'); + expect(messages).to.include('"email" must be a valid email'); + }); + }); + + it('should report error when email and password are not provided', () => { + return request(app) + .post('/v1/auth/register') + .send({}) + .expect(httpStatus.BAD_REQUEST) + .then((res) => { + const { field } = res.body.errors[0]; + const { location } = res.body.errors[0]; + const { messages } = res.body.errors[0]; + expect(field).to.be.equal('email'); + expect(location).to.be.equal('body'); + expect(messages).to.include('"email" is required'); + }); + }); + }); + + describe('POST /v1/auth/login', () => { + it('should return an accessToken and a refreshToken when email and password matches', () => { + return request(app) + .post('/v1/auth/login') + .send(dbUser) + .expect(httpStatus.OK) + .then((res) => { + delete dbUser.password; + expect(res.body.token).to.have.a.property('accessToken'); + expect(res.body.token).to.have.a.property('refreshToken'); + expect(res.body.token).to.have.a.property('expiresIn'); + expect(res.body.user).to.include(dbUser); + }); + }); + + it('should report error when email and password are not provided', () => { + return request(app) + .post('/v1/auth/login') + .send({}) + .expect(httpStatus.BAD_REQUEST) + .then((res) => { + const { field } = res.body.errors[0]; + const { location } = res.body.errors[0]; + const { messages } = res.body.errors[0]; + expect(field).to.be.equal('email'); + expect(location).to.be.equal('body'); + expect(messages).to.include('"email" is required'); + }); + }); + + it('should report error when the email provided is not valid', () => { + user.email = 'this_is_not_an_email'; + return request(app) + .post('/v1/auth/login') + .send(user) + .expect(httpStatus.BAD_REQUEST) + .then((res) => { + const { field } = res.body.errors[0]; + const { location } = res.body.errors[0]; + const { messages } = res.body.errors[0]; + expect(field).to.be.equal('email'); + expect(location).to.be.equal('body'); + expect(messages).to.include('"email" must be a valid email'); + }); + }); + + it("should report error when email and password don't match", () => { + dbUser.password = 'xxx'; + return request(app) + .post('/v1/auth/login') + .send(dbUser) + .expect(httpStatus.UNAUTHORIZED) + .then((res) => { + const { code } = res.body; + const { message } = res.body; + expect(code).to.be.equal(401); + expect(message).to.be.equal('Incorrect email or password'); + }); + }); + }); + + describe('POST /v1/auth/facebook', () => { + it('should create a new user and return an accessToken when user does not exist', () => { + sandbox.stub(authProviders, 'facebook').callsFake(fakeOAuthRequest); + return request(app) + .post('/v1/auth/facebook') + .send({ access_token: '123' }) + .expect(httpStatus.OK) + .then((res) => { + expect(res.body.token).to.have.a.property('accessToken'); + expect(res.body.token).to.have.a.property('refreshToken'); + expect(res.body.token).to.have.a.property('expiresIn'); + expect(res.body.user).to.be.an('object'); + }); + }); + + it('should return an accessToken when user already exists', async () => { + dbUser.email = 'test@test.com'; + await User.create(dbUser); + sandbox.stub(authProviders, 'facebook').callsFake(fakeOAuthRequest); + return request(app) + .post('/v1/auth/facebook') + .send({ access_token: '123' }) + .expect(httpStatus.OK) + .then((res) => { + expect(res.body.token).to.have.a.property('accessToken'); + expect(res.body.token).to.have.a.property('refreshToken'); + expect(res.body.token).to.have.a.property('expiresIn'); + expect(res.body.user).to.be.an('object'); + }); + }); + + it('should return error when access_token is not provided', async () => { + return request(app) + .post('/v1/auth/facebook') + .expect(httpStatus.BAD_REQUEST) + .then((res) => { + const { field } = res.body.errors[0]; + const { location } = res.body.errors[0]; + const { messages } = res.body.errors[0]; + expect(field).to.be.equal('access_token'); + expect(location).to.be.equal('body'); + expect(messages).to.include('"access_token" is required'); + }); + }); + }); + + describe('POST /v1/auth/google', () => { + it('should create a new user and return an accessToken when user does not exist', () => { + sandbox.stub(authProviders, 'google').callsFake(fakeOAuthRequest); + return request(app) + .post('/v1/auth/google') + .send({ access_token: '123' }) + .expect(httpStatus.OK) + .then((res) => { + expect(res.body.token).to.have.a.property('accessToken'); + expect(res.body.token).to.have.a.property('refreshToken'); + expect(res.body.token).to.have.a.property('expiresIn'); + expect(res.body.user).to.be.an('object'); + }); + }); + + it('should return an accessToken when user already exists', async () => { + dbUser.email = 'test@test.com'; + await User.create(dbUser); + sandbox.stub(authProviders, 'google').callsFake(fakeOAuthRequest); + return request(app) + .post('/v1/auth/google') + .send({ access_token: '123' }) + .expect(httpStatus.OK) + .then((res) => { + expect(res.body.token).to.have.a.property('accessToken'); + expect(res.body.token).to.have.a.property('refreshToken'); + expect(res.body.token).to.have.a.property('expiresIn'); + expect(res.body.user).to.be.an('object'); + }); + }); + + it('should return error when access_token is not provided', async () => { + return request(app) + .post('/v1/auth/google') + .expect(httpStatus.BAD_REQUEST) + .then((res) => { + const { field } = res.body.errors[0]; + const { location } = res.body.errors[0]; + const { messages } = res.body.errors[0]; + expect(field).to.be.equal('access_token'); + expect(location).to.be.equal('body'); + expect(messages).to.include('"access_token" is required'); + }); + }); + }); + + describe('POST /v1/auth/refresh-token', () => { + it('should return a new accessToken when refreshToken and email match', async () => { + await RefreshToken.create(refreshToken); + return request(app) + .post('/v1/auth/refresh-token') + .send({ email: dbUser.email, refreshToken: refreshToken.token }) + .expect(httpStatus.OK) + .then((res) => { + expect(res.body).to.have.a.property('accessToken'); + expect(res.body).to.have.a.property('refreshToken'); + expect(res.body).to.have.a.property('expiresIn'); + }); + }); + + it("should report error when email and refreshToken don't match", async () => { + await RefreshToken.create(refreshToken); + return request(app) + .post('/v1/auth/refresh-token') + .send({ email: user.email, refreshToken: refreshToken.token }) + .expect(httpStatus.UNAUTHORIZED) + .then((res) => { + const { code } = res.body; + const { message } = res.body; + expect(code).to.be.equal(401); + expect(message).to.be.equal('Incorrect email or refreshToken'); + }); + }); + + it('should report error when email and refreshToken are not provided', () => { + return request(app) + .post('/v1/auth/refresh-token') + .send({}) + .expect(httpStatus.BAD_REQUEST) + .then((res) => { + const field1 = res.body.errors[0].field; + const location1 = res.body.errors[0].location; + const messages1 = res.body.errors[0].messages; + const field2 = res.body.errors[1].field; + const location2 = res.body.errors[1].location; + const messages2 = res.body.errors[1].messages; + expect(field1).to.be.equal('email'); + expect(location1).to.be.equal('body'); + expect(messages1).to.include('"email" is required'); + expect(field2).to.be.equal('refreshToken'); + expect(location2).to.be.equal('body'); + expect(messages2).to.include('"refreshToken" is required'); + }); + }); + + it('should report error when the refreshToken is expired', async () => { + await RefreshToken.create(expiredRefreshToken); + + return request(app) + .post('/v1/auth/refresh-token') + .send({ email: dbUser.email, refreshToken: expiredRefreshToken.token }) + .expect(httpStatus.UNAUTHORIZED) + .then((res) => { + expect(res.body.code).to.be.equal(401); + expect(res.body.message).to.be.equal('Invalid refresh token.'); + }); + }); + }); + describe('POST /v1/auth/send-password-reset', () => { + it('should send an email with password reset link when email matches a user', async () => { + const PasswordResetTokenObj = await PasswordResetToken.create(resetToken); + + expect(PasswordResetTokenObj.resetToken).to.be.equal('5947397b323ae82d8c3a333b.c69d0435e62c9f4953af912442a3d064e20291f0d228c0552ed4be473e7d191ba40b18c2c47e8b9d'); + expect(PasswordResetTokenObj.userId.toString()).to.be.equal('5947397b323ae82d8c3a333b'); + expect(PasswordResetTokenObj.userEmail).to.be.equal(dbUser.email); + expect(PasswordResetTokenObj.expires).to.be.above(moment() + .add(1, 'hour') + .toDate()); + + sandbox + .stub(emailProvider, 'sendPasswordReset') + .callsFake(() => Promise.resolve('email sent')); + + return request(app) + .post('/v1/auth/send-password-reset') + .send({ email: dbUser.email }) + .expect(httpStatus.OK) + .then((res) => { + expect(res.body).to.be.equal('success'); + }); + }); + + it("should report error when email doesn't match a user", async () => { + await PasswordResetToken.create(resetToken); + return request(app) + .post('/v1/auth/send-password-reset') + .send({ email: user.email }) + .expect(httpStatus.UNAUTHORIZED) + .then((res) => { + const { code } = res.body; + const { message } = res.body; + expect(code).to.be.equal(httpStatus.UNAUTHORIZED); + expect(message).to.be.equal('No account found with that email'); + }); + }); + + it('should report error when email is not provided', () => { + return request(app) + .post('/v1/auth/send-password-reset') + .send({}) + .expect(httpStatus.BAD_REQUEST) + .then((res) => { + const field1 = res.body.errors[0].field; + const location1 = res.body.errors[0].location; + const messages1 = res.body.errors[0].messages; + expect(field1).to.be.equal('email'); + expect(location1).to.be.equal('body'); + expect(messages1).to.include('"email" is required'); + }); + }); + }); + describe('POST /v1/auth/reset-password', () => { + it('should update password and send confirmation email when email and reset token are valid', async () => { + await PasswordResetToken.create(resetToken); + + sandbox + .stub(emailProvider, 'sendPasswordChangeEmail') + .callsFake(() => Promise.resolve('email sent')); + + return request(app) + .post('/v1/auth/reset-password') + .send({ + email: dbUser.email, + password: 'updatedPassword2', + resetToken: resetToken.resetToken, + }) + .expect(httpStatus.OK) + .then((res) => { + expect(res.body).to.be.equal('Password Updated'); + }); + }); + it("should report error when email and reset token doesn't match a user", async () => { + await PasswordResetToken.create(resetToken); + return request(app) + .post('/v1/auth/reset-password') + .send({ + email: user.email, + password: 'updatedPassword', + resetToken: resetToken.resetToken, + }) + .expect(httpStatus.UNAUTHORIZED) + .then((res) => { + const { code } = res.body; + const { message } = res.body; + expect(code).to.be.equal(401); + expect(message).to.be.equal('Cannot find matching reset token'); + }); + }); + + it('should report error when email is not provided', () => { + return request(app) + .post('/v1/auth/reset-password') + .send({ password: 'updatedPassword', resetToken: resetToken.resetToken }) + .expect(httpStatus.BAD_REQUEST) + .then((res) => { + const field1 = res.body.errors[0].field; + const location1 = res.body.errors[0].location; + const messages1 = res.body.errors[0].messages; + expect(field1).to.be.equal('email'); + expect(location1).to.be.equal('body'); + expect(messages1).to.include('"email" is required'); + }); + }); + it('should report error when reset token is not provided', () => { + return request(app) + .post('/v1/auth/reset-password') + .send({ email: dbUser.email, password: 'updatedPassword' }) + .expect(httpStatus.BAD_REQUEST) + .then((res) => { + const field1 = res.body.errors[0].field; + const location1 = res.body.errors[0].location; + const messages1 = res.body.errors[0].messages; + expect(field1).to.be.equal('resetToken'); + expect(location1).to.be.equal('body'); + expect(messages1).to.include('"resetToken" is required'); + }); + }); + it('should report error when password is not provided', () => { + return request(app) + .post('/v1/auth/reset-password') + .send({ email: dbUser.email, resetToken: resetToken.resetToken }) + .expect(httpStatus.BAD_REQUEST) + .then((res) => { + const field1 = res.body.errors[0].field; + const location1 = res.body.errors[0].location; + const messages1 = res.body.errors[0].messages; + expect(field1).to.be.equal('password'); + expect(location1).to.be.equal('body'); + expect(messages1).to.include('"password" is required'); + }); + }); + + it('should report error when the resetToken is expired', async () => { + const expiredPasswordResetTokenObj = await PasswordResetToken.create(expiredResetToken); + + expect(expiredPasswordResetTokenObj.resetToken).to.be.equal('5947397b323ae82d8c3a333b.c69d0435e62c9f4953af912442a3d064e20291f0d228c0552ed4be473e7d191ba40b18c2c47e8b9d'); + expect(expiredPasswordResetTokenObj.userId.toString()).to.be.equal('5947397b323ae82d8c3a333b'); + expect(expiredPasswordResetTokenObj.userEmail).to.be.equal(dbUser.email); + expect(expiredPasswordResetTokenObj.expires).to.be.below(moment().toDate()); + + return request(app) + .post('/v1/auth/reset-password') + .send({ + email: dbUser.email, + password: 'updated password', + resetToken: expiredResetToken.resetToken, + }) + .expect(httpStatus.UNAUTHORIZED) + .then((res) => { + expect(res.body.code).to.be.equal(401); + expect(res.body.message).to.include('Reset token is expired'); + }); + }); + }); +}); diff --git a/src/api/tests/integration/user.test.js b/src/api/tests/integration/user.test.js new file mode 100644 index 0000000..7611cb4 --- /dev/null +++ b/src/api/tests/integration/user.test.js @@ -0,0 +1,550 @@ +/* eslint-disable arrow-body-style */ +/* eslint-disable no-unused-expressions */ +const request = require('supertest'); +const httpStatus = require('http-status'); +const { expect } = require('chai'); +const sinon = require('sinon'); +const bcrypt = require('bcryptjs'); +const { some, omitBy, isNil } = require('lodash'); +const app = require('../../../index'); +const User = require('../../models/user.model'); +const JWT_EXPIRATION = require('../../../config/vars').jwtExpirationInterval; + +/** + * root level hooks + */ + +async function format(user) { + const formated = user; + + // delete password + delete formated.password; + + // get users from database + const dbUser = (await User.findOne({ email: user.email })).transform(); + + // remove null and undefined properties + return omitBy(dbUser, isNil); +} + +describe('Users API', async () => { + let adminAccessToken; + let userAccessToken; + let dbUsers; + let user; + let admin; + + const password = '123456'; + const passwordHashed = await bcrypt.hash(password, 1); + + beforeEach(async () => { + dbUsers = { + branStark: { + email: 'branstark@gmail.com', + password: passwordHashed, + name: 'Bran Stark', + role: 'admin', + }, + jonSnow: { + email: 'jonsnow@gmail.com', + password: passwordHashed, + name: 'Jon Snow', + }, + }; + + user = { + email: 'sousa.dfs@gmail.com', + password, + name: 'krish Sousa', + }; + + admin = { + email: 'sousa.dfs@gmail.com', + password, + name: 'krish Sousa', + role: 'admin', + }; + + await User.deleteMany({}); + await User.insertMany([dbUsers.branStark, dbUsers.jonSnow]); + dbUsers.branStark.password = password; + dbUsers.jonSnow.password = password; + adminAccessToken = (await User.findAndGenerateToken(dbUsers.branStark)).accessToken; + userAccessToken = (await User.findAndGenerateToken(dbUsers.jonSnow)).accessToken; + }); + + describe('POST /v1/users', () => { + it('should create a new user when request is ok', () => { + return request(app) + .post('/v1/users') + .set('Authorization', `Bearer ${adminAccessToken}`) + .send(admin) + .expect(httpStatus.CREATED) + .then((res) => { + delete admin.password; + expect(res.body).to.include(admin); + }); + }); + + it('should create a new user and set default role to "user"', () => { + return request(app) + .post('/v1/users') + .set('Authorization', `Bearer ${adminAccessToken}`) + .send(user) + .expect(httpStatus.CREATED) + .then((res) => { + expect(res.body.role).to.be.equal('user'); + }); + }); + + it('should report error when email already exists', () => { + user.email = dbUsers.branStark.email; + + return request(app) + .post('/v1/users') + .set('Authorization', `Bearer ${adminAccessToken}`) + .send(user) + .expect(httpStatus.CONFLICT) + .then((res) => { + const { field } = res.body.errors[0]; + const { location } = res.body.errors[0]; + const { messages } = res.body.errors[0]; + expect(field).to.be.equal('email'); + expect(location).to.be.equal('body'); + expect(messages).to.include('"email" already exists'); + }); + }); + + it('should report error when email is not provided', () => { + delete user.email; + + return request(app) + .post('/v1/users') + .set('Authorization', `Bearer ${adminAccessToken}`) + .send(user) + .expect(httpStatus.BAD_REQUEST) + .then((res) => { + const { field } = res.body.errors[0]; + const { location } = res.body.errors[0]; + const { messages } = res.body.errors[0]; + expect(field).to.be.equal('email'); + expect(location).to.be.equal('body'); + expect(messages).to.include('"email" is required'); + }); + }); + + it('should report error when password length is less than 6', () => { + user.password = '12345'; + + return request(app) + .post('/v1/users') + .set('Authorization', `Bearer ${adminAccessToken}`) + .send(user) + .expect(httpStatus.BAD_REQUEST) + .then((res) => { + const { field } = res.body.errors[0]; + const { location } = res.body.errors[0]; + const { messages } = res.body.errors[0]; + expect(field).to.be.equal('password'); + expect(location).to.be.equal('body'); + expect(messages).to.include('"password" length must be at least 6 characters long'); + }); + }); + + it('should report error when logged user is not an admin', () => { + return request(app) + .post('/v1/users') + .set('Authorization', `Bearer ${userAccessToken}`) + .send(user) + .expect(httpStatus.FORBIDDEN) + .then((res) => { + expect(res.body.code).to.be.equal(httpStatus.FORBIDDEN); + expect(res.body.message).to.be.equal('Forbidden'); + }); + }); + }); + + describe('GET /v1/users', () => { + it('should get all users', () => { + return request(app) + .get('/v1/users') + .set('Authorization', `Bearer ${adminAccessToken}`) + .expect(httpStatus.OK) + .then(async (res) => { + const bran = await format(dbUsers.branStark); + const john = await format(dbUsers.jonSnow); + // before comparing it is necessary to convert String to Date + res.body[0].createdAt = new Date(res.body[0].createdAt); + res.body[1].createdAt = new Date(res.body[1].createdAt); + + const includesBranStark = some(res.body, bran); + const includesjonSnow = some(res.body, john); + + expect(res.body).to.be.an('array'); + expect(res.body).to.have.lengthOf(2); + expect(includesBranStark).to.be.true; + expect(includesjonSnow).to.be.true; + }); + }); + + it('should get all users with pagination', () => { + return request(app) + .get('/v1/users') + .set('Authorization', `Bearer ${adminAccessToken}`) + .query({ page: 2, perPage: 1 }) + .expect(httpStatus.OK) + .then(async (res) => { + delete dbUsers.jonSnow.password; + + expect(res.body).to.be.an('array'); + expect(res.body[0]).to.be.an('object'); + expect(res.body).to.have.lengthOf(1); + expect(res.body[0].name).to.be.equal('Jon Snow'); + }); + }); + + it('should filter users', () => { + return request(app) + .get('/v1/users') + .set('Authorization', `Bearer ${adminAccessToken}`) + .query({ email: dbUsers.jonSnow.email }) + .expect(httpStatus.OK) + .then(async (res) => { + delete dbUsers.jonSnow.password; + const john = await format(dbUsers.jonSnow); + + // before comparing it is necessary to convert String to Date + res.body[0].createdAt = new Date(res.body[0].createdAt); + + const includesjonSnow = some(res.body, john); + + expect(res.body).to.be.an('array'); + expect(res.body).to.have.lengthOf(1); + expect(includesjonSnow).to.be.true; + }); + }); + + it('should report error when pagination\'s parameters are not a number', () => { + return request(app) + .get('/v1/users') + .set('Authorization', `Bearer ${adminAccessToken}`) + .query({ page: '?', perPage: 'whaat' }) + .expect(httpStatus.BAD_REQUEST) + .then((res) => { + const { field } = res.body.errors[0]; + const { location } = res.body.errors[0]; + const { messages } = res.body.errors[0]; + expect(field).to.be.equal('page'); + expect(location).to.be.equal('query'); + expect(messages).to.include('"page" must be a number'); + return Promise.resolve(res); + }) + .then((res) => { + const { field } = res.body.errors[1]; + const { location } = res.body.errors[1]; + const { messages } = res.body.errors[1]; + expect(field).to.be.equal('perPage'); + expect(location).to.be.equal('query'); + expect(messages).to.include('"perPage" must be a number'); + }); + }); + + it('should report error if logged user is not an admin', () => { + return request(app) + .get('/v1/users') + .set('Authorization', `Bearer ${userAccessToken}`) + .expect(httpStatus.FORBIDDEN) + .then((res) => { + expect(res.body.code).to.be.equal(httpStatus.FORBIDDEN); + expect(res.body.message).to.be.equal('Forbidden'); + }); + }); + }); + + describe('GET /v1/users/:userId', () => { + it('should get user', async () => { + const id = (await User.findOne({}))._id; + delete dbUsers.branStark.password; + + return request(app) + .get(`/v1/users/${id}`) + .set('Authorization', `Bearer ${adminAccessToken}`) + .expect(httpStatus.OK) + .then((res) => { + expect(res.body).to.include(dbUsers.branStark); + }); + }); + + it('should report error "User does not exist" when user does not exists', () => { + return request(app) + .get('/v1/users/56c787ccc67fc16ccc1a5e92') + .set('Authorization', `Bearer ${adminAccessToken}`) + .expect(httpStatus.NOT_FOUND) + .then((res) => { + expect(res.body.code).to.be.equal(404); + expect(res.body.message).to.be.equal('User does not exist'); + }); + }); + + it('should report error "User does not exist" when id is not a valid ObjectID', () => { + return request(app) + .get('/v1/users/palmeiras1914') + .set('Authorization', `Bearer ${adminAccessToken}`) + .expect(httpStatus.NOT_FOUND) + .then((res) => { + expect(res.body.code).to.be.equal(404); + expect(res.body.message).to.equal('User does not exist'); + }); + }); + + it('should report error when logged user is not the same as the requested one', async () => { + const id = (await User.findOne({ email: dbUsers.branStark.email }))._id; + + return request(app) + .get(`/v1/users/${id}`) + .set('Authorization', `Bearer ${userAccessToken}`) + .expect(httpStatus.FORBIDDEN) + .then((res) => { + expect(res.body.code).to.be.equal(httpStatus.FORBIDDEN); + expect(res.body.message).to.be.equal('Forbidden'); + }); + }); + }); + + describe('PUT /v1/users/:userId', () => { + it('should replace user', async () => { + delete dbUsers.branStark.password; + const id = (await User.findOne(dbUsers.branStark))._id; + + return request(app) + .put(`/v1/users/${id}`) + .set('Authorization', `Bearer ${adminAccessToken}`) + .send(user) + .expect(httpStatus.OK) + .then((res) => { + delete user.password; + expect(res.body).to.include(user); + expect(res.body.role).to.be.equal('user'); + }); + }); + + it('should report error when email is not provided', async () => { + const id = (await User.findOne({}))._id; + delete user.email; + + return request(app) + .put(`/v1/users/${id}`) + .set('Authorization', `Bearer ${adminAccessToken}`) + .send(user) + .expect(httpStatus.BAD_REQUEST) + .then((res) => { + const { field } = res.body.errors[0]; + const { location } = res.body.errors[0]; + const { messages } = res.body.errors[0]; + expect(field).to.be.equal('email'); + expect(location).to.be.equal('body'); + expect(messages).to.include('"email" is required'); + }); + }); + + it('should report error user when password length is less than 6', async () => { + const id = (await User.findOne({}))._id; + user.password = '12345'; + + return request(app) + .put(`/v1/users/${id}`) + .set('Authorization', `Bearer ${adminAccessToken}`) + .send(user) + .expect(httpStatus.BAD_REQUEST) + .then((res) => { + const { field } = res.body.errors[0]; + const { location } = res.body.errors[0]; + const { messages } = res.body.errors[0]; + expect(field).to.be.equal('password'); + expect(location).to.be.equal('body'); + expect(messages).to.include('"password" length must be at least 6 characters long'); + }); + }); + + it('should report error "User does not exist" when user does not exists', () => { + return request(app) + .put('/v1/users/palmeiras1914') + .set('Authorization', `Bearer ${adminAccessToken}`) + .expect(httpStatus.NOT_FOUND) + .then((res) => { + expect(res.body.code).to.be.equal(404); + expect(res.body.message).to.be.equal('User does not exist'); + }); + }); + + it('should report error when logged user is not the same as the requested one', async () => { + const id = (await User.findOne({ email: dbUsers.branStark.email }))._id; + + return request(app) + .put(`/v1/users/${id}`) + .set('Authorization', `Bearer ${userAccessToken}`) + .expect(httpStatus.FORBIDDEN) + .then((res) => { + expect(res.body.code).to.be.equal(httpStatus.FORBIDDEN); + expect(res.body.message).to.be.equal('Forbidden'); + }); + }); + + it('should not replace the role of the user (not admin)', async () => { + const id = (await User.findOne({ email: dbUsers.jonSnow.email }))._id; + const role = 'admin'; + + return request(app) + .put(`/v1/users/${id}`) + .set('Authorization', `Bearer ${userAccessToken}`) + .send(admin) + .expect(httpStatus.OK) + .then((res) => { + expect(res.body.role).to.not.be.equal(role); + }); + }); + }); + + describe('PATCH /v1/users/:userId', () => { + it('should update user', async () => { + delete dbUsers.branStark.password; + const id = (await User.findOne(dbUsers.branStark))._id; + const { name } = user; + + return request(app) + .patch(`/v1/users/${id}`) + .set('Authorization', `Bearer ${adminAccessToken}`) + .send({ name }) + .expect(httpStatus.OK) + .then((res) => { + expect(res.body.name).to.be.equal(name); + expect(res.body.email).to.be.equal(dbUsers.branStark.email); + }); + }); + + it('should not update user when no parameters were given', async () => { + delete dbUsers.branStark.password; + const id = (await User.findOne(dbUsers.branStark))._id; + + return request(app) + .patch(`/v1/users/${id}`) + .set('Authorization', `Bearer ${adminAccessToken}`) + .send() + .expect(httpStatus.OK) + .then((res) => { + expect(res.body).to.include(dbUsers.branStark); + }); + }); + + it('should report error "User does not exist" when user does not exists', () => { + return request(app) + .patch('/v1/users/palmeiras1914') + .set('Authorization', `Bearer ${adminAccessToken}`) + .expect(httpStatus.NOT_FOUND) + .then((res) => { + expect(res.body.code).to.be.equal(404); + expect(res.body.message).to.be.equal('User does not exist'); + }); + }); + + it('should report error when logged user is not the same as the requested one', async () => { + const id = (await User.findOne({ email: dbUsers.branStark.email }))._id; + + return request(app) + .patch(`/v1/users/${id}`) + .set('Authorization', `Bearer ${userAccessToken}`) + .expect(httpStatus.FORBIDDEN) + .then((res) => { + expect(res.body.code).to.be.equal(httpStatus.FORBIDDEN); + expect(res.body.message).to.be.equal('Forbidden'); + }); + }); + + it('should not update the role of the user (not admin)', async () => { + const id = (await User.findOne({ email: dbUsers.jonSnow.email }))._id; + const role = 'admin'; + + return request(app) + .patch(`/v1/users/${id}`) + .set('Authorization', `Bearer ${userAccessToken}`) + .send({ role }) + .expect(httpStatus.OK) + .then((res) => { + expect(res.body.role).to.not.be.equal(role); + }); + }); + }); + + describe('DELETE /v1/users', () => { + it('should delete user', async () => { + const id = (await User.findOne({}))._id; + + return request(app) + .delete(`/v1/users/${id}`) + .set('Authorization', `Bearer ${adminAccessToken}`) + .expect(httpStatus.NO_CONTENT) + .then(() => request(app).get('/v1/users')) + .then(async () => { + const users = await User.find({}); + expect(users).to.have.lengthOf(1); + }); + }); + + it('should report error "User does not exist" when user does not exists', () => { + return request(app) + .delete('/v1/users/palmeiras1914') + .set('Authorization', `Bearer ${adminAccessToken}`) + .expect(httpStatus.NOT_FOUND) + .then((res) => { + expect(res.body.code).to.be.equal(404); + expect(res.body.message).to.be.equal('User does not exist'); + }); + }); + + it('should report error when logged user is not the same as the requested one', async () => { + const id = (await User.findOne({ email: dbUsers.branStark.email }))._id; + + return request(app) + .delete(`/v1/users/${id}`) + .set('Authorization', `Bearer ${userAccessToken}`) + .expect(httpStatus.FORBIDDEN) + .then((res) => { + expect(res.body.code).to.be.equal(httpStatus.FORBIDDEN); + expect(res.body.message).to.be.equal('Forbidden'); + }); + }); + }); + + describe('GET /v1/users/profile', () => { + it('should get the logged user\'s info', () => { + delete dbUsers.jonSnow.password; + + return request(app) + .get('/v1/users/profile') + .set('Authorization', `Bearer ${userAccessToken}`) + .expect(httpStatus.OK) + .then((res) => { + expect(res.body).to.include(dbUsers.jonSnow); + }); + }); + + it('should report error without stacktrace when accessToken is expired', async () => { + // fake time + const clock = sinon.useFakeTimers(); + const expiredAccessToken = (await User.findAndGenerateToken(dbUsers.branStark)).accessToken; + + // move clock forward by minutes set in config + 1 minute + clock.tick((JWT_EXPIRATION * 60000) + 60000); + + return request(app) + .get('/v1/users/profile') + .set('Authorization', `Bearer ${expiredAccessToken}`) + .expect(httpStatus.UNAUTHORIZED) + .then((res) => { + expect(res.body.code).to.be.equal(httpStatus.UNAUTHORIZED); + expect(res.body.message).to.be.equal('jwt expired'); + expect(res.body).to.not.have.a.property('stack'); + }); + }); + }); +}); diff --git a/src/api/utils/APIError.js b/src/api/utils/APIError.js new file mode 100644 index 0000000..bf83a15 --- /dev/null +++ b/src/api/utils/APIError.js @@ -0,0 +1,46 @@ +const httpStatus = require('http-status'); + +/** + * @extends Error + */ +class ExtendableError extends Error { + constructor({ + message, errors, status, isPublic, stack, + }) { + super(message); + this.name = this.constructor.name; + this.message = message; + this.errors = errors; + this.status = status; + this.isPublic = isPublic; + this.isOperational = true; // This is required since bluebird 4 doesn't append it anymore. + this.stack = stack; + // Error.captureStackTrace(this, this.constructor.name); + } +} + +/** + * Class representing an API error. + * @extends ExtendableError + */ +class APIError extends ExtendableError { + /** + * Creates an API error. + * @param {string} message - Error message. + * @param {number} status - HTTP status code of error. + * @param {boolean} isPublic - Whether the message should be visible to user or not. + */ + constructor({ + message, + errors, + stack, + status = httpStatus.INTERNAL_SERVER_ERROR, + isPublic = false, + }) { + super({ + message, errors, status, isPublic, stack, + }); + } +} + +module.exports = APIError; diff --git a/src/api/utils/passwordResetEmailTemplate.html b/src/api/utils/passwordResetEmailTemplate.html new file mode 100644 index 0000000..71cbfae --- /dev/null +++ b/src/api/utils/passwordResetEmailTemplate.html @@ -0,0 +1,584 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/api/validations/auth.validation.js b/src/api/validations/auth.validation.js new file mode 100644 index 0000000..c15d50f --- /dev/null +++ b/src/api/validations/auth.validation.js @@ -0,0 +1,69 @@ +const Joi = require('joi'); + +module.exports = { + // POST /v1/auth/register + register: { + body: { + email: Joi.string() + .email() + .required(), + password: Joi.string() + .required() + .min(6) + .max(128), + }, + }, + + // POST /v1/auth/login + login: { + body: { + email: Joi.string() + .email() + .required(), + password: Joi.string() + .required() + .max(128), + }, + }, + + // POST /v1/auth/facebook + // POST /v1/auth/google + oAuth: { + body: { + access_token: Joi.string().required(), + }, + }, + + // POST /v1/auth/refresh + refresh: { + body: { + email: Joi.string() + .email() + .required(), + refreshToken: Joi.string().required(), + }, + }, + + // POST /v1/auth/refresh + sendPasswordReset: { + body: { + email: Joi.string() + .email() + .required(), + }, + }, + + // POST /v1/auth/password-reset + passwordReset: { + body: { + email: Joi.string() + .email() + .required(), + password: Joi.string() + .required() + .min(6) + .max(128), + resetToken: Joi.string().required(), + }, + }, +}; diff --git a/src/api/validations/user.validation.js b/src/api/validations/user.validation.js new file mode 100644 index 0000000..cad5fb4 --- /dev/null +++ b/src/api/validations/user.validation.js @@ -0,0 +1,52 @@ +const Joi = require('joi'); +const User = require('../models/user.model'); + +module.exports = { + + // GET /v1/users + listUsers: { + query: { + page: Joi.number().min(1), + perPage: Joi.number().min(1).max(100), + name: Joi.string(), + email: Joi.string(), + role: Joi.string().valid(User.roles), + }, + }, + + // POST /v1/users + createUser: { + body: { + email: Joi.string().email().required(), + password: Joi.string().min(6).max(128).required(), + name: Joi.string().max(128), + role: Joi.string().valid(User.roles), + }, + }, + + // PUT /v1/users/:userId + replaceUser: { + body: { + email: Joi.string().email().required(), + password: Joi.string().min(6).max(128).required(), + name: Joi.string().max(128), + role: Joi.string().valid(User.roles), + }, + params: { + userId: Joi.string().regex(/^[a-fA-F0-9]{24}$/).required(), + }, + }, + + // PATCH /v1/users/:userId + updateUser: { + body: { + email: Joi.string().email(), + password: Joi.string().min(6).max(128), + name: Joi.string().max(128), + role: Joi.string().valid(User.roles), + }, + params: { + userId: Joi.string().regex(/^[a-fA-F0-9]{24}$/).required(), + }, + }, +}; diff --git a/src/config/express.js b/src/config/express.js new file mode 100644 index 0000000..a5ab6c7 --- /dev/null +++ b/src/config/express.js @@ -0,0 +1,58 @@ +const express = require('express'); +const morgan = require('morgan'); +const bodyParser = require('body-parser'); +const compress = require('compression'); +const methodOverride = require('method-override'); +const cors = require('cors'); +const helmet = require('helmet'); +const passport = require('passport'); +const routes = require('../api/routes/v1'); +const { logs } = require('./vars'); +const strategies = require('./passport'); +const error = require('../api/middlewares/error'); + +/** +* Express instance +* @public +*/ +const app = express(); + +// request logging. dev: console | production: file +app.use(morgan(logs)); + +// parse body params and attache them to req.body +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: true })); + +// gzip compression +app.use(compress()); + +// lets you use HTTP verbs such as PUT or DELETE +// in places where the client doesn't support it +app.use(methodOverride()); + +// secure apps by setting various HTTP headers +app.use(helmet()); + +// enable CORS - Cross Origin Resource Sharing +app.use(cors()); + +// enable authentication +app.use(passport.initialize()); +passport.use('jwt', strategies.jwt); +passport.use('facebook', strategies.facebook); +passport.use('google', strategies.google); + +// mount api v1 routes +app.use('/v1', routes); + +// if error is not an instanceOf APIError, convert it. +app.use(error.converter); + +// catch 404 and forward to error handler +app.use(error.notFound); + +// error handler, send stacktrace only during development +app.use(error.handler); + +module.exports = app; diff --git a/src/config/logger.js b/src/config/logger.js new file mode 100644 index 0000000..38ad8e9 --- /dev/null +++ b/src/config/logger.js @@ -0,0 +1,32 @@ +const winston = require('winston'); + +const logger = winston.createLogger({ + level: 'info', + format: winston.format.json(), + transports: [ + // + // - Write to all logs with level `info` and below to `combined.log` + // - Write all logs error (and below) to `error.log`. + // + new winston.transports.File({ filename: 'error.log', level: 'error' }), + new winston.transports.File({ filename: 'combined.log' }), + ], +}); + +// +// If we're not in production then log to the `console` with the format: +// `${info.level}: ${info.message} JSON.stringify({ ...rest }) ` +// +if (process.env.NODE_ENV !== 'production') { + logger.add(new winston.transports.Console({ + format: winston.format.simple(), + })); +} + +logger.stream = { + write: (message) => { + logger.info(message.trim()); + }, +}; + +module.exports = logger; diff --git a/src/config/mongoose.js b/src/config/mongoose.js new file mode 100644 index 0000000..22e468e --- /dev/null +++ b/src/config/mongoose.js @@ -0,0 +1,36 @@ +const mongoose = require('mongoose'); +const logger = require('./../config/logger'); +const { mongo, env } = require('./vars'); + +// set mongoose Promise to Bluebird +mongoose.Promise = Promise; + +// Exit application on error +mongoose.connection.on('error', (err) => { + logger.error(`MongoDB connection error: ${err}`); + process.exit(-1); +}); + +// print mongoose logs in dev env +if (env === 'development') { + mongoose.set('debug', true); +} + +/** + * Connect to mongo db + * + * @returns {object} Mongoose connection + * @public + */ +exports.connect = () => { + mongoose + .connect(mongo.uri, { + useCreateIndex: true, + keepAlive: 1, + useNewUrlParser: true, + useUnifiedTopology: true, + useFindAndModify: false, + }) + .then(() => console.log('mongoDB connected...')); + return mongoose.connection; +}; diff --git a/src/config/passport.js b/src/config/passport.js new file mode 100644 index 0000000..8a07383 --- /dev/null +++ b/src/config/passport.js @@ -0,0 +1,35 @@ +const JwtStrategy = require('passport-jwt').Strategy; +const BearerStrategy = require('passport-http-bearer'); +const { ExtractJwt } = require('passport-jwt'); +const { jwtSecret } = require('./vars'); +const authProviders = require('../api/services/authProviders'); +const User = require('../api/models/user.model'); + +const jwtOptions = { + secretOrKey: jwtSecret, + jwtFromRequest: ExtractJwt.fromAuthHeaderWithScheme('Bearer'), +}; + +const jwt = async (payload, done) => { + try { + const user = await User.findById(payload.sub); + if (user) return done(null, user); + return done(null, false); + } catch (error) { + return done(error, false); + } +}; + +const oAuth = service => async (token, done) => { + try { + const userData = await authProviders[service](token); + const user = await User.oAuthLogin(userData); + return done(null, user); + } catch (err) { + return done(err); + } +}; + +exports.jwt = new JwtStrategy(jwtOptions, jwt); +exports.facebook = new BearerStrategy(oAuth('facebook')); +exports.google = new BearerStrategy(oAuth('google')); diff --git a/src/config/vars.js b/src/config/vars.js new file mode 100644 index 0000000..d3b7da5 --- /dev/null +++ b/src/config/vars.js @@ -0,0 +1,24 @@ +const path = require('path'); + +// import .env variables +require('dotenv-safe').load({ + path: path.join(__dirname, '../../.env'), + sample: path.join(__dirname, '../../.env.example'), +}); + +module.exports = { + env: process.env.NODE_ENV, + port: process.env.PORT, + jwtSecret: process.env.JWT_SECRET, + jwtExpirationInterval: process.env.JWT_EXPIRATION_MINUTES, + mongo: { + uri: process.env.NODE_ENV === 'test' ? process.env.MONGO_URI_TESTS : process.env.MONGO_URI, + }, + logs: process.env.NODE_ENV === 'production' ? 'combined' : 'dev', + emailConfig: { + host: process.env.EMAIL_HOST, + port: process.env.EMAIL_PORT, + username: process.env.EMAIL_USERNAME, + password: process.env.EMAIL_PASSWORD, + }, +}; diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..0c735c3 --- /dev/null +++ b/src/index.js @@ -0,0 +1,17 @@ +// make bluebird default Promise +Promise = require('bluebird'); // eslint-disable-line no-global-assign +const { port, env } = require('./config/vars'); +const logger = require('./config/logger'); +const app = require('./config/express'); +const mongoose = require('./config/mongoose'); + +// open mongoose connection + +// listen to requests +app.listen(port, () => logger.info(`server started on port ${port} (${env})`)); + +/** +* Exports express +* @public +*/ +module.exports = app; diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..1c10831 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,5533 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.5.5": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d" + dependencies: + "@babel/highlight" "^7.0.0" + +"@babel/generator@^7.4.0", "@babel/generator@^7.6.2": + version "7.6.2" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.6.2.tgz#dac8a3c2df118334c2a29ff3446da1636a8f8c03" + dependencies: + "@babel/types" "^7.6.0" + jsesc "^2.5.1" + lodash "^4.17.13" + source-map "^0.5.0" + +"@babel/helper-function-name@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz#a0ceb01685f73355d4360c1247f582bfafc8ff53" + dependencies: + "@babel/helper-get-function-arity" "^7.0.0" + "@babel/template" "^7.1.0" + "@babel/types" "^7.0.0" + +"@babel/helper-get-function-arity@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz#83572d4320e2a4657263734113c42868b64e49c3" + dependencies: + "@babel/types" "^7.0.0" + +"@babel/helper-split-export-declaration@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz#ff94894a340be78f53f06af038b205c49d993677" + dependencies: + "@babel/types" "^7.4.4" + +"@babel/highlight@^7.0.0": + version "7.5.0" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.5.0.tgz#56d11312bd9248fa619591d02472be6e8cb32540" + dependencies: + chalk "^2.0.0" + esutils "^2.0.2" + js-tokens "^4.0.0" + +"@babel/parser@^7.4.3", "@babel/parser@^7.6.0", "@babel/parser@^7.6.2": + version "7.6.2" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.6.2.tgz#205e9c95e16ba3b8b96090677a67c9d6075b70a1" + +"@babel/runtime@^7.6.3": + version "7.7.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.7.4.tgz#b23a856751e4bf099262f867767889c0e3fe175b" + dependencies: + regenerator-runtime "^0.13.2" + +"@babel/template@^7.1.0", "@babel/template@^7.4.0": + version "7.6.0" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.6.0.tgz#7f0159c7f5012230dad64cca42ec9bdb5c9536e6" + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/parser" "^7.6.0" + "@babel/types" "^7.6.0" + +"@babel/traverse@^7.4.3": + version "7.6.2" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.6.2.tgz#b0e2bfd401d339ce0e6c05690206d1e11502ce2c" + dependencies: + "@babel/code-frame" "^7.5.5" + "@babel/generator" "^7.6.2" + "@babel/helper-function-name" "^7.1.0" + "@babel/helper-split-export-declaration" "^7.4.4" + "@babel/parser" "^7.6.2" + "@babel/types" "^7.6.0" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.13" + +"@babel/types@^7.0.0", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.6.0": + version "7.6.1" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.6.1.tgz#53abf3308add3ac2a2884d539151c57c4b3ac648" + dependencies: + esutils "^2.0.2" + lodash "^4.17.13" + to-fast-properties "^2.0.0" + +"@dabh/diagnostics@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.2.tgz#290d08f7b381b8f94607dc8f471a12c675f9db31" + dependencies: + colorspace "1.1.x" + enabled "2.0.x" + kuler "^2.0.0" + +"@hapi/boom@^8.0.1": + version "8.0.1" + resolved "https://registry.yarnpkg.com/@hapi/boom/-/boom-8.0.1.tgz#13f1f2f2a3abfb0787c79e35e238c8aff6aa1661" + dependencies: + "@hapi/hoek" "8.x.x" + +"@hapi/hoek@8.x.x": + version "8.5.0" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-8.5.0.tgz#2f9ce301c8898e1c3248b0a8564696b24d1a9a5a" + +"@ladjs/i18n@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@ladjs/i18n/-/i18n-2.0.0.tgz#0235d989a8600bb9b230264261a99dfce4b0aae3" + dependencies: + "@hapi/boom" "^8.0.1" + boolean "1.0.0" + country-language "^0.1.7" + debug "^4.1.1" + i18n "^0.8.4" + i18n-locales "^0.0.2" + lodash "^4.17.15" + moment "^2.24.0" + multimatch "^4.0.0" + qs "^6.9.1" + titleize "^2.1.0" + +"@opencensus/core@^0.0.8": + version "0.0.8" + resolved "https://registry.yarnpkg.com/@opencensus/core/-/core-0.0.8.tgz#df01f200c2d2fbfe14dae129a1a86fb87286db92" + dependencies: + continuation-local-storage "^3.2.1" + log-driver "^1.2.7" + semver "^5.5.0" + shimmer "^1.2.0" + uuid "^3.2.1" + +"@opencensus/core@^0.0.9": + version "0.0.9" + resolved "https://registry.yarnpkg.com/@opencensus/core/-/core-0.0.9.tgz#b16f775435ee309433e4126af194d37313fc93b3" + dependencies: + continuation-local-storage "^3.2.1" + log-driver "^1.2.7" + semver "^5.5.0" + shimmer "^1.2.0" + uuid "^3.2.1" + +"@opencensus/propagation-b3@^0.0.8": + version "0.0.8" + resolved "https://registry.yarnpkg.com/@opencensus/propagation-b3/-/propagation-b3-0.0.8.tgz#0751e6fd75f09400d9d3c419001e9e15a0df68e9" + dependencies: + "@opencensus/core" "^0.0.8" + uuid "^3.2.1" + +"@pm2/agent-node@^1.1.10": + version "1.1.10" + resolved "https://registry.yarnpkg.com/@pm2/agent-node/-/agent-node-1.1.10.tgz#29fafc9d1b75288dec87b6af1216ddfab8ea9b06" + dependencies: + debug "^3.1.0" + eventemitter2 "^5.0.1" + proxy-agent "^3.0.3" + ws "^6.0.0" + +"@pm2/agent@^0.5.26": + version "0.5.26" + resolved "https://registry.yarnpkg.com/@pm2/agent/-/agent-0.5.26.tgz#fa664e365fb89428136ba34c2f9569b796ef8f33" + dependencies: + async "^2.6.0" + chalk "^2.3.2" + eventemitter2 "^5.0.1" + fclone "^1.0.11" + moment "^2.21.0" + nssocket "^0.6.0" + pm2-axon "^3.2.0" + pm2-axon-rpc "^0.5.0" + proxy-agent "^3.1.0" + semver "^5.5.0" + ws "^5.1.0" + +"@pm2/io@^4.3.2": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@pm2/io/-/io-4.3.3.tgz#3a719da5b0897718173d51ab313d25d7e1471c9b" + dependencies: + "@opencensus/core" "^0.0.9" + "@opencensus/propagation-b3" "^0.0.8" + "@pm2/agent-node" "^1.1.10" + async "~2.6.1" + debug "3.1.0" + eventemitter2 "~5.0.1" + require-in-the-middle "^5.0.0" + semver "5.5.0" + shimmer "~1.2.0" + signal-exit "3.0.2" + tslib "1.9.3" + +"@pm2/js-api@^0.5.60": + version "0.5.60" + resolved "https://registry.yarnpkg.com/@pm2/js-api/-/js-api-0.5.60.tgz#ae05324da8edc83f8f3172dadc5f9d5f16c99e54" + dependencies: + async "^2.4.1" + axios "^0.19.0" + debug "^2.6.8" + eventemitter2 "^4.1.0" + ws "^3.0.0" + +"@sindresorhus/is@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" + +"@sindresorhus/is@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-1.2.0.tgz#63ce3638cb85231f3704164c90a18ef816da3fb7" + +"@sinonjs/commons@^1", "@sinonjs/commons@^1.3.0", "@sinonjs/commons@^1.4.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.6.0.tgz#ec7670432ae9c8eb710400d112c201a362d83393" + dependencies: + type-detect "4.0.8" + +"@sinonjs/formatio@^3.2.1": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-3.2.1.tgz#52310f2f9bcbc67bdac18c94ad4901b95fde267e" + dependencies: + "@sinonjs/commons" "^1" + "@sinonjs/samsam" "^3.1.0" + +"@sinonjs/samsam@^3.1.0", "@sinonjs/samsam@^3.3.3": + version "3.3.3" + resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-3.3.3.tgz#46682efd9967b259b81136b9f120fd54585feb4a" + dependencies: + "@sinonjs/commons" "^1.3.0" + array-from "^2.1.1" + lodash "^4.17.15" + +"@sinonjs/text-encoding@^0.7.1": + version "0.7.1" + resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5" + +"@szmarczak/http-timer@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" + dependencies: + defer-to-connect "^1.0.1" + +"@types/babel-types@*", "@types/babel-types@^7.0.0": + version "7.0.7" + resolved "https://registry.yarnpkg.com/@types/babel-types/-/babel-types-7.0.7.tgz#667eb1640e8039436028055737d2b9986ee336e3" + +"@types/babylon@^6.16.2": + version "6.16.5" + resolved "https://registry.yarnpkg.com/@types/babylon/-/babylon-6.16.5.tgz#1c5641db69eb8cdf378edd25b4be7754beeb48b4" + dependencies: + "@types/babel-types" "*" + +"@types/color-name@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" + +"@types/joi@^14.3.3": + version "14.3.3" + resolved "https://registry.yarnpkg.com/@types/joi/-/joi-14.3.3.tgz#f251aa8150fc0b6a7ce9feab21802a28473de335" + +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + +"@types/minimatch@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" + +"@types/node@^8.0.7": + version "8.10.48" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.48.tgz#e385073561643a9ba6199a1985ffc03530f90781" + +"@types/normalize-package-data@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + +accepts@~1.3.5, accepts@~1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + dependencies: + mime-types "~2.1.24" + negotiator "0.6.2" + +acorn-globals@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-3.1.0.tgz#fd8270f71fbb4996b004fa880ee5d46573a731bf" + dependencies: + acorn "^4.0.4" + +acorn-jsx@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.1.0.tgz#294adb71b57398b0680015f0a38c563ee1db5384" + +acorn@^3.1.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" + +acorn@^4.0.4, acorn@~4.0.2: + version "4.0.13" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" + +acorn@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.0.tgz#949d36f2c292535da602283586c2477c57eb2d6c" + +agent-base@4, agent-base@^4.2.0, agent-base@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" + dependencies: + es6-promisify "^5.0.0" + +agent-base@~4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" + dependencies: + es6-promisify "^5.0.0" + +ajv@^6.10.0, ajv@^6.10.2: + version "6.10.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52" + dependencies: + fast-deep-equal "^2.0.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^6.12.3: + version "6.12.4" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.4.tgz#0614facc4522127fa713445c6bfd3ebd376e2234" + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +align-text@^0.1.1, align-text@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" + dependencies: + kind-of "^3.0.2" + longest "^1.0.1" + repeat-string "^1.5.2" + +amp-message@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/amp-message/-/amp-message-0.1.2.tgz#a78f1c98995087ad36192a41298e4db49e3dfc45" + dependencies: + amp "0.3.1" + +amp@0.3.1, amp@~0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/amp/-/amp-0.3.1.tgz#6adf8d58a74f361e82c1fa8d389c079e139fc47d" + +ansi-align@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb" + dependencies: + string-width "^3.0.0" + +ansi-colors@3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" + +ansi-colors@^3.2.1: + version "3.2.4" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" + +ansi-escapes@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.2.1.tgz#4dccdb846c3eee10f6d64dea66273eab90c37228" + dependencies: + type-fest "^0.5.2" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" + dependencies: + "@types/color-name" "^1.1.1" + color-convert "^2.0.1" + +anymatch@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +apidoc-core@~0.8.2: + version "0.8.3" + resolved "https://registry.yarnpkg.com/apidoc-core/-/apidoc-core-0.8.3.tgz#d9d63545829df250d2cca049683a87e775364b96" + dependencies: + fs-extra "^3.0.1" + glob "^7.1.1" + iconv-lite "^0.4.17" + klaw-sync "^2.1.0" + lodash "~4.17.4" + semver "~5.3.0" + +apidoc@^0.17.5: + version "0.17.7" + resolved "https://registry.yarnpkg.com/apidoc/-/apidoc-0.17.7.tgz#a49090cbd8b3aa457bd054f00e0037e975fd3ee7" + dependencies: + apidoc-core "~0.8.2" + commander "^2.19.0" + fs-extra "^7.0.0" + lodash "^4.17.10" + markdown-it "^8.3.1" + winston "^3.0.0" + +append-transform@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-1.0.0.tgz#046a52ae582a228bd72f58acfbe2967c678759ab" + dependencies: + default-require-extensions "^2.0.0" + +archy@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + dependencies: + sprintf-js "~1.0.2" + +array-differ@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-3.0.0.tgz#3cbb3d0f316810eafcc47624734237d6aee4ae6b" + +array-find-index@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + +array-from@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/array-from/-/array-from-2.1.1.tgz#cfe9d8c26628b9dc5aecc62a9f5d8f1f352c1195" + +array-includes@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.1.tgz#cdd67e6852bdf9c1215460786732255ed2459348" + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0" + is-string "^1.0.5" + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + +array.prototype.flat@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz#0de82b426b0318dbfdb940089e38b043d37f6c7b" + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + +arrify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + +arrify@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" + +asap@~2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + +asn1@~0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + +ast-types@0.x.x: + version "0.13.1" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.1.tgz#9461428a270c5a27fda44b738dd3bab2e9353003" + +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + +async-limiter@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" + +async-listener@^0.6.0: + version "0.6.8" + resolved "https://registry.yarnpkg.com/async-listener/-/async-listener-0.6.8.tgz#d3556ef905d5ad77b52e52b37d68b1d8a02481f5" + dependencies: + semver "^5.3.0" + shimmer "^1.1.0" + +async@2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610" + dependencies: + lodash "^4.17.10" + +async@^2.4.1, async@^2.6, async@^2.6.0, async@~2.6.1: + version "2.6.2" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.2.tgz#18330ea7e6e313887f5d2f2a904bac6fe4dd5381" + dependencies: + lodash "^4.17.11" + +async@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/async/-/async-3.1.0.tgz#42b3b12ae1b74927b5217d8c0016baaf62463772" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + +aws4@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" + +axios@^0.19.0: + version "0.19.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.0.tgz#8e09bff3d9122e133f7b8101c8fbdd00ed3d2ab8" + dependencies: + follow-redirects "1.5.10" + is-buffer "^2.0.2" + +babel-runtime@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.11.0" + +babel-types@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" + dependencies: + babel-runtime "^6.26.0" + esutils "^2.0.2" + lodash "^4.17.4" + to-fast-properties "^1.0.3" + +babylon@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + +basic-auth@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.0.tgz#015db3f353e02e56377755f962742e8981e7bbba" + dependencies: + safe-buffer "5.1.1" + +bcrypt-pbkdf@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" + dependencies: + tweetnacl "^0.14.3" + +bcryptjs@2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-2.4.3.tgz#9ab5627b93e60621ff7cdac5da9733027df1d0cb" + +binary-extensions@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c" + +bl@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/bl/-/bl-2.2.1.tgz#8c11a7b730655c5d56898cdc871224f40fd901d5" + dependencies: + readable-stream "^2.3.5" + safe-buffer "^5.1.1" + +blessed@0.1.81: + version "0.1.81" + resolved "https://registry.yarnpkg.com/blessed/-/blessed-0.1.81.tgz#f962d687ec2c369570ae71af843256e6d0ca1129" + +bluebird@3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" + +bluebird@^3.1.1: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + +bluebird@^3.5.0: + version "3.7.1" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.1.tgz#df70e302b471d7473489acf26a93d63b53f874de" + +bodec@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/bodec/-/bodec-0.1.0.tgz#bc851555430f23c9f7650a75ef64c6a94c3418cc" + +body-parser@1.19.0, body-parser@^1.17.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" + dependencies: + bytes "3.1.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "1.7.2" + iconv-lite "0.4.24" + on-finished "~2.3.0" + qs "6.7.0" + raw-body "2.4.0" + type-is "~1.6.17" + +boolbase@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + +boolean@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolean/-/boolean-1.0.0.tgz#45764b4aac187a050995b0a33d7579b6759f0dfd" + +bowser@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.7.0.tgz#96eab1fa07fab08c1ec4c75977a7c8ddf8e0fe1f" + +boxen@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64" + dependencies: + ansi-align "^3.0.0" + camelcase "^5.3.1" + chalk "^3.0.0" + cli-boxes "^2.2.0" + string-width "^4.1.0" + term-size "^2.1.0" + type-fest "^0.8.1" + widest-line "^3.1.0" + +brace-expansion@^1.1.7: + version "1.1.8" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + dependencies: + fill-range "^7.0.1" + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + +bson@^1.1.4: + version "1.1.5" + resolved "https://registry.yarnpkg.com/bson/-/bson-1.1.5.tgz#2aaae98fcdf6750c0848b0cba1ddec3c73060a34" + +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + +buffer-from@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.0.tgz#87fcaa3a298358e0ade6e442cfce840740d1ad04" + +builtin-modules@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + +bytes@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + +cacheable-request@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^3.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^1.0.2" + +caching-transform@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/caching-transform/-/caching-transform-3.0.2.tgz#601d46b91eca87687a281e71cef99791b0efca70" + dependencies: + hasha "^3.0.0" + make-dir "^2.0.0" + package-hash "^3.0.0" + write-file-atomic "^2.4.2" + +caller-callsite@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" + dependencies: + callsites "^2.0.0" + +caller-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" + dependencies: + caller-callsite "^2.0.0" + +callsites@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + +camelcase-keys@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-4.2.0.tgz#a2aa5fb1af688758259c32c141426d78923b9b77" + dependencies: + camelcase "^4.1.0" + map-obj "^2.0.0" + quick-lru "^1.0.0" + +camelcase@^1.0.2: + version "1.2.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" + +camelcase@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" + +camelcase@^5.0.0, camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + +camelize@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.0.tgz#164a5483e630fa4321e5af07020e531831b2609b" + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + +center-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" + dependencies: + align-text "^0.1.3" + lazy-cache "^1.0.3" + +chai-as-promised@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/chai-as-promised/-/chai-as-promised-7.1.1.tgz#08645d825deb8696ee61725dbf590c012eb00ca0" + dependencies: + check-error "^1.0.2" + +chai@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5" + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.2" + deep-eql "^3.0.1" + get-func-name "^2.0.0" + pathval "^1.1.0" + type-detect "^4.0.5" + +chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.2, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +character-parser@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/character-parser/-/character-parser-2.2.0.tgz#c7ce28f36d4bcd9744e5ffc2c5fcde1c73261fc0" + dependencies: + is-regex "^1.0.3" + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + +charm@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/charm/-/charm-0.1.2.tgz#06c21eed1a1b06aeb67553cdc53e23274bac2296" + +check-error@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" + +cheerio@^0.22.0: + version "0.22.0" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.22.0.tgz#a9baa860a3f9b595a6b81b1a86873121ed3a269e" + dependencies: + css-select "~1.2.0" + dom-serializer "~0.1.0" + entities "~1.1.1" + htmlparser2 "^3.9.1" + lodash.assignin "^4.0.9" + lodash.bind "^4.1.4" + lodash.defaults "^4.0.1" + lodash.filter "^4.4.0" + lodash.flatten "^4.2.0" + lodash.foreach "^4.3.0" + lodash.map "^4.4.0" + lodash.merge "^4.4.0" + lodash.pick "^4.2.1" + lodash.reduce "^4.4.0" + lodash.reject "^4.4.0" + lodash.some "^4.4.0" + +chokidar@^3.2.0, chokidar@^3.2.2: + version "3.3.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.0.tgz#12c0714668c55800f659e262d4962a97faf554a6" + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.2.0" + optionalDependencies: + fsevents "~2.1.1" + +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + +clean-css@^4.1.11: + version "4.2.1" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.1.tgz#2d411ef76b8569b6d0c84068dabe85b0aa5e5c17" + dependencies: + source-map "~0.6.0" + +cli-boxes@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" + +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + dependencies: + restore-cursor "^3.1.0" + +cli-table-redemption@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cli-table-redemption/-/cli-table-redemption-1.0.1.tgz#0359d8c34df74980029d76dff071a05a127c4fdd" + dependencies: + chalk "^1.1.3" + +cli-width@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" + +cliui@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" + dependencies: + center-align "^0.1.1" + right-align "^0.1.1" + wordwrap "0.0.2" + +cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" + +clone-response@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + dependencies: + mimic-response "^1.0.0" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + +color-convert@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.0.tgz#1accf97dd739b983bf994d56fec8f95853641b7a" + dependencies: + color-name "^1.1.1" + +color-convert@^1.9.1: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + dependencies: + color-name "~1.1.4" + +color-name@1.1.3, color-name@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + +color-name@^1.0.0, color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + +color-string@^1.5.2: + version "1.5.3" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc" + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color@3.0.x: + version "3.0.0" + resolved "https://registry.yarnpkg.com/color/-/color-3.0.0.tgz#d920b4328d534a3ac8295d68f7bd4ba6c427be9a" + dependencies: + color-convert "^1.9.1" + color-string "^1.5.2" + +colors@^1.2.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.3.2.tgz#2df8ff573dfbf255af562f8ce7181d6b971a359b" + +colorspace@1.1.x: + version "1.1.1" + resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.1.tgz#9ac2491e1bc6f8fb690e2176814f8d091636d972" + dependencies: + color "3.0.x" + text-hex "1.0.x" + +combined-stream@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" + dependencies: + delayed-stream "~1.0.0" + +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + dependencies: + delayed-stream "~1.0.0" + +commander@2.15.1: + version "2.15.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" + +commander@^2.15.1, commander@^2.19.0, commander@~2.20.3: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + +component-emitter@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" + +compressible@~2.0.16: + version "2.0.17" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.17.tgz#6e8c108a16ad58384a977f3a482ca20bff2f38c1" + dependencies: + mime-db ">= 1.40.0 < 2" + +compression@^1.6.2: + version "1.7.4" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.16" + debug "2.6.9" + on-headers "~1.0.2" + safe-buffer "5.1.2" + vary "~1.1.2" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +configstore@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" + dependencies: + dot-prop "^5.2.0" + graceful-fs "^4.1.2" + make-dir "^3.0.0" + unique-string "^2.0.0" + write-file-atomic "^3.0.0" + xdg-basedir "^4.0.0" + +consolidate@^0.15.1: + version "0.15.1" + resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.15.1.tgz#21ab043235c71a07d45d9aad98593b0dba56bab7" + dependencies: + bluebird "^3.1.1" + +constantinople@^3.0.1, constantinople@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/constantinople/-/constantinople-3.1.2.tgz#d45ed724f57d3d10500017a7d3a889c1381ae647" + dependencies: + "@types/babel-types" "^7.0.0" + "@types/babylon" "^6.16.2" + babel-types "^6.26.0" + babylon "^6.18.0" + +contains-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" + +content-disposition@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" + dependencies: + safe-buffer "5.1.2" + +content-security-policy-builder@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/content-security-policy-builder/-/content-security-policy-builder-2.1.0.tgz#0a2364d769a3d7014eec79ff7699804deb8cfcbb" + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + +continuation-local-storage@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz#11f613f74e914fe9b34c92ad2d28fe6ae1db7ffb" + dependencies: + async-listener "^0.6.0" + emitter-listener "^1.1.1" + +convert-source-map@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.6.0.tgz#51b537a8c43e0f04dec1993bffcdd504e758ac20" + dependencies: + safe-buffer "~5.1.1" + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + +cookie@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" + +cookiejar@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.1.tgz#41ad57b1b555951ec171412a81942b1e8200d34a" + +core-js@^2.4.0: + version "2.6.10" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.10.tgz#8a5b8391f8cc7013da703411ce5b585706300d7f" + +core-util-is@1.0.2, core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + +cors@^2.8.3: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + dependencies: + object-assign "^4" + vary "^1" + +cosmiconfig@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" + dependencies: + import-fresh "^2.0.0" + is-directory "^0.3.1" + js-yaml "^3.13.1" + parse-json "^4.0.0" + +country-language@^0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/country-language/-/country-language-0.1.7.tgz#7870f4ba125db9a6071f19737bd9ef9343ae35db" + dependencies: + underscore "~1.7.0" + underscore.deep "~0.5.1" + +coveralls@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/coveralls/-/coveralls-3.1.0.tgz#13c754d5e7a2dd8b44fe5269e21ca394fb4d615b" + dependencies: + js-yaml "^3.13.1" + lcov-parse "^1.0.0" + log-driver "^1.2.7" + minimist "^1.2.5" + request "^2.88.2" + +cp-file@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/cp-file/-/cp-file-6.2.0.tgz#40d5ea4a1def2a9acdd07ba5c0b0246ef73dc10d" + dependencies: + graceful-fs "^4.1.2" + make-dir "^2.0.0" + nested-error-stacks "^2.0.0" + pify "^4.0.1" + safe-buffer "^5.0.1" + +cron@1.7.1: + version "1.7.1" + resolved "https://registry.yarnpkg.com/cron/-/cron-1.7.1.tgz#e85ee9df794d1bc6579896ee382053c3ce33778f" + dependencies: + moment-timezone "^0.5.x" + +cross-env@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-6.0.3.tgz#4256b71e49b3a40637a0ce70768a6ef5c72ae941" + dependencies: + cross-spawn "^7.0.0" + +cross-spawn@^4: + version "4.0.2" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-4.0.2.tgz#7b9247621c23adfdd3856004a823cbe397424d41" + dependencies: + lru-cache "^4.0.1" + which "^1.2.9" + +cross-spawn@^6.0.0, cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14" + 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" + +css-select@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" + dependencies: + boolbase "~1.0.0" + css-what "2.1" + domutils "1.5.1" + nth-check "~1.0.1" + +css-what@2.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" + +culvert@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/culvert/-/culvert-0.1.2.tgz#9502f5f0154a2d5a22a023e79f71cc936fa6ef6f" + +currently-unhandled@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" + dependencies: + array-find-index "^1.0.1" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + dependencies: + assert-plus "^1.0.0" + +dasherize@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dasherize/-/dasherize-2.0.0.tgz#6d809c9cd0cf7bb8952d80fc84fa13d47ddb1308" + +data-uri-to-buffer@2: + version "2.0.1" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-2.0.1.tgz#ca8f56fe38b1fd329473e9d1b4a9afcd8ce1c045" + dependencies: + "@types/node" "^8.0.7" + +datauri@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/datauri/-/datauri-2.0.0.tgz#ff0ee23729935a6bcc81f301621bed3e692bf3c7" + dependencies: + image-size "^0.7.3" + mimer "^1.0.0" + +date-fns@1.30.1: + version "1.30.1" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" + +dayjs@^1.8.16: + version "1.8.17" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.8.17.tgz#53ec413f2a7b02afbea1846d61bb260fa8567cea" + +debug@*, debug@4, debug@4.1.1, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + dependencies: + ms "^2.1.1" + +debug@2.6.9, debug@^2.2.0, debug@^2.6.8, debug@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + dependencies: + ms "2.0.0" + +debug@3.1.0, debug@=3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + dependencies: + ms "2.0.0" + +debug@3.2.6, debug@^3.0, debug@^3.1.0, debug@^3.2.6: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + dependencies: + ms "^2.1.1" + +decamelize-keys@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" + dependencies: + decamelize "^1.1.0" + map-obj "^1.0.0" + +decamelize@^1.0.0, decamelize@^1.1.0, decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + +decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + dependencies: + mimic-response "^1.0.0" + +deep-eql@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" + dependencies: + type-detect "^4.0.0" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + +default-require-extensions@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-2.0.0.tgz#f5f8fbb18a7d6d50b21f641f649ebb522cfe24f7" + dependencies: + strip-bom "^3.0.0" + +defer-to-connect@^1.0.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" + +define-properties@^1.1.2, define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + dependencies: + object-keys "^1.0.12" + +degenerator@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-1.0.4.tgz#fcf490a37ece266464d9cc431ab98c5819ced095" + dependencies: + ast-types "0.x.x" + escodegen "1.x.x" + esprima "3.x.x" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + +denque@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/denque/-/denque-1.4.1.tgz#6744ff7641c148c3f8a69c307e51235c1f4a37cf" + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + +diff@3.5.0, diff@^3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + +dns-prefetch-control@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/dns-prefetch-control/-/dns-prefetch-control-0.2.0.tgz#73988161841f3dcc81f47686d539a2c702c88624" + +doctrine@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" + dependencies: + esutils "^2.0.2" + isarray "^1.0.0" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + dependencies: + esutils "^2.0.2" + +doctypes@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/doctypes/-/doctypes-1.1.0.tgz#ea80b106a87538774e8a3a4a5afe293de489e0a9" + +dom-serializer@0, dom-serializer@^0.2.1: + version "0.2.2" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" + dependencies: + domelementtype "^2.0.1" + entities "^2.0.0" + +dom-serializer@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0" + dependencies: + domelementtype "^1.3.0" + entities "^1.1.1" + +domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" + +domelementtype@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.1.tgz#1f8bdfe91f5a78063274e803b4bdcedf6e94f94d" + +domhandler@^2.3.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" + dependencies: + domelementtype "1" + +domhandler@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-3.0.0.tgz#51cd13efca31da95bbb0c5bee3a48300e333b3e9" + dependencies: + domelementtype "^2.0.1" + +domutils@1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" + dependencies: + dom-serializer "0" + domelementtype "1" + +domutils@^1.5.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" + dependencies: + dom-serializer "0" + domelementtype "1" + +domutils@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.0.0.tgz#15b8278e37bfa8468d157478c58c367718133c08" + dependencies: + dom-serializer "^0.2.1" + domelementtype "^2.0.1" + domhandler "^3.0.0" + +dont-sniff-mimetype@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/dont-sniff-mimetype/-/dont-sniff-mimetype-1.1.0.tgz#c7d0427f8bcb095762751252af59d148b0a623b2" + +dot-prop@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.2.0.tgz#c34ecc29556dc45f1f4c22697b6f4904e0cc4fcb" + dependencies: + is-obj "^2.0.0" + +dotenv-safe@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/dotenv-safe/-/dotenv-safe-6.0.0.tgz#8e5e5a8a7b0f588d22a8d7e8000d21a8ec42194f" + dependencies: + dotenv "^6.0.0" + +dotenv@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.0.0.tgz#24e37c041741c5f4b25324958ebbc34bca965935" + +duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + +ecc-jsbn@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" + dependencies: + jsbn "~0.1.0" + +ecdsa-sig-formatter@1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.10.tgz#1c595000f04a8897dfb85000892a0f4c33af86c3" + dependencies: + safe-buffer "^5.0.1" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + +email-templates@^6.0.3: + version "6.0.6" + resolved "https://registry.yarnpkg.com/email-templates/-/email-templates-6.0.6.tgz#b32163b17900fd6fdacb526e81ee32656cb9cf9c" + dependencies: + "@ladjs/i18n" "^2.0.0" + "@sindresorhus/is" "^1.2.0" + consolidate "^0.15.1" + debug "^4.1.1" + get-paths "^0.0.7" + html-to-text "^5.1.1" + juice "^6.0.0" + lodash "^4.17.15" + nodemailer "^6.3.1" + pify "^4.0.1" + preview-email "^1.0.2" + +emitter-listener@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/emitter-listener/-/emitter-listener-1.1.2.tgz#56b140e8f6992375b3d7cb2cab1cc7432d9632e8" + dependencies: + shimmer "^1.2.0" + +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + +enabled@2.0.x: + version "2.0.0" + resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2" + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + dependencies: + once "^1.4.0" + +enquirer@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.2.tgz#1c30284907cadff5ed2404bd8396036dd3da070e" + dependencies: + ansi-colors "^3.2.1" + +entities@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" + +entities@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4" + +entities@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" + +error-ex@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc" + dependencies: + is-arrayish "^0.2.1" + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.5: + version "1.17.6" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.6.tgz#9142071707857b2cacc7b89ecb670316c3e2d52a" + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.2.0" + is-regex "^1.1.0" + object-inspect "^1.7.0" + object-keys "^1.1.1" + object.assign "^4.1.0" + string.prototype.trimend "^1.0.1" + string.prototype.trimstart "^1.0.1" + +es-abstract@^1.5.1: + version "1.16.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.16.0.tgz#d3a26dc9c3283ac9750dca569586e976d9dcc06d" + dependencies: + es-to-primitive "^1.2.0" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.0" + is-callable "^1.1.4" + is-regex "^1.0.4" + object-inspect "^1.6.0" + object-keys "^1.1.1" + string.prototype.trimleft "^2.1.0" + string.prototype.trimright "^2.1.0" + +es-to-primitive@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377" + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es6-error@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" + +es6-promise@^4.0.3: + version "4.2.8" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + +es6-promisify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" + dependencies: + es6-promise "^4.0.3" + +escape-goat@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + +escape-regexp@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/escape-regexp/-/escape-regexp-0.0.1.tgz#f44bda12d45bbdf9cb7f862ee7e4827b3dd32254" + +escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + +escodegen@1.x.x: + version "1.11.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.11.1.tgz#c485ff8d6b4cdb89e27f4a856e91f118401ca510" + dependencies: + esprima "^3.1.3" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + +eslint-config-airbnb-base@^12.0.1: + version "12.0.1" + resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-12.0.1.tgz#abaca7b4536faa7e094add5a9801970fefdd39a4" + dependencies: + eslint-restricted-globals "^0.1.1" + +eslint-import-resolver-node@^0.3.3: + version "0.3.4" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717" + dependencies: + debug "^2.6.9" + resolve "^1.13.1" + +eslint-module-utils@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz#579ebd094f56af7797d19c9866c9c9486629bfa6" + dependencies: + debug "^2.6.9" + pkg-dir "^2.0.0" + +eslint-plugin-import@^2.2.0: + version "2.22.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.22.0.tgz#92f7736fe1fde3e2de77623c838dd992ff5ffb7e" + dependencies: + array-includes "^3.1.1" + array.prototype.flat "^1.2.3" + contains-path "^0.1.0" + debug "^2.6.9" + doctrine "1.5.0" + eslint-import-resolver-node "^0.3.3" + eslint-module-utils "^2.6.0" + has "^1.0.3" + minimatch "^3.0.4" + object.values "^1.1.1" + read-pkg-up "^2.0.0" + resolve "^1.17.0" + tsconfig-paths "^3.9.0" + +eslint-restricted-globals@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz#35f0d5cbc64c2e3ed62e93b4b1a7af05ba7ed4d7" + +eslint-scope@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9" + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-utils@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-visitor-keys@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" + +eslint@^6.4.0: + version "6.7.2" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.7.2.tgz#c17707ca4ad7b2d8af986a33feba71e18a9fecd1" + dependencies: + "@babel/code-frame" "^7.0.0" + ajv "^6.10.0" + chalk "^2.1.0" + cross-spawn "^6.0.5" + debug "^4.0.1" + doctrine "^3.0.0" + eslint-scope "^5.0.0" + eslint-utils "^1.4.3" + eslint-visitor-keys "^1.1.0" + espree "^6.1.2" + esquery "^1.0.1" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^5.0.0" + globals "^12.1.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + inquirer "^7.0.0" + is-glob "^4.0.0" + js-yaml "^3.13.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.14" + minimatch "^3.0.4" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.3" + progress "^2.0.0" + regexpp "^2.0.1" + semver "^6.1.2" + strip-ansi "^5.2.0" + strip-json-comments "^3.0.1" + table "^5.2.3" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +espree@^6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/espree/-/espree-6.1.2.tgz#6c272650932b4f91c3714e5e7b5f5e2ecf47262d" + dependencies: + acorn "^7.1.0" + acorn-jsx "^5.1.0" + eslint-visitor-keys "^1.1.0" + +esprima@3.x.x, esprima@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + +esquery@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" + dependencies: + estraverse "^4.0.0" + +esrecurse@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.0.tgz#fa9568d98d3823f9a41d91e902dcab9ea6e5b163" + dependencies: + estraverse "^4.1.0" + object-assign "^4.0.1" + +estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" + +esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + +eventemitter2@5.0.1, eventemitter2@^5.0.1, eventemitter2@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-5.0.1.tgz#6197a095d5fb6b57e8942f6fd7eaad63a09c9452" + +eventemitter2@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-4.1.2.tgz#0e1a8477af821a6ef3995b311bf74c23a5247f15" + +eventemitter2@~0.4.14: + version "0.4.14" + resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-0.4.14.tgz#8f61b75cde012b2e9eb284d4545583b5643b61ab" + +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +expect-ct@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/expect-ct/-/expect-ct-0.2.0.tgz#3a54741b6ed34cc7a93305c605f63cd268a54a62" + +express-validation@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/express-validation/-/express-validation-1.0.3.tgz#677261e65f27f4e869c98dedc7934da6bbefaa8f" + dependencies: + "@types/joi" "^14.3.3" + lodash "^4.17.15" + +express@^4.15.2: + version "4.17.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" + dependencies: + accepts "~1.3.7" + array-flatten "1.1.1" + body-parser "1.19.0" + content-disposition "0.5.3" + content-type "~1.0.4" + cookie "0.4.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "~1.1.2" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.5" + qs "6.7.0" + range-parser "~1.2.1" + safe-buffer "5.1.2" + send "0.17.1" + serve-static "1.14.1" + setprototypeof "1.1.1" + statuses "~1.5.0" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +extend@^3.0.0, extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +extsprintf@1.3.0, extsprintf@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + +fast-deep-equal@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + +fast-json-stable-stringify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + +fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + +fast-safe-stringify@^2.0.4: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.6.tgz#04b26106cc56681f51a044cfc0d76cf0008ac2c2" + +fclone@1.0.11, fclone@^1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/fclone/-/fclone-1.0.11.tgz#10e85da38bfea7fc599341c296ee1d77266ee640" + +feature-policy@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/feature-policy/-/feature-policy-0.3.0.tgz#7430e8e54a40da01156ca30aaec1a381ce536069" + +fecha@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.0.tgz#3ffb6395453e3f3efff850404f0a59b6747f5f41" + +figures@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.1.0.tgz#4b198dd07d8d71530642864af2d45dd9e459c4ec" + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + dependencies: + flat-cache "^2.0.1" + +file-type@^11.0.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/file-type/-/file-type-11.1.0.tgz#93780f3fed98b599755d846b99a1617a2ad063b8" + +file-uri-to-path@1: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + dependencies: + to-regex-range "^5.0.1" + +finalhandler@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + +find-cache-dir@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" + dependencies: + commondir "^1.0.1" + make-dir "^2.0.0" + pkg-dir "^3.0.0" + +find-up@3.0.0, find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + dependencies: + locate-path "^3.0.0" + +find-up@^2.0.0, find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + dependencies: + locate-path "^2.0.0" + +find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + +flat@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.0.tgz#090bec8b05e39cba309747f1d588f04dbaf98db2" + dependencies: + is-buffer "~2.0.3" + +flatted@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08" + +fn.name@1.x.x: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" + +follow-redirects@1.5.10: + version "1.5.10" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" + dependencies: + debug "=3.1.0" + +foreground-child@^1.5.6: + version "1.5.6" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-1.5.6.tgz#4fd71ad2dfde96789b980a5c0a295937cb2f5ce9" + dependencies: + cross-spawn "^4" + signal-exit "^3.0.0" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + +form-data@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.1.tgz#6fb94fbd71885306d73d15cc497fe4cc4ecd44bf" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.5" + mime-types "^2.1.12" + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +formidable@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.1.tgz#70fb7ca0290ee6ff961090415f4b3df3d2082659" + +forwarded@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" + +frameguard@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/frameguard/-/frameguard-3.1.0.tgz#bd1442cca1d67dc346a6751559b6d04502103a22" + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + +fs-extra@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-3.0.1.tgz#3794f378c58b342ea7dbbb23095109c4b3b62291" + dependencies: + graceful-fs "^4.1.2" + jsonfile "^3.0.0" + universalify "^0.1.0" + +fs-extra@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + +fsevents@~2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.2.tgz#4c0a1fb34bc68e543b4b82a9ec392bfbda840805" + +ftp@~0.3.10: + version "0.3.10" + resolved "https://registry.yarnpkg.com/ftp/-/ftp-0.3.10.tgz#9197d861ad8142f3e63d5a83bfe4c59f7330885d" + dependencies: + readable-stream "1.1.x" + xregexp "2.0.0" + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + +get-func-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" + +get-paths@^0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/get-paths/-/get-paths-0.0.7.tgz#15331086752077cf130166ccd233a1cdbeefcf38" + dependencies: + pify "^4.0.1" + +get-stdin@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-7.0.0.tgz#8d5de98f15171a125c5e516643c7a6d0ea8a96f6" + +get-stream@^4.0.0, get-stream@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + dependencies: + pump "^3.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" + dependencies: + pump "^3.0.0" + +get-uri@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-2.0.3.tgz#fa13352269781d75162c6fc813c9e905323fbab5" + dependencies: + data-uri-to-buffer "2" + debug "4" + extend "~3.0.2" + file-uri-to-path "1" + ftp "~0.3.10" + readable-stream "3" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + dependencies: + assert-plus "^1.0.0" + +git-node-fs@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/git-node-fs/-/git-node-fs-1.0.0.tgz#49b215e242ebe43aa4c7561bbba499521752080f" + +git-sha1@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/git-sha1/-/git-sha1-0.1.2.tgz#599ac192b71875825e13a445f3a6e05118c2f745" + +glob-parent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.0.0.tgz#1dc99f0f39b006d3e92c2c284068382f0c20e954" + dependencies: + is-glob "^4.0.1" + +glob-parent@~5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.0.tgz#5f4c1d1e748d30cd73ad2944b3577a81b081e8c2" + dependencies: + is-glob "^4.0.1" + +glob@7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.0.0, glob@^7.0.5, glob@^7.1.1, glob@^7.1.3: + version "7.1.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +global-dirs@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-2.0.1.tgz#acdf3bb6685bcd55cb35e8a052266569e9469201" + dependencies: + ini "^1.3.5" + +globals@^11.1.0: + version "11.7.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.7.0.tgz#a583faa43055b1aca771914bf68258e2fc125673" + +globals@^12.1.0: + version "12.3.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-12.3.0.tgz#1e564ee5c4dded2ab098b0f88f24702a3c56be13" + dependencies: + type-fest "^0.8.1" + +got@^9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" + dependencies: + "@sindresorhus/is" "^0.14.0" + "@szmarczak/http-timer" "^1.1.2" + cacheable-request "^6.0.0" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^4.1.0" + lowercase-keys "^1.0.1" + mimic-response "^1.0.1" + p-cancelable "^1.0.0" + to-readable-stream "^1.0.0" + url-parse-lax "^3.0.0" + +graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6: + version "4.1.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" + +graceful-fs@^4.1.15: + version "4.2.2" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.2.tgz#6f0952605d0140c1cfdb138ed005775b92d67b02" + +growl@1.10.5: + version "1.10.5" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" + +handlebars@^4.1.2: + version "4.7.6" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.6.tgz#d4c05c1baf90e9945f77aa68a7a219aa4a7df74e" + dependencies: + minimist "^1.2.5" + neo-async "^2.6.0" + source-map "^0.6.1" + wordwrap "^1.0.0" + optionalDependencies: + uglify-js "^3.1.4" + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + +har-validator@~5.1.3: + version "5.1.5" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + dependencies: + ajv "^6.12.3" + har-schema "^2.0.0" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + dependencies: + ansi-regex "^2.0.0" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + +has-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" + +has-symbols@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" + +has-yarn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" + +has@^1.0.1, has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + dependencies: + function-bind "^1.1.1" + +hasha@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/hasha/-/hasha-3.0.0.tgz#52a32fab8569d41ca69a61ff1a214f8eb7c8bd39" + dependencies: + is-stream "^1.0.1" + +he@1.2.0, he@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + +helmet-crossdomain@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/helmet-crossdomain/-/helmet-crossdomain-0.4.0.tgz#5f1fe5a836d0325f1da0a78eaa5fd8429078894e" + +helmet-csp@2.9.4: + version "2.9.4" + resolved "https://registry.yarnpkg.com/helmet-csp/-/helmet-csp-2.9.4.tgz#801382bac98f2f88706dc5c89d95c7e31af3a4a9" + dependencies: + bowser "^2.7.0" + camelize "1.0.0" + content-security-policy-builder "2.1.0" + dasherize "2.0.0" + +helmet@^3.5.0: + version "3.21.2" + resolved "https://registry.yarnpkg.com/helmet/-/helmet-3.21.2.tgz#7e2a19d5f6d898a77b5d2858e8e4bb2cda59f19f" + dependencies: + depd "2.0.0" + dns-prefetch-control "0.2.0" + dont-sniff-mimetype "1.1.0" + expect-ct "0.2.0" + feature-policy "0.3.0" + frameguard "3.1.0" + helmet-crossdomain "0.4.0" + helmet-csp "2.9.4" + hide-powered-by "1.1.0" + hpkp "2.0.0" + hsts "2.2.0" + ienoopen "1.1.0" + nocache "2.1.0" + referrer-policy "1.2.0" + x-xss-protection "1.3.0" + +hide-powered-by@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/hide-powered-by/-/hide-powered-by-1.1.0.tgz#be3ea9cab4bdb16f8744be873755ca663383fa7a" + +hoek@4.x.x: + version "4.2.1" + resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb" + +hosted-git-info@^2.1.4: + version "2.5.0" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.5.0.tgz#6d60e34b3abbc8313062c3b798ef8d901a07af3c" + +hpkp@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hpkp/-/hpkp-2.0.0.tgz#10e142264e76215a5d30c44ec43de64dee6d1672" + +hsts@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/hsts/-/hsts-2.2.0.tgz#09119d42f7a8587035d027dda4522366fe75d964" + dependencies: + depd "2.0.0" + +html-to-text@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/html-to-text/-/html-to-text-5.1.1.tgz#2d89db7bf34bc7bcb7d546b1b228991a16926e87" + dependencies: + he "^1.2.0" + htmlparser2 "^3.10.1" + lodash "^4.17.11" + minimist "^1.2.0" + +htmlparser2@^3.10.1, htmlparser2@^3.9.1: + version "3.10.1" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" + dependencies: + domelementtype "^1.3.1" + domhandler "^2.3.0" + domutils "^1.5.1" + entities "^1.1.1" + inherits "^2.0.1" + readable-stream "^3.1.1" + +htmlparser2@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-4.0.0.tgz#6034658db65b7713a572a9ebf79f650832dceec8" + dependencies: + domelementtype "^2.0.1" + domhandler "^3.0.0" + domutils "^2.0.0" + entities "^2.0.0" + +http-cache-semantics@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + +http-errors@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-proxy-agent@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" + dependencies: + agent-base "4" + debug "3.1.0" + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +http-status@^1.0.1: + version "1.5.0" + resolved "https://registry.yarnpkg.com/http-status/-/http-status-1.5.0.tgz#2edfb02068d236ba60fd1481ad89219aa96e1677" + +https-proxy-agent@^2.2.1: + version "2.2.4" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" + dependencies: + agent-base "^4.3.0" + debug "^3.1.0" + +husky@^3.0.7: + version "3.1.0" + resolved "https://registry.yarnpkg.com/husky/-/husky-3.1.0.tgz#5faad520ab860582ed94f0c1a77f0f04c90b57c0" + dependencies: + chalk "^2.4.2" + ci-info "^2.0.0" + cosmiconfig "^5.2.1" + execa "^1.0.0" + get-stdin "^7.0.0" + opencollective-postinstall "^2.0.2" + pkg-dir "^4.2.0" + please-upgrade-node "^3.2.0" + read-pkg "^5.2.0" + run-node "^1.0.0" + slash "^3.0.0" + +i18n-locales@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/i18n-locales/-/i18n-locales-0.0.2.tgz#12e56046f1fa260e11658f4ac62f60b363479ff9" + +i18n@^0.8.4: + version "0.8.4" + resolved "https://registry.yarnpkg.com/i18n/-/i18n-0.8.4.tgz#948e1ee7ddefef18b2785969388e097327031bd0" + dependencies: + debug "*" + make-plural "^6.0.1" + math-interval-parser "^2.0.1" + messageformat "^2.3.0" + mustache "*" + sprintf-js "^1.1.2" + +iconv-lite@0.4.24, iconv-lite@^0.4.17, iconv-lite@^0.4.24, iconv-lite@^0.4.4: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ienoopen@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ienoopen/-/ienoopen-1.1.0.tgz#411e5d530c982287dbdc3bb31e7a9c9e32630974" + +ignore-by-default@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" + +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + +image-size@^0.7.3: + version "0.7.5" + resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.7.5.tgz#269f357cf5797cb44683dfa99790e54c705ead04" + +import-fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" + dependencies: + caller-path "^2.0.0" + resolve-from "^3.0.0" + +import-fresh@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.1.0.tgz#6d33fa1dcef6df930fae003446f33415af905118" + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-lazy@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + +indent-string@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.3, inherits@^2.0.3, inherits@~2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + +inherits@2.0.4, inherits@^2.0.1, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + +ini@^1.3.4, ini@^1.3.5: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + +ini@~1.3.0: + version "1.3.4" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" + +inquirer@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.0.0.tgz#9e2b032dde77da1db5db804758b8fea3a970519a" + dependencies: + ansi-escapes "^4.2.1" + chalk "^2.4.2" + cli-cursor "^3.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.15" + mute-stream "0.0.8" + run-async "^2.2.0" + rxjs "^6.4.0" + string-width "^4.1.0" + strip-ansi "^5.1.0" + through "^2.3.6" + +interpret@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.4.tgz#820cdd588b868ffb191a809506d6c9c8f212b1b0" + +ip@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" + +ipaddr.js@1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.0.tgz#37df74e430a0e47550fe54a2defe30d8acd95f65" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + dependencies: + binary-extensions "^2.0.0" + +is-buffer@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc" + +is-buffer@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725" + +is-buffer@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623" + +is-builtin-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" + dependencies: + builtin-modules "^1.0.0" + +is-callable@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" + +is-callable@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.0.tgz#83336560b54a38e35e3a2df7afd0454d691468bb" + +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + dependencies: + ci-info "^2.0.0" + +is-date-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" + +is-directory@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" + +is-expression@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-expression/-/is-expression-3.0.0.tgz#39acaa6be7fd1f3471dc42c7416e61c24317ac9f" + dependencies: + acorn "~4.0.2" + object-assign "^4.0.1" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + dependencies: + is-extglob "^2.1.1" + +is-installed-globally@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.3.2.tgz#fd3efa79ee670d1187233182d5b0a1dd00313141" + dependencies: + global-dirs "^2.0.1" + is-path-inside "^3.0.1" + +is-npm@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-4.0.0.tgz#c90dd8380696df87a7a6d823c20d0b12bbe3c84d" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + +is-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" + +is-path-inside@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.2.tgz#f5220fc82a3e233757291dddc9c5877f2a1f3017" + +is-plain-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + +is-promise@^2.0.0, is-promise@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" + +is-regex@^1.0.3, is-regex@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" + dependencies: + has "^1.0.1" + +is-regex@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" + dependencies: + has-symbols "^1.0.1" + +is-stream@^1.0.1, is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + +is-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" + +is-string@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" + +is-symbol@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38" + dependencies: + has-symbols "^1.0.0" + +is-typedarray@^1.0.0, is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + +is-yarn-global@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + +isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + +isemail@2.x.x: + version "2.2.1" + resolved "https://registry.yarnpkg.com/isemail/-/isemail-2.2.1.tgz#0353d3d9a62951080c262c2aa0a42b8ea8e9e2a6" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + +istanbul-lib-coverage@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz#675f0ab69503fad4b1d849f736baaca803344f49" + +istanbul-lib-hook@^2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz#c95695f383d4f8f60df1f04252a9550e15b5b133" + dependencies: + append-transform "^1.0.0" + +istanbul-lib-instrument@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz#a5f63d91f0bbc0c3e479ef4c5de027335ec6d630" + dependencies: + "@babel/generator" "^7.4.0" + "@babel/parser" "^7.4.3" + "@babel/template" "^7.4.0" + "@babel/traverse" "^7.4.3" + "@babel/types" "^7.4.0" + istanbul-lib-coverage "^2.0.5" + semver "^6.0.0" + +istanbul-lib-report@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz#5a8113cd746d43c4889eba36ab10e7d50c9b4f33" + dependencies: + istanbul-lib-coverage "^2.0.5" + make-dir "^2.1.0" + supports-color "^6.1.0" + +istanbul-lib-source-maps@^3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz#284997c48211752ec486253da97e3879defba8c8" + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^2.0.5" + make-dir "^2.1.0" + rimraf "^2.6.3" + source-map "^0.6.1" + +istanbul-reports@^2.2.4: + version "2.2.6" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-2.2.6.tgz#7b4f2660d82b29303a8fe6091f8ca4bf058da1af" + dependencies: + handlebars "^4.1.2" + +items@2.x.x: + version "2.1.1" + resolved "https://registry.yarnpkg.com/items/-/items-2.1.1.tgz#8bd16d9c83b19529de5aea321acaada78364a198" + +joi@^10.4.1: + version "10.6.0" + resolved "https://registry.yarnpkg.com/joi/-/joi-10.6.0.tgz#52587f02d52b8b75cdb0c74f0b164a191a0e1fc2" + dependencies: + hoek "4.x.x" + isemail "2.x.x" + items "2.x.x" + topo "2.x.x" + +js-git@^0.7.8: + version "0.7.8" + resolved "https://registry.yarnpkg.com/js-git/-/js-git-0.7.8.tgz#52fa655ab61877d6f1079efc6534b554f31e5444" + dependencies: + bodec "^0.1.0" + culvert "^0.1.2" + git-sha1 "^0.1.2" + pako "^0.2.5" + +js-stringify@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/js-stringify/-/js-stringify-1.0.2.tgz#1736fddfd9724f28a3682adc6230ae7e4e9679db" + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + +js-yaml@3.13.1, js-yaml@^3.13.1: + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + +jsesc@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.1.tgz#e421a2a8e20d6b0819df28908f782526b96dd1fe" + +json-buffer@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + +json-parse-better-errors@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + +json5@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + dependencies: + minimist "^1.2.0" + +jsonfile@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-3.0.1.tgz#a5ecc6f65f53f662c4415c7675a0331d0992ec66" + optionalDependencies: + graceful-fs "^4.1.6" + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + optionalDependencies: + graceful-fs "^4.1.6" + +jsonwebtoken@^8.2.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.3.0.tgz#056c90eee9a65ed6e6c72ddb0a1d325109aaf643" + dependencies: + jws "^3.1.5" + lodash.includes "^4.3.0" + lodash.isboolean "^3.0.3" + lodash.isinteger "^4.0.4" + lodash.isnumber "^3.0.3" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + lodash.once "^4.0.0" + ms "^2.1.1" + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +jstransformer@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/jstransformer/-/jstransformer-1.0.0.tgz#ed8bf0921e2f3f1ed4d5c1a44f68709ed24722c3" + dependencies: + is-promise "^2.0.0" + promise "^7.0.1" + +juice@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/juice/-/juice-6.0.0.tgz#cd8f8fe5210ef129d186fe2c41c0ec169f7b07b6" + dependencies: + cheerio "^0.22.0" + commander "^2.15.1" + cross-spawn "^6.0.5" + deep-extend "^0.6.0" + mensch "^0.3.4" + slick "^1.12.2" + web-resource-inliner "^4.3.3" + +just-extend@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.0.2.tgz#f3f47f7dfca0f989c55410a7ebc8854b07108afc" + +jwa@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.1.6.tgz#87240e76c9808dbde18783cf2264ef4929ee50e6" + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.10" + safe-buffer "^5.0.1" + +jws@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.1.5.tgz#80d12d05b293d1e841e7cb8b4e69e561adcf834f" + dependencies: + jwa "^1.1.5" + safe-buffer "^5.0.1" + +jwt-simple@0.5.6: + version "0.5.6" + resolved "https://registry.yarnpkg.com/jwt-simple/-/jwt-simple-0.5.6.tgz#3357adec55b26547114157be66748995b75b333a" + +kareem@2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/kareem/-/kareem-2.3.1.tgz#def12d9c941017fabfb00f873af95e9c99e1be87" + +keyv@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" + dependencies: + json-buffer "3.0.0" + +kind-of@^3.0.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + dependencies: + is-buffer "^1.1.5" + +klaw-sync@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/klaw-sync/-/klaw-sync-2.1.0.tgz#3d3bcd8600e7bfdef53231c739ff053aed560e44" + optionalDependencies: + graceful-fs "^4.1.11" + +kuler@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" + +latest-version@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" + dependencies: + package-json "^6.3.0" + +lazy-cache@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" + +lazy@~1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/lazy/-/lazy-1.0.11.tgz#daa068206282542c088288e975c297c1ae77b690" + +lcov-parse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lcov-parse/-/lcov-parse-1.0.0.tgz#eb0d46b54111ebc561acb4c408ef9363bdc8f7e0" + +levn@^0.3.0, levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +lines-and-columns@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" + +linkify-it@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.0.3.tgz#d94a4648f9b1c179d64fa97291268bdb6ce9434f" + dependencies: + uc.micro "^1.0.1" + +load-json-file@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + strip-bom "^3.0.0" + +load-json-file@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" + dependencies: + graceful-fs "^4.1.2" + parse-json "^4.0.0" + pify "^3.0.0" + strip-bom "^3.0.0" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + dependencies: + p-locate "^4.1.0" + +lodash.assignin@^4.0.9: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e263a8dc6a28a2" + +lodash.bind@^4.1.4: + version "4.2.1" + resolved "https://registry.yarnpkg.com/lodash.bind/-/lodash.bind-4.2.1.tgz#7ae3017e939622ac31b7d7d7dcb1b34db1690d35" + +lodash.defaults@^4.0.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" + +lodash.filter@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.filter/-/lodash.filter-4.6.0.tgz#668b1d4981603ae1cc5a6fa760143e480b4c4ace" + +lodash.findindex@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.findindex/-/lodash.findindex-4.6.0.tgz#a3245dee61fb9b6e0624b535125624bb69c11106" + +lodash.flatten@^4.2.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" + +lodash.flattendeep@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" + +lodash.foreach@^4.3.0, lodash.foreach@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53" + +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + +lodash.isnumber@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + +lodash.last@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash.last/-/lodash.last-3.0.0.tgz#242f663112dd4c6e63728c60a3c909d1bdadbd4c" + +lodash.map@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3" + +lodash.merge@^4.4.0: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + +lodash.pick@^4.2.1: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" + +lodash.reduce@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b" + +lodash.reject@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.reject/-/lodash.reject-4.6.0.tgz#80d6492dc1470864bbf583533b651f42a9f52415" + +lodash.some@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d" + +lodash.unescape@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c" + +lodash@4.17.14: + version "4.17.14" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.14.tgz#9ce487ae66c96254fe20b599f21b6816028078ba" + +lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@~4.17.4: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + +log-driver@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/log-driver/-/log-driver-1.2.7.tgz#63b95021f0702fedfa2c9bb0a24e7797d71871d8" + +log-symbols@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" + dependencies: + chalk "^2.0.1" + +logform@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/logform/-/logform-2.2.0.tgz#40f036d19161fc76b68ab50fdc7fe495544492f2" + dependencies: + colors "^1.2.1" + fast-safe-stringify "^2.0.4" + fecha "^4.2.0" + ms "^2.1.1" + triple-beam "^1.3.0" + +lolex@^4.1.0, lolex@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lolex/-/lolex-4.2.0.tgz#ddbd7f6213ca1ea5826901ab1222b65d714b3cd7" + +longest@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" + +loud-rejection@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" + dependencies: + currently-unhandled "^0.4.1" + signal-exit "^3.0.0" + +lowercase-keys@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" + +lowercase-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + +lru-cache@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.1.tgz#622e32e82488b49279114a4f9ecf45e7cd6bba55" + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +lru-cache@^4.1.2: + version "4.1.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +make-dir@^2.0.0, make-dir@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" + dependencies: + pify "^4.0.1" + semver "^5.6.0" + +make-dir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.0.0.tgz#1b5f39f6b9270ed33f9f054c5c0f84304989f801" + dependencies: + semver "^6.0.0" + +make-plural@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/make-plural/-/make-plural-4.3.0.tgz#f23de08efdb0cac2e0c9ba9f315b0dff6b4c2735" + optionalDependencies: + minimist "^1.2.0" + +make-plural@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/make-plural/-/make-plural-6.0.1.tgz#ed3839fac3f469ebbe505751d48fe3319769edfc" + +map-obj@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + +map-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-2.0.0.tgz#a65cd29087a92598b8791257a523e021222ac1f9" + +markdown-it@^8.3.1: + version "8.4.0" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-8.4.0.tgz#e2400881bf171f7018ed1bd9da441dac8af6306d" + dependencies: + argparse "^1.0.7" + entities "~1.1.1" + linkify-it "^2.0.0" + mdurl "^1.0.1" + uc.micro "^1.0.3" + +math-interval-parser@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/math-interval-parser/-/math-interval-parser-2.0.1.tgz#e22cd6d15a0a7f4c03aec560db76513da615bed4" + +mdurl@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + +memory-pager@^1.0.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/memory-pager/-/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5" + +mensch@^0.3.4: + version "0.3.4" + resolved "https://registry.yarnpkg.com/mensch/-/mensch-0.3.4.tgz#770f91b46cb16ea5b204ee735768c3f0c491fecd" + +meow@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-5.0.0.tgz#dfc73d63a9afc714a5e371760eb5c88b91078aa4" + dependencies: + camelcase-keys "^4.0.0" + decamelize-keys "^1.0.0" + loud-rejection "^1.0.0" + minimist-options "^3.0.1" + normalize-package-data "^2.3.4" + read-pkg-up "^3.0.0" + redent "^2.0.0" + trim-newlines "^2.0.0" + yargs-parser "^10.0.0" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + +merge-source-map@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.1.0.tgz#2fdde7e6020939f70906a68f2d7ae685e4c8c646" + dependencies: + source-map "^0.6.1" + +messageformat-formatters@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/messageformat-formatters/-/messageformat-formatters-2.0.1.tgz#0492c1402a48775f751c9b17c0354e92be012b08" + +messageformat-parser@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/messageformat-parser/-/messageformat-parser-4.1.2.tgz#fd34ec39912a14868a1595eaeb742485ab8ab372" + +messageformat@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/messageformat/-/messageformat-2.3.0.tgz#de263c49029d5eae65d7ee25e0754f57f425ad91" + dependencies: + make-plural "^4.3.0" + messageformat-formatters "^2.0.1" + messageformat-parser "^4.1.2" + +method-override@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/method-override/-/method-override-3.0.0.tgz#6ab0d5d574e3208f15b0c9cf45ab52000468d7a2" + dependencies: + debug "3.1.0" + methods "~1.1.2" + parseurl "~1.3.2" + vary "~1.1.2" + +methods@^1.1.1, methods@^1.1.2, methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + +mime-db@1.40.0: + version "1.40.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32" + +"mime-db@>= 1.40.0 < 2": + version "1.42.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.42.0.tgz#3e252907b4c7adb906597b4b65636272cf9e7bac" + +mime-db@~1.30.0: + version "1.30.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" + +mime-types@^2.1.12: + version "2.1.17" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a" + dependencies: + mime-db "~1.30.0" + +mime-types@~2.1.19, mime-types@~2.1.24: + version "2.1.24" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81" + dependencies: + mime-db "1.40.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + +mime@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" + +mimer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/mimer/-/mimer-1.0.0.tgz#32251bef4dc7a63184db3a1082ed9be3abe0f3db" + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + +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" + +minimatch@3.0.4, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + dependencies: + brace-expansion "^1.1.7" + +minimist-options@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-3.0.2.tgz#fba4c8191339e13ecf4d61beb03f070103f3d954" + dependencies: + arrify "^1.0.1" + is-plain-obj "^1.1.0" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + +minimist@^1.2.0, minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + +mkdirp@0.5.1, mkdirp@^0.5.0, mkdirp@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" + +mocha@^6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-6.2.2.tgz#5d8987e28940caf8957a7d7664b910dc5b2fea20" + dependencies: + ansi-colors "3.2.3" + browser-stdout "1.3.1" + debug "3.2.6" + diff "3.5.0" + escape-string-regexp "1.0.5" + find-up "3.0.0" + glob "7.1.3" + growl "1.10.5" + he "1.2.0" + js-yaml "3.13.1" + log-symbols "2.2.0" + minimatch "3.0.4" + mkdirp "0.5.1" + ms "2.1.1" + node-environment-flags "1.0.5" + object.assign "4.1.0" + strip-json-comments "2.0.1" + supports-color "6.0.0" + which "1.3.1" + wide-align "1.1.3" + yargs "13.3.0" + yargs-parser "13.1.1" + yargs-unparser "1.6.0" + +module-details-from-path@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b" + +moment-timezone@^0.5.13, moment-timezone@^0.5.x: + version "0.5.27" + resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.27.tgz#73adec8139b6fe30452e78f210f27b1f346b8877" + dependencies: + moment ">= 2.9.0" + +moment@2.24.0, "moment@>= 2.9.0", moment@^2.21.0, moment@^2.24.0: + version "2.24.0" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" + +mongodb@3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-3.6.0.tgz#babd7172ec717e2ed3f85e079b3f1aa29dce4724" + dependencies: + bl "^2.2.0" + bson "^1.1.4" + denque "^1.4.1" + require_optional "^1.0.1" + safe-buffer "^5.1.2" + optionalDependencies: + saslprep "^1.0.0" + +mongoose-legacy-pluralize@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz#3ba9f91fa507b5186d399fb40854bff18fb563e4" + +mongoose@^5.2.17: + version "5.10.0" + resolved "https://registry.yarnpkg.com/mongoose/-/mongoose-5.10.0.tgz#05a35f5a3d8485613c9988aeb9548285a97083f7" + dependencies: + bson "^1.1.4" + kareem "2.3.1" + mongodb "3.6.0" + mongoose-legacy-pluralize "1.0.2" + mpath "0.7.0" + mquery "3.2.2" + ms "2.1.2" + regexp-clone "1.0.0" + safe-buffer "5.2.1" + sift "7.0.1" + sliced "1.0.1" + +morgan@^1.8.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.9.1.tgz#0a8d16734a1d9afbc824b99df87e738e58e2da59" + dependencies: + basic-auth "~2.0.0" + debug "2.6.9" + depd "~1.1.2" + on-finished "~2.3.0" + on-headers "~1.0.1" + +mpath@0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/mpath/-/mpath-0.7.0.tgz#20e8102e276b71709d6e07e9f8d4d0f641afbfb8" + +mquery@3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/mquery/-/mquery-3.2.2.tgz#e1383a3951852ce23e37f619a9b350f1fb3664e7" + dependencies: + bluebird "3.5.1" + debug "3.1.0" + regexp-clone "^1.0.0" + safe-buffer "5.1.2" + sliced "1.0.1" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + +ms@2.1.2, ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + +multimatch@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-4.0.0.tgz#8c3c0f6e3e8449ada0af3dd29efb491a375191b3" + dependencies: + "@types/minimatch" "^3.0.3" + array-differ "^3.0.0" + array-union "^2.1.0" + arrify "^2.0.1" + minimatch "^3.0.4" + +mustache@*: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mustache/-/mustache-3.1.0.tgz#9fba26e7aefc5709f07ff585abb7e0abced6c372" + +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + +mute-stream@~0.0.4: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + +needle@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c" + dependencies: + debug "^3.2.6" + iconv-lite "^0.4.4" + sax "^1.2.4" + +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + +neo-async@^2.6.0: + version "2.6.1" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" + +nested-error-stacks@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz#0fbdcf3e13fe4994781280524f8b96b0cdff9c61" + +netmask@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35" + +nice-try@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.4.tgz#d93962f6c52f2c1558c0fbda6d512819f1efe1c4" + +nise@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/nise/-/nise-1.5.2.tgz#b6d29af10e48b321b307e10e065199338eeb2652" + dependencies: + "@sinonjs/formatio" "^3.2.1" + "@sinonjs/text-encoding" "^0.7.1" + just-extend "^4.0.2" + lolex "^4.1.0" + path-to-regexp "^1.7.0" + +nocache@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/nocache/-/nocache-2.1.0.tgz#120c9ffec43b5729b1d5de88cd71aa75a0ba491f" + +node-environment-flags@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.5.tgz#fa930275f5bf5dae188d6192b24b4c8bbac3d76a" + dependencies: + object.getownpropertydescriptors "^2.0.3" + semver "^5.7.0" + +nodemailer@^6.3.1: + version "6.4.11" + resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.4.11.tgz#1f00b4ffd106403f17c03f3d43d5945b2677046c" + +nodemon@^2.0.1: + version "2.0.4" + resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.4.tgz#55b09319eb488d6394aa9818148c0c2d1c04c416" + dependencies: + chokidar "^3.2.2" + debug "^3.2.6" + ignore-by-default "^1.0.1" + minimatch "^3.0.4" + pstree.remy "^1.1.7" + semver "^5.7.1" + supports-color "^5.5.0" + touch "^3.1.0" + undefsafe "^2.0.2" + update-notifier "^4.0.0" + +nopt@~1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" + dependencies: + abbrev "1" + +normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: + version "2.4.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" + dependencies: + hosted-git-info "^2.1.4" + is-builtin-module "^1.0.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-package-data@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + +normalize-url@^4.1.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + dependencies: + path-key "^2.0.0" + +nssocket@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/nssocket/-/nssocket-0.6.0.tgz#59f96f6ff321566f33c70f7dbeeecdfdc07154fa" + dependencies: + eventemitter2 "~0.4.14" + lazy "~1.0.11" + +nth-check@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" + dependencies: + boolbase "~1.0.0" + +nyc@^14.1.1: + version "14.1.1" + resolved "https://registry.yarnpkg.com/nyc/-/nyc-14.1.1.tgz#151d64a6a9f9f5908a1b73233931e4a0a3075eeb" + dependencies: + archy "^1.0.0" + caching-transform "^3.0.2" + convert-source-map "^1.6.0" + cp-file "^6.2.0" + find-cache-dir "^2.1.0" + find-up "^3.0.0" + foreground-child "^1.5.6" + glob "^7.1.3" + istanbul-lib-coverage "^2.0.5" + istanbul-lib-hook "^2.0.7" + istanbul-lib-instrument "^3.3.0" + istanbul-lib-report "^2.0.8" + istanbul-lib-source-maps "^3.0.6" + istanbul-reports "^2.2.4" + js-yaml "^3.13.1" + make-dir "^2.1.0" + merge-source-map "^1.1.0" + resolve-from "^4.0.0" + rimraf "^2.6.3" + signal-exit "^3.0.2" + spawn-wrap "^1.4.2" + test-exclude "^5.2.3" + uuid "^3.3.2" + yargs "^13.2.2" + yargs-parser "^13.0.0" + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + +object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + +object-inspect@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.6.0.tgz#c70b6cbf72f274aab4c34c0c82f5167bf82cf15b" + +object-inspect@^1.7.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0" + +object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + +object.assign@4.1.0, object.assign@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + has-symbols "^1.0.0" + object-keys "^1.0.11" + +object.getownpropertydescriptors@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" + dependencies: + define-properties "^1.1.2" + es-abstract "^1.5.1" + +object.values@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e" + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + function-bind "^1.1.1" + has "^1.0.3" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.1, on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + dependencies: + wrappy "1" + +one-time@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/one-time/-/one-time-1.0.0.tgz#e06bc174aed214ed58edede573b433bbf827cb45" + dependencies: + fn.name "1.x.x" + +onetime@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.0.tgz#fff0f3c91617fe62bb50189636e99ac8a6df7be5" + dependencies: + mimic-fn "^2.1.0" + +open@^6.3.0, open@^6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/open/-/open-6.4.0.tgz#5c13e96d0dc894686164f18965ecfe889ecfc8a9" + dependencies: + is-wsl "^1.1.0" + +opencollective-postinstall@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz#5657f1bede69b6e33a45939b061eb53d3c6c3a89" + +opn-cli@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/opn-cli/-/opn-cli-5.0.0.tgz#e576fca5991333465d092a05ddfe8bf796c1bd0c" + dependencies: + file-type "^11.0.0" + get-stdin "^7.0.0" + meow "^5.0.0" + open "^6.3.0" + temp-write "^4.0.0" + +optionator@^0.8.1, optionator@^0.8.3: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +os-homedir@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + +p-cancelable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + +p-limit@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.1.0.tgz#b07ff2d9a5d88bec806035895a2bab66a27988bc" + +p-limit@^2.0.0, p-limit@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.1.tgz#aa07a788cc3151c939b5131f63570f0dd2009537" + dependencies: + p-try "^2.0.0" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + dependencies: + p-limit "^1.1.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + dependencies: + p-limit "^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" + 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" + +pac-proxy-agent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-3.0.0.tgz#11d578b72a164ad74bf9d5bac9ff462a38282432" + dependencies: + agent-base "^4.2.0" + debug "^3.1.0" + get-uri "^2.0.0" + http-proxy-agent "^2.1.0" + https-proxy-agent "^2.2.1" + pac-resolver "^3.0.0" + raw-body "^2.2.0" + socks-proxy-agent "^4.0.1" + +pac-resolver@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-3.0.0.tgz#6aea30787db0a891704deb7800a722a7615a6f26" + dependencies: + co "^4.6.0" + degenerator "^1.0.4" + ip "^1.1.5" + netmask "^1.0.6" + thunkify "^2.1.2" + +package-hash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/package-hash/-/package-hash-3.0.0.tgz#50183f2d36c9e3e528ea0a8605dff57ce976f88e" + dependencies: + graceful-fs "^4.1.15" + hasha "^3.0.0" + lodash.flattendeep "^4.4.0" + release-zalgo "^1.0.0" + +package-json@^6.3.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" + dependencies: + got "^9.6.0" + registry-auth-token "^4.0.0" + registry-url "^5.0.0" + semver "^6.2.0" + +pako@^0.2.5: + version "0.2.9" + resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + dependencies: + callsites "^3.0.0" + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + dependencies: + error-ex "^1.2.0" + +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + +parse-json@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.0.0.tgz#73e5114c986d143efa3712d4ea24db9a4266f60f" + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + lines-and-columns "^1.1.6" + +parseurl@~1.3.2, parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + +passport-http-bearer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/passport-http-bearer/-/passport-http-bearer-1.0.1.tgz#147469ea3669e2a84c6167ef99dbb77e1f0098a8" + dependencies: + passport-strategy "1.x.x" + +passport-jwt@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/passport-jwt/-/passport-jwt-4.0.0.tgz#7f0be7ba942e28b9f5d22c2ebbb8ce96ef7cf065" + dependencies: + jsonwebtoken "^8.2.0" + passport-strategy "^1.0.0" + +passport-strategy@1.x.x, passport-strategy@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4" + +passport@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/passport/-/passport-0.4.0.tgz#c5095691347bd5ad3b5e180238c3914d16f05811" + dependencies: + passport-strategy "1.x.x" + pause "0.0.1" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + +path-key@^2.0.0, path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + +path-key@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.0.tgz#99a10d870a803bdd5ee6f0470e58dfcd2f9a54d3" + +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + +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" + +path-to-regexp@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d" + dependencies: + isarray "0.0.1" + +path-type@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" + dependencies: + pify "^2.0.0" + +path-type@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + dependencies: + pify "^3.0.0" + +pathval@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0" + +pause@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/pause/-/pause-0.0.1.tgz#1d408b3fdb76923b9543d96fb4c9dfd535d9cb5d" + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + +picomatch@^2.0.4: + version "2.1.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.1.1.tgz#ecdfbea7704adb5fe6fb47f9866c4c0e15e905c5" + +pidusage@2.0.17: + version "2.0.17" + resolved "https://registry.yarnpkg.com/pidusage/-/pidusage-2.0.17.tgz#6b4a2b4a09026f0e9828f7e5627837e4c0672581" + dependencies: + safe-buffer "^5.1.2" + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + +pkg-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" + dependencies: + find-up "^2.1.0" + +pkg-dir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" + dependencies: + find-up "^3.0.0" + +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + dependencies: + find-up "^4.0.0" + +please-upgrade-node@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz#aeddd3f994c933e4ad98b99d9a556efa0e2fe942" + dependencies: + semver-compare "^1.0.0" + +pm2-axon-rpc@0.5.1, pm2-axon-rpc@^0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/pm2-axon-rpc/-/pm2-axon-rpc-0.5.1.tgz#ad3c43c43811c71f13e5eee2821194d03ceb03fe" + dependencies: + debug "^3.0" + +pm2-axon@3.3.0, pm2-axon@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/pm2-axon/-/pm2-axon-3.3.0.tgz#a9badfdb8e083fbd5d7d24317b4a21eb708f0735" + dependencies: + amp "~0.3.1" + amp-message "~0.1.1" + debug "^3.0" + escape-regexp "0.0.1" + +pm2-deploy@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/pm2-deploy/-/pm2-deploy-0.4.0.tgz#d543076919f7776c57eb75841a4320f547287661" + dependencies: + async "^2.6" + tv4 "^1.3" + +pm2-multimeter@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/pm2-multimeter/-/pm2-multimeter-0.1.2.tgz#1a1e55153d41a05534cea23cfe860abaa0eb4ace" + dependencies: + charm "~0.1.1" + +pm2@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pm2/-/pm2-4.2.0.tgz#2ddf1cfbfb678434ef2bf956370c6a354a6184e2" + dependencies: + "@pm2/agent" "^0.5.26" + "@pm2/io" "^4.3.2" + "@pm2/js-api" "^0.5.60" + async "^3.1.0" + blessed "0.1.81" + chalk "2.4.2" + chokidar "^3.2.0" + cli-table-redemption "1.0.1" + commander "2.15.1" + cron "1.7.1" + date-fns "1.30.1" + debug "4.1.1" + enquirer "^2.3.2" + eventemitter2 "5.0.1" + fclone "1.0.11" + lodash "4.17.14" + mkdirp "0.5.1" + moment "2.24.0" + needle "2.4.0" + pidusage "2.0.17" + pm2-axon "3.3.0" + pm2-axon-rpc "0.5.1" + pm2-deploy "^0.4.0" + pm2-multimeter "^0.1.2" + promptly "^2" + ps-list "6.3.0" + semver "^5.5" + shelljs "0.8.3" + source-map-support "0.5.12" + sprintf-js "1.1.2" + vizion "~2.0.2" + yamljs "0.3.0" + optionalDependencies: + systeminformation "^4.14.16" + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + +prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + +preview-email@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/preview-email/-/preview-email-1.0.2.tgz#c158da70cf723f9603d71d5d2662fd1ad246d73a" + dependencies: + "@babel/runtime" "^7.6.3" + dayjs "^1.8.16" + debug "^4.1.1" + nodemailer "^6.3.1" + open "^6.4.0" + pify "^4.0.1" + pug "^2.0.4" + uuid "^3.3.3" + +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" + +progress@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f" + +promise@^7.0.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" + dependencies: + asap "~2.0.3" + +promptly@^2: + version "2.2.0" + resolved "https://registry.yarnpkg.com/promptly/-/promptly-2.2.0.tgz#2a13fa063688a2a5983b161fff0108a07d26fc74" + dependencies: + read "^1.0.4" + +proxy-addr@~2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.5.tgz#34cbd64a2d81f4b1fd21e76f9f06c8a45299ee34" + dependencies: + forwarded "~0.1.2" + ipaddr.js "1.9.0" + +proxy-agent@^3.0.3, proxy-agent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-3.1.0.tgz#3cf86ee911c94874de4359f37efd9de25157c113" + dependencies: + agent-base "^4.2.0" + debug "^3.1.0" + http-proxy-agent "^2.1.0" + https-proxy-agent "^2.2.1" + lru-cache "^4.1.2" + pac-proxy-agent "^3.0.0" + proxy-from-env "^1.0.0" + socks-proxy-agent "^4.0.1" + +proxy-from-env@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee" + +ps-list@6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/ps-list/-/ps-list-6.3.0.tgz#a2b775c2db7d547a28fbaa3a05e4c281771259be" + +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + +psl@^1.1.28: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + +pstree.remy@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.7.tgz#c76963a28047ed61542dc361aa26ee55a7fa15f3" + +pug-attrs@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pug-attrs/-/pug-attrs-2.0.4.tgz#b2f44c439e4eb4ad5d4ef25cac20d18ad28cc336" + dependencies: + constantinople "^3.0.1" + js-stringify "^1.0.1" + pug-runtime "^2.0.5" + +pug-code-gen@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/pug-code-gen/-/pug-code-gen-2.0.2.tgz#ad0967162aea077dcf787838d94ed14acb0217c2" + dependencies: + constantinople "^3.1.2" + doctypes "^1.1.0" + js-stringify "^1.0.1" + pug-attrs "^2.0.4" + pug-error "^1.3.3" + pug-runtime "^2.0.5" + void-elements "^2.0.1" + with "^5.0.0" + +pug-error@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/pug-error/-/pug-error-1.3.3.tgz#f342fb008752d58034c185de03602dd9ffe15fa6" + +pug-filters@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/pug-filters/-/pug-filters-3.1.1.tgz#ab2cc82db9eeccf578bda89130e252a0db026aa7" + dependencies: + clean-css "^4.1.11" + constantinople "^3.0.1" + jstransformer "1.0.0" + pug-error "^1.3.3" + pug-walk "^1.1.8" + resolve "^1.1.6" + uglify-js "^2.6.1" + +pug-lexer@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/pug-lexer/-/pug-lexer-4.1.0.tgz#531cde48c7c0b1fcbbc2b85485c8665e31489cfd" + dependencies: + character-parser "^2.1.1" + is-expression "^3.0.0" + pug-error "^1.3.3" + +pug-linker@^3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/pug-linker/-/pug-linker-3.0.6.tgz#f5bf218b0efd65ce6670f7afc51658d0f82989fb" + dependencies: + pug-error "^1.3.3" + pug-walk "^1.1.8" + +pug-load@^2.0.12: + version "2.0.12" + resolved "https://registry.yarnpkg.com/pug-load/-/pug-load-2.0.12.tgz#d38c85eb85f6e2f704dea14dcca94144d35d3e7b" + dependencies: + object-assign "^4.1.0" + pug-walk "^1.1.8" + +pug-parser@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/pug-parser/-/pug-parser-5.0.1.tgz#03e7ada48b6840bd3822f867d7d90f842d0ffdc9" + dependencies: + pug-error "^1.3.3" + token-stream "0.0.1" + +pug-runtime@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/pug-runtime/-/pug-runtime-2.0.5.tgz#6da7976c36bf22f68e733c359240d8ae7a32953a" + +pug-strip-comments@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/pug-strip-comments/-/pug-strip-comments-1.0.4.tgz#cc1b6de1f6e8f5931cf02ec66cdffd3f50eaf8a8" + dependencies: + pug-error "^1.3.3" + +pug-walk@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/pug-walk/-/pug-walk-1.1.8.tgz#b408f67f27912f8c21da2f45b7230c4bd2a5ea7a" + +pug@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pug/-/pug-2.0.4.tgz#ee7682ec0a60494b38d48a88f05f3b0ac931377d" + dependencies: + pug-code-gen "^2.0.2" + pug-filters "^3.1.1" + pug-lexer "^4.1.0" + pug-linker "^3.0.6" + pug-load "^2.0.12" + pug-parser "^5.0.1" + pug-runtime "^2.0.5" + pug-strip-comments "^1.0.4" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + +pupa@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.0.1.tgz#dbdc9ff48ffbea4a26a069b6f9f7abb051008726" + dependencies: + escape-goat "^2.0.0" + +qs@6.7.0, qs@^6.5.1: + version "6.7.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + +qs@^6.9.1: + version "6.9.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.1.tgz#20082c65cb78223635ab1a9eaca8875a29bf8ec9" + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + +quick-lru@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8" + +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + +raw-body@2.4.0, raw-body@^2.2.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" + dependencies: + bytes "3.1.0" + http-errors "1.7.2" + iconv-lite "0.4.24" + unpipe "1.0.0" + +rc@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +read-pkg-up@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" + dependencies: + find-up "^2.0.0" + read-pkg "^2.0.0" + +read-pkg-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" + dependencies: + find-up "^2.0.0" + read-pkg "^3.0.0" + +read-pkg-up@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-4.0.0.tgz#1b221c6088ba7799601c808f91161c66e58f8978" + dependencies: + find-up "^3.0.0" + read-pkg "^3.0.0" + +read-pkg@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" + dependencies: + load-json-file "^2.0.0" + normalize-package-data "^2.3.2" + path-type "^2.0.0" + +read-pkg@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" + dependencies: + load-json-file "^4.0.0" + normalize-package-data "^2.3.2" + path-type "^3.0.0" + +read-pkg@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" + dependencies: + "@types/normalize-package-data" "^2.4.0" + normalize-package-data "^2.5.0" + parse-json "^5.0.0" + type-fest "^0.6.0" + +read@^1.0.4: + version "1.0.7" + resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" + dependencies: + mute-stream "~0.0.4" + +readable-stream@1.1.x: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@3, readable-stream@^3.1.1, readable-stream@^3.4.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-stream@^2.3.5, readable-stream@^2.3.7: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readdirp@~3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.2.0.tgz#c30c33352b12c96dfb4b895421a49fd5a9593839" + dependencies: + picomatch "^2.0.4" + +rechoir@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + dependencies: + resolve "^1.1.6" + +redent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-2.0.0.tgz#c1b2007b42d57eb1389079b3c8333639d5e1ccaa" + dependencies: + indent-string "^3.0.0" + strip-indent "^2.0.0" + +referrer-policy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/referrer-policy/-/referrer-policy-1.2.0.tgz#b99cfb8b57090dc454895ef897a4cc35ef67a98e" + +regenerator-runtime@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" + +regenerator-runtime@^0.13.2: + version "0.13.3" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5" + +regexp-clone@1.0.0, regexp-clone@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/regexp-clone/-/regexp-clone-1.0.0.tgz#222db967623277056260b992626354a04ce9bf63" + +regexpp@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" + +registry-auth-token@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.0.tgz#1d37dffda72bbecd0f581e4715540213a65eb7da" + dependencies: + rc "^1.2.8" + +registry-url@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009" + dependencies: + rc "^1.2.8" + +release-zalgo@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/release-zalgo/-/release-zalgo-1.0.0.tgz#09700b7e5074329739330e535c5a90fb67851730" + dependencies: + es6-error "^4.0.1" + +repeat-string@^1.5.2: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + +request@^2.88.0, request@^2.88.2: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + +require-in-the-middle@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-5.0.2.tgz#ce3593007a61583b39ccdcd2c167a2a326c670b2" + dependencies: + debug "^4.1.1" + module-details-from-path "^1.0.3" + resolve "^1.12.0" + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + +require_optional@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require_optional/-/require_optional-1.0.1.tgz#4cf35a4247f64ca3df8c2ef208cc494b1ca8fc2e" + dependencies: + resolve-from "^2.0.0" + semver "^5.1.0" + +resolve-from@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-2.0.0.tgz#9480ab20e94ffa1d9e80a804c7ea147611966b57" + +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.17.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + dependencies: + path-parse "^1.0.6" + +responselike@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + dependencies: + lowercase-keys "^1.0.0" + +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + +right-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" + dependencies: + align-text "^0.1.1" + +rimraf@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + dependencies: + glob "^7.1.3" + +rimraf@^2.6.2, rimraf@^2.6.3: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + dependencies: + glob "^7.1.3" + +run-async@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" + dependencies: + is-promise "^2.1.0" + +run-node@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/run-node/-/run-node-1.0.0.tgz#46b50b946a2aa2d4947ae1d886e9856fd9cabe5e" + +rxjs@^6.4.0: + version "6.5.3" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.3.tgz#510e26317f4db91a7eb1de77d9dd9ba0a4899a3a" + dependencies: + tslib "^1.9.0" + +safe-buffer@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" + +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + +safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + +saslprep@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/saslprep/-/saslprep-1.0.3.tgz#4c02f946b56cf54297e347ba1093e7acac4cf226" + dependencies: + sparse-bitfield "^3.0.3" + +sax@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + +semver-compare@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" + +semver-diff@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" + dependencies: + semver "^6.3.0" + +"semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.3.0, semver@^5.5, semver@^5.5.0, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + +semver@5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" + +semver@^6.0.0, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + +semver@~5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" + +send@0.17.1: + version "0.17.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.7.2" + mime "1.6.0" + ms "2.1.1" + on-finished "~2.3.0" + range-parser "~1.2.1" + statuses "~1.5.0" + +serve-static@1.14.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.1" + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + dependencies: + shebang-regex "^1.0.0" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + +shelljs@0.8.3: + version "0.8.3" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.3.tgz#a7f3319520ebf09ee81275b2368adb286659b097" + dependencies: + glob "^7.0.0" + interpret "^1.0.0" + rechoir "^0.6.2" + +shimmer@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.1.0.tgz#97d7377137ffbbab425522e429fe0aa89a488b35" + +shimmer@^1.2.0, shimmer@~1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" + +sift@7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/sift/-/sift-7.0.1.tgz#47d62c50b159d316f1372f8b53f9c10cd21a4b08" + +signal-exit@3.0.2, signal-exit@^3.0.0, signal-exit@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + dependencies: + is-arrayish "^0.3.1" + +sinon-chai@^3.0.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/sinon-chai/-/sinon-chai-3.3.0.tgz#8084ff99451064910fbe2c2cb8ab540c00b740ea" + +sinon@^7.5.0: + version "7.5.0" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-7.5.0.tgz#e9488ea466070ea908fd44a3d6478fd4923c67ec" + dependencies: + "@sinonjs/commons" "^1.4.0" + "@sinonjs/formatio" "^3.2.1" + "@sinonjs/samsam" "^3.3.3" + diff "^3.5.0" + lolex "^4.2.0" + nise "^1.5.2" + supports-color "^5.5.0" + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + +sliced@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sliced/-/sliced-1.0.1.tgz#0b3a662b5d04c3177b1926bea82b03f837a2ef41" + +slick@^1.12.2: + version "1.12.2" + resolved "https://registry.yarnpkg.com/slick/-/slick-1.12.2.tgz#bd048ddb74de7d1ca6915faa4a57570b3550c2d7" + +smart-buffer@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.0.2.tgz#5207858c3815cc69110703c6b94e46c15634395d" + +socks-proxy-agent@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz#3c8991f3145b2799e70e11bd5fbc8b1963116386" + dependencies: + agent-base "~4.2.1" + socks "~2.3.2" + +socks@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.3.2.tgz#ade388e9e6d87fdb11649c15746c578922a5883e" + dependencies: + ip "^1.1.5" + smart-buffer "4.0.2" + +source-map-support@0.5.12: + version "0.5.12" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599" + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.5.0, source-map@~0.5.1: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + +sparse-bitfield@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz#ff4ae6e68656056ba4b3e792ab3334d38273ca11" + dependencies: + memory-pager "^1.0.2" + +spawn-wrap@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/spawn-wrap/-/spawn-wrap-1.4.2.tgz#cff58e73a8224617b6561abdc32586ea0c82248c" + dependencies: + foreground-child "^1.5.6" + mkdirp "^0.5.0" + os-homedir "^1.0.1" + rimraf "^2.6.2" + signal-exit "^3.0.2" + which "^1.3.0" + +spdx-correct@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40" + dependencies: + spdx-license-ids "^1.0.2" + +spdx-expression-parse@~1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz#9bdf2f20e1f40ed447fbe273266191fced51626c" + +spdx-license-ids@^1.0.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57" + +sprintf-js@1.1.2, sprintf-js@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + +sshpk@^1.7.0: + version "1.15.2" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.15.2.tgz#c946d6bd9b1a39d0e8635763f5242d6ed6dcb629" + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +stack-trace@0.0.x: + version "0.0.10" + resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" + +"statuses@>= 1.5.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + +"string-width@^1.0.2 || 2": + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string-width@^3.0.0, string-width@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string-width@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + +string-width@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.1.0.tgz#ba846d1daa97c3c596155308063e075ed1c99aff" + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^5.2.0" + +string.prototype.trimend@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913" + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + +string.prototype.trimleft@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz#6cc47f0d7eb8d62b0f3701611715a3954591d634" + dependencies: + define-properties "^1.1.3" + function-bind "^1.1.1" + +string.prototype.trimright@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz#669d164be9df9b6f7559fa8e89945b168a5a6c58" + dependencies: + define-properties "^1.1.3" + function-bind "^1.1.1" + +string.prototype.trimstart@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54" + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + +string_decoder@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.2.0.tgz#fe86e738b19544afe70469243b2a1ee9240eae8d" + dependencies: + safe-buffer "~5.1.0" + +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + dependencies: + ansi-regex "^4.1.0" + +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + dependencies: + ansi-regex "^5.0.0" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + +strip-indent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" + +strip-json-comments@2.0.1, 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" + +strip-json-comments@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" + +superagent@^3.8.3: + version "3.8.3" + resolved "https://registry.yarnpkg.com/superagent/-/superagent-3.8.3.tgz#460ea0dbdb7d5b11bc4f78deba565f86a178e128" + dependencies: + component-emitter "^1.2.0" + cookiejar "^2.1.0" + debug "^3.1.0" + extend "^3.0.0" + form-data "^2.3.1" + formidable "^1.2.0" + methods "^1.1.1" + mime "^1.4.1" + qs "^6.5.1" + readable-stream "^2.3.5" + +supertest@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/supertest/-/supertest-4.0.2.tgz#c2234dbdd6dc79b6f15b99c8d6577b90e4ce3f36" + dependencies: + methods "^1.1.2" + superagent "^3.8.3" + +supports-color@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.0.0.tgz#76cfe742cf1f41bb9b1c29ad03068c05b4c0e40a" + dependencies: + has-flag "^3.0.0" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + +supports-color@^5.3.0, supports-color@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + dependencies: + has-flag "^3.0.0" + +supports-color@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + dependencies: + has-flag "^4.0.0" + +systeminformation@^4.14.16: + version "4.30.7" + resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-4.30.7.tgz#7e211875aada42abc8bb46cd6c652a274d7e78e0" + +table@^5.2.3: + version "5.4.6" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" + dependencies: + ajv "^6.10.2" + lodash "^4.17.14" + slice-ansi "^2.1.0" + string-width "^3.0.0" + +temp-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d" + +temp-write@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/temp-write/-/temp-write-4.0.0.tgz#cd2e0825fc826ae72d201dc26eef3bf7e6fc9320" + dependencies: + graceful-fs "^4.1.15" + is-stream "^2.0.0" + make-dir "^3.0.0" + temp-dir "^1.0.0" + uuid "^3.3.2" + +term-size@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.0.tgz#1f16adedfe9bdc18800e1776821734086fcc6753" + +test-exclude@^5.2.3: + version "5.2.3" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-5.2.3.tgz#c3d3e1e311eb7ee405e092dac10aefd09091eac0" + dependencies: + glob "^7.1.3" + minimatch "^3.0.4" + read-pkg-up "^4.0.0" + require-main-filename "^2.0.0" + +text-hex@1.0.x: + version "1.0.0" + resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + +through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + +thunkify@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/thunkify/-/thunkify-2.1.2.tgz#faa0e9d230c51acc95ca13a361ac05ca7e04553d" + +titleize@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/titleize/-/titleize-2.1.0.tgz#5530de07c22147a0488887172b5bd94f5b30a48f" + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + dependencies: + os-tmpdir "~1.0.2" + +to-fast-properties@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + +to-readable-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + dependencies: + is-number "^7.0.0" + +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + +token-stream@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/token-stream/-/token-stream-0.0.1.tgz#ceeefc717a76c4316f126d0b9dbaa55d7e7df01a" + +topo@2.x.x: + version "2.0.2" + resolved "https://registry.yarnpkg.com/topo/-/topo-2.0.2.tgz#cd5615752539057c0dc0491a621c3bc6fbe1d182" + dependencies: + hoek "4.x.x" + +touch@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b" + dependencies: + nopt "~1.0.10" + +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +trim-newlines@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-2.0.0.tgz#b403d0b91be50c331dfc4b82eeceb22c3de16d20" + +triple-beam@^1.2.0, triple-beam@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9" + +tsconfig-paths@^3.9.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b" + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.1" + minimist "^1.2.0" + strip-bom "^3.0.0" + +tslib@1.9.3: + version "1.9.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" + +tslib@^1.9.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + dependencies: + safe-buffer "^5.0.1" + +tv4@^1.3: + version "1.3.0" + resolved "https://registry.yarnpkg.com/tv4/-/tv4-1.3.0.tgz#d020c846fadd50c855abb25ebaecc68fc10f7963" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + dependencies: + prelude-ls "~1.1.2" + +type-detect@4.0.8, type-detect@^4.0.5: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + +type-detect@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.3.tgz#0e3f2670b44099b0b46c284d136a7ef49c74c2ea" + +type-fest@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.5.2.tgz#d6ef42a0356c6cd45f49485c3b6281fc148e48a2" + +type-fest@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + +type-is@~1.6.17, type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + dependencies: + is-typedarray "^1.0.0" + +uc.micro@^1.0.1, uc.micro@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.3.tgz#7ed50d5e0f9a9fb0a573379259f2a77458d50192" + +uglify-js@^2.6.1: + version "2.8.29" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" + dependencies: + source-map "~0.5.1" + yargs "~3.10.0" + optionalDependencies: + uglify-to-browserify "~1.0.0" + +uglify-js@^3.1.4: + version "3.9.3" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.9.3.tgz#4a285d1658b8a2ebaef9e51366b3a0f7acd79ec2" + dependencies: + commander "~2.20.3" + +uglify-to-browserify@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" + +ultron@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" + +undefsafe@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.2.tgz#225f6b9e0337663e0d8e7cfd686fc2836ccace76" + dependencies: + debug "^2.2.0" + +underscore.deep@~0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/underscore.deep/-/underscore.deep-0.5.1.tgz#072671f48d68735c34223fcfef63e69e5276cc2b" + +underscore@~1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.7.0.tgz#6bbaf0877500d36be34ecaa584e0db9fef035209" + +unique-string@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" + dependencies: + crypto-random-string "^2.0.0" + +universalify@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.1.tgz#fa71badd4437af4c148841e3b3b165f9e9e590b7" + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + +update-notifier@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.1.1.tgz#895fc8562bbe666179500f9f2cebac4f26323746" + dependencies: + boxen "^4.2.0" + chalk "^3.0.0" + configstore "^5.0.1" + has-yarn "^2.1.0" + import-lazy "^2.1.0" + is-ci "^2.0.0" + is-installed-globally "^0.3.1" + is-npm "^4.0.0" + is-yarn-global "^0.3.0" + latest-version "^5.0.0" + pupa "^2.0.1" + semver-diff "^3.1.1" + xdg-basedir "^4.0.0" + +uri-js@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + dependencies: + punycode "^2.1.0" + +url-parse-lax@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" + dependencies: + prepend-http "^2.0.0" + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + +uuid@^3.1.0, uuid@^3.2.1, uuid@^3.3.2, uuid@^3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866" + +v8-compile-cache@^2.0.3: + version "2.1.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" + +valid-data-url@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/valid-data-url/-/valid-data-url-2.0.0.tgz#2220fa9f8d4e761ebd3f3bb02770f1212b810537" + +validate-npm-package-license@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc" + dependencies: + spdx-correct "~1.0.0" + spdx-expression-parse "~1.0.0" + +vary@^1, vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +vizion@~2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/vizion/-/vizion-2.0.2.tgz#fcc263f41a4543b02b655c1b6c4ff1406726d2fa" + dependencies: + async "2.6.1" + git-node-fs "^1.0.0" + ini "^1.3.4" + js-git "^0.7.8" + lodash.findindex "^4.6.0" + lodash.foreach "^4.5.0" + lodash.get "^4.4.2" + lodash.last "^3.0.0" + +void-elements@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" + +web-resource-inliner@^4.3.3: + version "4.3.4" + resolved "https://registry.yarnpkg.com/web-resource-inliner/-/web-resource-inliner-4.3.4.tgz#07e1b4bcbcbee1021251b018e902bac5713f1be0" + dependencies: + async "^3.1.0" + chalk "^2.4.2" + datauri "^2.0.0" + htmlparser2 "^4.0.0" + lodash.unescape "^4.0.1" + request "^2.88.0" + safer-buffer "^2.1.2" + valid-data-url "^2.0.0" + xtend "^4.0.2" + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + +which@1.3.1, which@^1.2.9, which@^1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + dependencies: + isexe "^2.0.0" + +which@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.1.tgz#f1cf94d07a8e571b6ff006aeb91d0300c47ef0a4" + dependencies: + isexe "^2.0.0" + +wide-align@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + dependencies: + string-width "^1.0.2 || 2" + +widest-line@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" + dependencies: + string-width "^4.0.0" + +window-size@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" + +winston-transport@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.4.0.tgz#17af518daa690d5b2ecccaa7acf7b20ca7925e59" + dependencies: + readable-stream "^2.3.7" + triple-beam "^1.2.0" + +winston@^3.0.0, winston@^3.1.0: + version "3.3.3" + resolved "https://registry.yarnpkg.com/winston/-/winston-3.3.3.tgz#ae6172042cafb29786afa3d09c8ff833ab7c9170" + dependencies: + "@dabh/diagnostics" "^2.0.2" + async "^3.1.0" + is-stream "^2.0.0" + logform "^2.2.0" + one-time "^1.0.0" + readable-stream "^3.4.0" + stack-trace "0.0.x" + triple-beam "^1.3.0" + winston-transport "^4.4.0" + +with@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/with/-/with-5.1.1.tgz#fa4daa92daf32c4ea94ed453c81f04686b575dfe" + dependencies: + acorn "^3.1.0" + acorn-globals "^3.0.0" + +word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + +wordwrap@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" + +wordwrap@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + +write-file-atomic@^2.4.2: + version "2.4.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481" + dependencies: + graceful-fs "^4.1.11" + imurmurhash "^0.1.4" + signal-exit "^3.0.2" + +write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + dependencies: + mkdirp "^0.5.1" + +ws@^3.0.0: + version "3.3.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" + dependencies: + async-limiter "~1.0.0" + safe-buffer "~5.1.0" + ultron "~1.1.0" + +ws@^5.1.0: + version "5.2.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f" + dependencies: + async-limiter "~1.0.0" + +ws@^6.0.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" + dependencies: + async-limiter "~1.0.0" + +x-xss-protection@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/x-xss-protection/-/x-xss-protection-1.3.0.tgz#3e3a8dd638da80421b0e9fff11a2dbe168f6d52c" + +xdg-basedir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" + +xregexp@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943" + +xtend@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + +y18n@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + +yamljs@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/yamljs/-/yamljs-0.3.0.tgz#dc060bf267447b39f7304e9b2bfbe8b5a7ddb03b" + dependencies: + argparse "^1.0.7" + glob "^7.0.5" + +yargs-parser@13.1.1, yargs-parser@^13.0.0, yargs-parser@^13.1.1: + version "13.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0" + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^10.0.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8" + dependencies: + camelcase "^4.1.0" + +yargs-unparser@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.0.tgz#ef25c2c769ff6bd09e4b0f9d7c605fb27846ea9f" + dependencies: + flat "^4.1.0" + lodash "^4.17.15" + yargs "^13.3.0" + +yargs@13.3.0, yargs@^13.2.2, yargs@^13.3.0: + version "13.3.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83" + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.1" + +yargs@~3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" + dependencies: + camelcase "^1.0.2" + cliui "^2.1.0" + decamelize "^1.0.0" + window-size "0.1.0"