├── .DS_Store ├── .eslintrc ├── .gitignore ├── .prettierrc ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── bin ├── email-config.js ├── generators.js ├── index.js ├── new-project.js └── utils.js ├── lib ├── .DS_Store ├── email │ ├── js │ │ ├── mailgun.js │ │ └── sendgrid.js │ └── ts │ │ ├── mailgun.ts │ │ └── sendgrid.ts ├── mongoose │ ├── .DS_Store │ ├── js │ │ ├── .DS_Store │ │ └── express │ │ │ ├── .DS_Store │ │ │ ├── .babelrc │ │ │ ├── .env │ │ │ ├── .eslintrc │ │ │ ├── .gitignore │ │ │ ├── .prettierrc │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── src │ │ │ ├── config │ │ │ │ ├── database.js │ │ │ │ └── logger.js │ │ │ ├── controllers │ │ │ │ └── user.controller.js │ │ │ ├── index.js │ │ │ ├── middlewares │ │ │ │ ├── auth.middleware.js │ │ │ │ └── error.middleware.js │ │ │ ├── models │ │ │ │ └── user.model.js │ │ │ ├── routes │ │ │ │ ├── index.js │ │ │ │ └── user.route.js │ │ │ ├── services │ │ │ │ └── user.service.js │ │ │ ├── utils │ │ │ │ └── user.util.js │ │ │ └── validators │ │ │ │ └── user.validator.js │ │ │ └── tests │ │ │ ├── integration │ │ │ └── user.test.js │ │ │ └── unit │ │ │ └── user.test.js │ └── ts │ │ ├── .DS_Store │ │ └── express │ │ ├── .DS_Store │ │ ├── .env │ │ ├── .eslintignore │ │ ├── .eslintrc │ │ ├── .gitignore │ │ ├── .prettierrc │ │ ├── README.md │ │ ├── package.json │ │ ├── src │ │ ├── config │ │ │ ├── database.ts │ │ │ └── logger.ts │ │ ├── controllers │ │ │ └── user.controller.ts │ │ ├── index.ts │ │ ├── interfaces │ │ │ └── user.interface.ts │ │ ├── middlewares │ │ │ ├── auth.middleware.ts │ │ │ └── error.middleware.ts │ │ ├── models │ │ │ └── user.model.ts │ │ ├── routes │ │ │ ├── index.ts │ │ │ └── user.route.ts │ │ ├── services │ │ │ └── user.service.ts │ │ ├── utils │ │ │ └── user.util.ts │ │ └── validators │ │ │ └── user.validator.ts │ │ ├── tests │ │ ├── integration │ │ │ └── user.test.ts │ │ └── unit │ │ │ └── user.test.ts │ │ └── tsconfig.json └── sequelize │ ├── .DS_Store │ ├── js │ ├── .DS_Store │ └── express │ │ ├── .DS_Store │ │ ├── .babelrc │ │ ├── .env │ │ ├── .eslintrc │ │ ├── .gitignore │ │ ├── .prettierrc │ │ ├── .sequelizerc │ │ ├── README.md │ │ ├── package.json │ │ ├── src │ │ ├── .DS_Store │ │ ├── config │ │ │ ├── config.js │ │ │ ├── database.js │ │ │ └── logger.js │ │ ├── controllers │ │ │ └── user.controller.js │ │ ├── index.js │ │ ├── middlewares │ │ │ ├── auth.middleware.js │ │ │ └── error.middleware.js │ │ ├── models │ │ │ └── user.js │ │ ├── routes │ │ │ ├── index.js │ │ │ └── user.route.js │ │ ├── services │ │ │ └── user.service.js │ │ ├── utils │ │ │ └── user.util.js │ │ └── validators │ │ │ └── user.validator.js │ │ └── tests │ │ ├── integration │ │ └── user.test.js │ │ └── unit │ │ └── user.test.js │ └── ts │ ├── .DS_Store │ └── express │ ├── .DS_Store │ ├── .env │ ├── .eslintignore │ ├── .eslintrc │ ├── .gitignore │ ├── .prettierrc │ ├── .sequelizerc │ ├── README.md │ ├── package.json │ ├── src │ ├── .DS_Store │ ├── config │ │ ├── config.js │ │ ├── database.ts │ │ └── logger.ts │ ├── controllers │ │ └── user.controller.ts │ ├── index.ts │ ├── interfaces │ │ └── user.interface.ts │ ├── middlewares │ │ ├── auth.middleware.ts │ │ └── error.middleware.ts │ ├── models │ │ └── user.ts │ ├── routes │ │ ├── index.ts │ │ └── user.route.ts │ ├── services │ │ └── user.service.ts │ ├── utils │ │ └── user.util.ts │ └── validators │ │ └── user.validator.ts │ ├── tests │ ├── integration │ │ └── user.test.ts │ └── unit │ │ └── user.test.ts │ └── tsconfig.json ├── package-lock.json └── package.json /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tolustar/express-api-cli/d39a7ce7f3da09188023519a595e58bb2cadc51a/.DS_Store -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "babel-eslint", 4 | "extends": ["plugin:prettier/recommended"], 5 | "rules": { 6 | "no-console": 0, 7 | "no-unused-vars": "error", 8 | "quotes": [ 9 | "error", 10 | "single", 11 | { 12 | "allowTemplateLiterals": true 13 | } 14 | ], 15 | "semi-style": ["error", "last"], 16 | "max-len": [ 17 | "error", 18 | { 19 | "code": 100 20 | } 21 | ], 22 | "no-irregular-whitespace": "error", 23 | "no-trailing-spaces": "error", 24 | "no-multi-spaces": "error", 25 | "eqeqeq": ["error", "always"] 26 | }, 27 | "env": { 28 | "browser": true, 29 | "node": true 30 | } 31 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "tabWidth": 2, 4 | "singleQuote": true, 5 | "trailingComma": "none", 6 | "arrowParens": "always", 7 | "proseWrap": "always" 8 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing 2 | 3 | - Fork the project from the Github page - https://github.com/tolustar/express-api-cli 4 | - Clone the project into your local computer 5 | - Run _npm install_ to install the provided dependencies 6 | - Create a new branch and make your changes 7 | - Run _npm link_ 8 | - Git Push the changes 9 | - Raise a PR once you are done. 10 | 11 | ## Pull request template 12 | 13 | _What does this pull request do?_ 14 | 15 | - x 16 | - y 17 | - z 18 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright © 2020 Olaogun Toluwalase 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 6 | associated documentation files (the “Software”), to deal in the Software without restriction, 7 | including without limitation the rights to use, copy, modify, merge, publish, distribute, 8 | sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all copies or substantial 12 | portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 15 | NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 16 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES 17 | OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | **npm install -g express-api-cli** 4 | 5 | > speed up your application development with express-api-cli 6 | 7 | ## About 8 | 9 | Express-api-cli is a command line tool that generates an express project structure preconfigured 10 | with most commonly used packages that can speed up your development workflow. Rather than wasting 11 | time setting up your project structure, express-api-cli does the heavy lifting so that you can 12 | concentrate on building that awesome application 13 | 14 | ## Features 15 | 16 | - [x] Preconfigured database driver (Mongoose and Sequelize) 17 | - [x] Typescript support 18 | - [x] Generate Models, Controllers, Routes, Services and Test files directly from the command 19 | - [x] Easy exception handling 20 | - [x] Beautiful clear code structure 21 | - [x] Integrated Testing tool 22 | - [x] Eslint and Prettier Formatting 23 | - [x] Integrated Basic Security e.t.c 24 | 25 | ## Getting Started 26 | 27 | 1. Ensure [Node.js and NPM](https://nodejs.org/en/download/) is installed on your computer 28 | 2. Install the package globally **npm install -g express-api-cli** 29 | 3. Open your command line tool and type the following command to create a new project **exp-api 30 | create awesome-project** 31 | 32 | _Subsitute "awesome-project" with the name of your project._ 33 | 34 | 4. Select your preferred language (Javascript or Typescript) and Database Driver (Mongoose or 35 | Sequelize) 36 | 5. Once project is installed, configure your environment variables 37 | 38 | Voila!!! you are set to start creating an awesome application 🚀🚀🚀 39 | 40 | ## Commands 41 | 42 | | Commands | Description | Example | 43 | | ----------------------- | :-----------------------------------------------------------------------------------: | -----------------------------: | 44 | | create | Creates a new project | exp-api create awesome-project | 45 | | -m or --model | Create a new model in the model directory | exp-api -m post | 46 | | -c or --controller | Create a new controller in the controller directory | exp-api -c post | 47 | | -s or --service | Create a new service in the service directory | exp-api -s post | 48 | | -r or --route | Create a new route in the route directory | exp-api -r post | 49 | | -R or --resource | Create a new route, model, controller and service in their respective directories | exp-api -R post | 50 | | -u or --utility | Create a new utility file in the utils directory | exp-api -u random-string | 51 | | -M or --middleware | Create a new middleware in the middleware directory | exp-api -M auth | 52 | | -V or --validator | Create a new validator file in the validator directory | exp-api -v user-validator | 53 | | -v or --version | Get express-api-cli version | exp-api -v | 54 | | -i or --interface | Create a new interface file in the interface directory (Available only on Typescript) | exp-api -i post | 55 | | -U or --unittest | Create a new unit test file in the test directory | exp-api -U post | 56 | | -I or --integrationtest | Create a new integration test file in the test directory | exp-api -I post | 57 | | -T or --test | Create new unit and integration test | exp-api -U post | 58 | | -C or --config | Create a new config file in the config directory | exp-api -C mail | 59 | -------------------------------------------------------------------------------- /bin/email-config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs-extra'); 3 | const inquirer = require('inquirer'); 4 | const chalk = require('chalk'); 5 | 6 | const { spinner, checkLangAndDB } = require('./utils'); 7 | 8 | const setupEmailConfig = async (emailConfig, lang) => { 9 | let langAlias = lang === 'Javascript' ? 'js' : 'ts'; 10 | const emailType = getEmailConfigType(emailConfig); 11 | await fs.copy( 12 | path.resolve( 13 | __filename, 14 | // eslint-disable-next-line max-len 15 | `./../../lib/email/${langAlias}/${emailType.name}.${langAlias}` 16 | ), 17 | `./src/config/${emailType.name}.${langAlias}` 18 | ); 19 | appendEnvironmentVariable(emailType.env); 20 | }; 21 | 22 | const checkIfProjectHasBeenCreated = () => { 23 | try { 24 | return fs.statSync('package.json').isFile(); 25 | } catch (err) { 26 | return false; 27 | } 28 | }; 29 | 30 | const appendEnvironmentVariable = (env) => { 31 | fs.appendFile('.env', env, function (err) { 32 | if (err) { 33 | console.log(err); 34 | return false; 35 | } 36 | return true; 37 | }); 38 | }; 39 | 40 | const getEmailConfigType = (type) => { 41 | const emailEnv = { 42 | Sendgrid: { 43 | name: 'sendgrid', 44 | env: '\nSENDGRID_KEY= ' + '\nSENDGRID_FROM=' 45 | }, 46 | Mailgun: { 47 | name: 'mailgun', 48 | env: '\nMAILGUN_DOMAIN= ' + '\nMAILGUN_API_KEY=' 49 | }, 50 | default: { 51 | name: 'sendgrid', 52 | env: '\nENDGRID_KEY= ' + '\nSENDGRID_FROM=' 53 | } 54 | }; 55 | return emailEnv[type] || emailEnv['default']; 56 | }; 57 | 58 | const newEmailConfig = async () => { 59 | try { 60 | if (!checkIfProjectHasBeenCreated()) { 61 | console.log( 62 | chalk.cyan(`You need to create at least one project before setting up Email Config`) 63 | ); 64 | return 'false'; 65 | } 66 | const template = await inquirer.prompt([ 67 | { 68 | type: 'list', 69 | name: 'emailConfig', 70 | message: 'Select an Email Configuration', 71 | choices: ['Sendgrid', 'Mailgun'] 72 | } 73 | ]); 74 | 75 | const { lang } = await checkLangAndDB(); 76 | const projectLanguage = lang === 'js' ? 'Javascript' : 'Typescript'; 77 | 78 | await setupEmailConfig(template.emailConfig, projectLanguage); 79 | console.log(chalk.green(`Email config for ${template.emailConfig} created successfully`)); 80 | } catch (error) { 81 | spinner.fail(`${chalk.cyan(`Error creating template`)} - ${chalk.red(error)}`); 82 | } 83 | }; 84 | 85 | module.exports = newEmailConfig; 86 | -------------------------------------------------------------------------------- /bin/generators.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-len */ 2 | const fs = require('fs-extra'); 3 | const chalk = require('chalk'); 4 | 5 | const { 6 | addRouteToRouteIndex, 7 | addImportToRouteIndex, 8 | generateFile, 9 | checkLangAndDB 10 | } = require('./utils'); 11 | 12 | const generateModel = async (model, config) => { 13 | if (!model) return; 14 | try { 15 | await generateFile('model', model, config.lang, config.dbDriver); 16 | 17 | console.log(chalk.green(`${model}.model.${config.lang} generated successfully`)); 18 | } catch (error) { 19 | console.log(error); 20 | } 21 | }; 22 | 23 | const generateController = async (controller, config) => { 24 | try { 25 | if (!controller) return; 26 | await generateFile('controller', controller, config.lang, config.dbDriver); 27 | 28 | console.log(chalk.green(`${controller}.controller.${config.lang} generated successfully`)); 29 | } catch (error) { 30 | console.log(error); 31 | } 32 | }; 33 | 34 | const generateService = async (service, config) => { 35 | try { 36 | if (!service) return; 37 | 38 | await generateFile('service', service, config.lang, config.dbDriver); 39 | console.log(chalk.green(`${service}.service.${config.lang} generated successfully`)); 40 | } catch (error) { 41 | console.log(error); 42 | } 43 | }; 44 | 45 | const generateRoute = async (route, config) => { 46 | try { 47 | if (!route) return; 48 | await generateFile('route', route, config.lang, config.dbDriver); 49 | 50 | console.log(chalk.green(`${route}.route.${config.lang} generated successfully`)); 51 | addImportToRouteIndex(route, config.lang); 52 | addRouteToRouteIndex(route, config.lang); 53 | } catch (error) { 54 | console.log(error); 55 | } 56 | }; 57 | 58 | const generateResource = (resource, config) => { 59 | if (!resource) return; 60 | generateModel(resource, config); 61 | generateController(resource, config); 62 | generateService(resource, config); 63 | generateRoute(resource, config); 64 | }; 65 | 66 | const generateValidator = async (validator, config) => { 67 | try { 68 | if (!validator) return; 69 | await fs.writeFile(`./src/validators/${validator}.validator.${config.lang}`, ''); 70 | console.log(chalk.green(`${validator}.validator.${config.lang} generated successfully`)); 71 | } catch (error) { 72 | console.log(error); 73 | } 74 | }; 75 | 76 | const generateInterface = async (interface_, config) => { 77 | try { 78 | if (config.lang === 'js') return; 79 | if (!interface_) return; 80 | await fs.writeFile(`./src/interfaces/${interface_}.interface.${config.lang}`, ''); 81 | console.log(chalk.green(`${interface_}.interface.${config.lang} generated successfully`)); 82 | } catch (error) { 83 | console.log(error); 84 | } 85 | }; 86 | 87 | const generateUtil = async (util, config) => { 88 | try { 89 | if (!util) return; 90 | await fs.writeFile(`./src/utils/${util}.util.${config.lang}`, ''); 91 | console.log(chalk.green(`${util}.util.${config.lang} generated successfully`)); 92 | } catch (error) { 93 | console.log(error); 94 | } 95 | }; 96 | 97 | const generateConfig = async (configFile, config) => { 98 | try { 99 | if (!configFile) return; 100 | await fs.writeFile(`./src/config/${configFile}.${config.lang}`, ''); 101 | console.log(chalk.green(`${configFile}.${config.lang} generated successfully`)); 102 | } catch (error) { 103 | console.log(error); 104 | } 105 | }; 106 | 107 | const generateMiddleware = async (middleware, config) => { 108 | try { 109 | if (!middleware) return; 110 | await fs.writeFile(`./src/middlewares/${middleware}.middleware.${config.lang}`, ''); 111 | console.log(chalk.green(`${middleware}.middleware.${config.lang} generated successfully`)); 112 | } catch (error) { 113 | console.log(error); 114 | } 115 | }; 116 | 117 | const generateUnitTest = async (unittest, config) => { 118 | try { 119 | if (!unittest) return; 120 | // await fs.writeFile(`./src/tests/unit/${unittest}.test.js`, unitTestTemplate(unittest)); 121 | await generateFile('tests/unit', unittest, config.lang, config.dbDriver); 122 | 123 | console.log(chalk.green(`unit/ ${unittest}.test.${config.lang} generated successfully`)); 124 | } catch (error) { 125 | console.log(error); 126 | } 127 | }; 128 | 129 | const generateIntegrationTest = async (integrationtest, config) => { 130 | try { 131 | if (!integrationtest) return; 132 | 133 | await generateFile('tests/integration', integrationtest, config.lang, config.dbDriver); 134 | 135 | console.log( 136 | chalk.green(`integration/ ${integrationtest}.test.${config.lang} generated successfully`) 137 | ); 138 | } catch (error) { 139 | console.log(error); 140 | } 141 | }; 142 | 143 | const getVersion = async (options) => { 144 | try { 145 | if (Object.keys(options)[2] !== 'version') return; 146 | const getVersion = require('./../package.json').version; 147 | console.log(chalk.green(`v${getVersion}`)); 148 | } catch (error) { 149 | console.log(error); 150 | } 151 | }; 152 | 153 | const generateTest = async (test, config) => { 154 | generateUnitTest(test, config); 155 | generateIntegrationTest(test, config); 156 | }; 157 | 158 | module.exports = async (options) => { 159 | try { 160 | let config = await checkLangAndDB(); 161 | generateModel(options.model, config); 162 | generateController(options.controller, config); 163 | generateService(options.service, config); 164 | generateRoute(options.route, config); 165 | generateResource(options.resource, config); 166 | generateValidator(options.validator, config); 167 | generateInterface(options.interface, config); 168 | generateUtil(options.util, config); 169 | generateConfig(options.config, config); 170 | generateMiddleware(options.middleware, config); 171 | generateUnitTest(options.unittest, config); 172 | generateIntegrationTest(options.integrationtest, config); 173 | generateTest(options.test, config); 174 | getVersion(options); 175 | } catch (error) {} 176 | }; 177 | -------------------------------------------------------------------------------- /bin/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const yargs = require('yargs'); 4 | const chalk = require('chalk'); 5 | 6 | const newProject = require('./new-project'); 7 | const newEmailConfig = require('./email-config'); 8 | 9 | const run = require('./generators'); 10 | 11 | const result = yargs 12 | .version() 13 | .command('create', 'Create a new Express API Project', (value) => { 14 | const args = value.argv._; 15 | if (args[1]) { 16 | newProject(args[1]); 17 | } else { 18 | console.log( 19 | chalk.red(` 20 | Please provide a project name. 21 | 22 | Example: 23 | exp-api create AwesomeProject 24 | `) 25 | ); 26 | } 27 | }) 28 | .command('config', 'Setup a new config', (value) => { 29 | const args = value.argv._; 30 | if (args[1] === 'email') { 31 | newEmailConfig(); 32 | } else { 33 | console.log( 34 | chalk.red(` 35 | Please provide a configuration type eg 'email'. 36 | 37 | Example: 38 | exp-api config email 39 | `) 40 | ); 41 | } 42 | }) 43 | .usage( 44 | ` 45 | Usage: Speed up your application development with express-api-cli 46 | ` 47 | ) 48 | .option('v', { 49 | alias: 'version', 50 | describe: 'Get express-api-cli version', 51 | type: 'string' 52 | }) 53 | .option('m', { 54 | alias: 'model', 55 | describe: 'Create new model', 56 | type: 'string' 57 | }) 58 | .option('c', { 59 | alias: 'controller', 60 | describe: 'Create new controller', 61 | type: 'string' 62 | }) 63 | .option('s', { 64 | alias: 'service', 65 | describe: 'Create new service', 66 | type: 'string' 67 | }) 68 | .option('r', { 69 | alias: 'route', 70 | describe: 'Create new route', 71 | type: 'string' 72 | }) 73 | .option('R', { 74 | alias: 'resource', 75 | describe: 'Create new resource', 76 | type: 'string' 77 | }) 78 | .option('u', { 79 | alias: 'util', 80 | describe: 'Create new utility file', 81 | type: 'string' 82 | }) 83 | .option('M', { 84 | alias: 'middleware', 85 | describe: 'Create new middleware', 86 | type: 'string' 87 | }) 88 | .option('V', { 89 | alias: 'validator', 90 | describe: 'Create new validator', 91 | type: 'string' 92 | }) 93 | .option('i', { 94 | alias: 'interface', 95 | describe: 'Create new interface', 96 | type: 'string' 97 | }) 98 | .option('T', { 99 | alias: 'test', 100 | describe: 'Create new unit and integration test', 101 | type: 'string' 102 | }) 103 | .option('U', { 104 | alias: 'unittest', 105 | describe: 'Create new unit test', 106 | type: 'string' 107 | }) 108 | .option('I', { 109 | alias: 'integrationtest', 110 | describe: 'Create new integration test', 111 | type: 'string' 112 | }) 113 | .option('C', { 114 | alias: 'config', 115 | describe: 'Create new config file', 116 | type: 'string' 117 | }) 118 | .help().argv; 119 | 120 | run(result); 121 | -------------------------------------------------------------------------------- /bin/new-project.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs-extra'); 3 | const inquirer = require('inquirer'); 4 | const chalk = require('chalk'); 5 | const ora = require('ora'); 6 | 7 | const creatingProjectSpinner = ora({ 8 | spinner: 'star2' 9 | }); 10 | const npmInstallSpinner = ora({ 11 | spinner: 'star2' 12 | }); 13 | 14 | const npmInstall = (projectName) => { 15 | npmInstallSpinner.start(chalk.cyan(`Installing required packages for ${projectName}`)); 16 | 17 | const { spawn } = require('child_process'); 18 | 19 | const installationProcess = spawn(`cd ${projectName} && npm install`, { 20 | shell: true 21 | }); 22 | 23 | installationProcess.on('error', () => { 24 | npmInstallSpinner.fail( 25 | chalk.red( 26 | ` 27 | An error occured, please try again. 28 | If problem persist please raise an issue on Github` 29 | ) 30 | ); 31 | }); 32 | 33 | installationProcess.on('close', () => { 34 | npmInstallSpinner.succeed(chalk.green(`Packages installed successfully`)); 35 | 36 | console.log( 37 | chalk.green(` 38 | 🥳🥳🥳🥳🥳 39 | Voila!!! ${projectName} is ready for development. 40 | 41 | Create something Awesome 42 | 🚀🚀🚀🚀🚀 43 | 44 | For How to use and more info on express-api-cli 45 | Visit https://github.com/tolustar/express-api-cli/ 46 | Cheers!!! 47 | 48 | `) 49 | ); 50 | }); 51 | }; 52 | 53 | const projectToInstall = async (selectLang, selectDbDriver, projectName) => { 54 | await installProject(selectLang, selectDbDriver, projectName); 55 | }; 56 | 57 | const installProject = async (selectLang, selectDbDriver, projectName) => { 58 | let langAlias = selectLang === 'Javascript' ? 'js' : 'ts'; 59 | let driverAlias = selectDbDriver === 'Mongoose' ? 'mongoose' : 'sequelize'; 60 | 61 | await fs.copy( 62 | path.resolve(__dirname, `./../lib/${driverAlias}/${langAlias}/express`), 63 | `./${projectName}` 64 | ); 65 | }; 66 | 67 | const newProject = async (projectName) => { 68 | try { 69 | const template = await inquirer.prompt([ 70 | { 71 | type: 'list', 72 | name: 'selectLang', 73 | message: 'Select a development language', 74 | choices: ['Javascript', 'Typescript'] 75 | }, 76 | { 77 | type: 'list', 78 | name: 'selectDbDriver', 79 | message: 'Select a database driver', 80 | choices: ['Mongoose', 'Sequelize'] 81 | } 82 | ]); 83 | 84 | creatingProjectSpinner.start(chalk.cyan(`Creating ${projectName}`)); 85 | await projectToInstall(template.selectLang, template.selectDbDriver, projectName); 86 | 87 | creatingProjectSpinner.succeed(chalk.green(`${projectName} created successfully`)); 88 | 89 | npmInstall(projectName); 90 | } catch (error) { 91 | creatingProjectSpinner.fail( 92 | `${chalk.cyan(`Error creating ${projectName}`)} - ${chalk.red(error)}` 93 | ); 94 | } 95 | }; 96 | 97 | module.exports = newProject; 98 | -------------------------------------------------------------------------------- /bin/utils.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-len */ 2 | const fs = require('fs-extra'); 3 | const path = require('path'); 4 | const ora = require('ora'); 5 | 6 | exports.addRouteToRouteIndex = (route, lang) => { 7 | const data = fs.readFileSync(`./src/routes/index.${lang}`).toString().split('\n'); 8 | 9 | let processComplete = false; 10 | data.forEach((item, index) => { 11 | if (item.includes('return router') && processComplete === false) { 12 | let newRoute = ` router.use('/${route}', ${route}Route);`; 13 | if (lang === 'ts') { 14 | newRoute = ` router.use('/${route}', new ${route}Route().getRoutes());`; 15 | } 16 | 17 | data.splice(index, 0, newRoute); 18 | const addNewRoute = data.join('\n'); 19 | 20 | fs.writeFileSync(`./src/routes/index.${lang}`, addNewRoute); 21 | 22 | processComplete = true; 23 | } 24 | }); 25 | }; 26 | 27 | exports.addImportToRouteIndex = (route, lang) => { 28 | const data = fs.readFileSync(`./src/routes/index.${lang}`).toString().split('\n'); 29 | 30 | let processComplete = false; 31 | data.forEach((item, index) => { 32 | if (item.includes('import') && processComplete === false && index !== 0) { 33 | const newImport = `import ${route}Route from './${route}.route';`; 34 | 35 | data.splice(index, 0, newImport); 36 | const addNewImport = data.join('\n'); 37 | 38 | fs.writeFileSync(`./src/routes/index.${lang}`, addNewImport); 39 | 40 | processComplete = true; 41 | } 42 | }); 43 | }; 44 | 45 | exports.generateFile = async (dir, fileName, lang, dbDriver) => { 46 | const FileName = fileName.charAt(0).toUpperCase() + fileName.slice(1); 47 | 48 | let dirExt = `${dir}.${lang}`; 49 | 50 | //conditonal case for sequelize when generating model 51 | if (dbDriver === 'sequelize' && dir === 'model') { 52 | dirExt = `${lang}`; 53 | } 54 | 55 | let srcCopy; 56 | let destinationCopy; 57 | //conditonal case for test folder 58 | let dirs = dir + 's'; 59 | if (dir.includes('test')) { 60 | dirs = dir; 61 | dirExt = `test.${lang}`; 62 | 63 | srcCopy = `./../lib/${dbDriver}/${lang}/express/${dirs}/user.${dirExt}`; 64 | destinationCopy = `./${dirs}/${fileName}.${dirExt}`; 65 | } else { 66 | srcCopy = `./../lib/${dbDriver}/${lang}/express/src/${dirs}/user.${dirExt}`; 67 | destinationCopy = `./src/${dirs}/${fileName}.${dirExt}`; 68 | } 69 | 70 | await fs.copy(path.resolve(__dirname, srcCopy), destinationCopy); 71 | 72 | const data = fs.readFileSync(destinationCopy).toString(); 73 | 74 | let newData = data.replace(/user/g, fileName); 75 | newData = newData.replace(/User/g, FileName); 76 | 77 | fs.writeFileSync(destinationCopy, newData); 78 | }; 79 | 80 | exports.checkLangAndDB = async () => { 81 | let config = { 82 | lang: 'js', 83 | dbDriver: 'mongoose' 84 | }; 85 | 86 | let files = fs.readdirSync('./src/'); 87 | const file = files.find((item) => item.includes('.ts')); 88 | if (file) { 89 | config.lang = 'ts'; 90 | } 91 | 92 | let db = null; 93 | try { 94 | if (config.lang === 'js') { 95 | db = await fs.readFile('./src/config/database.js'); 96 | } else { 97 | db = await fs.readFile('./src/config/database.ts'); 98 | } 99 | db = db.toString(); 100 | } catch (error) { 101 | console.log( 102 | chalk.yellow(` 103 | Database config not detected in src/config. 104 | express-api-cli shall assume project default database config uses mongoose. Thank you. 105 | `) 106 | ); 107 | } 108 | 109 | if (db && db.includes('mongoose') && db.includes('sequelize')) { 110 | console.log( 111 | chalk.yellow(` 112 | Application contains more than one DB configuration in src/config/database.js. 113 | Please use one db configuration or remove unused imports to allow express-api-cli function properly. 114 | 115 | In the meantime Express-api-cli shall use mongoose database configuration 116 | Thank you. 117 | `) 118 | ); 119 | } else { 120 | if (db && db.includes('sequelize')) { 121 | config.dbDriver = 'sequelize'; 122 | } 123 | } 124 | 125 | return config; 126 | }; 127 | 128 | exports.spinner = ora({ 129 | spinner: 'star2' 130 | }); 131 | -------------------------------------------------------------------------------- /lib/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tolustar/express-api-cli/d39a7ce7f3da09188023519a595e58bb2cadc51a/lib/.DS_Store -------------------------------------------------------------------------------- /lib/email/js/mailgun.js: -------------------------------------------------------------------------------- 1 | import HttpStatus from 'http-status-codes'; 2 | import dotenv from 'dotenv'; 3 | dotenv.config(); 4 | const api_key = process.env.MAILGUN_API_KEY; 5 | const domain = process.env.MAILGUN_DOMAIN; 6 | const mailgun = require('mailgun-js')({ apiKey: api_key, domain: domain }); 7 | 8 | const validateMailBody = (body) => { 9 | const { from, to, subject, text } = body; 10 | if (!from || !to || !subject || !text) return 'Please supply email body params'; 11 | }; 12 | 13 | const sendMail = (data) => { 14 | validateMailBody(data); 15 | mailgun.messages().send(data, function (error, body) { 16 | if (error) { 17 | throw { 18 | message: error, 19 | data: null, 20 | code: HttpStatus.INTERNAL_SERVER_ERROR 21 | }; 22 | } 23 | return body; 24 | }); 25 | }; 26 | 27 | module.exports = sendMail; 28 | -------------------------------------------------------------------------------- /lib/email/js/sendgrid.js: -------------------------------------------------------------------------------- 1 | import sgMail from '@sendgrid/mail'; 2 | import dotenv from 'dotenv'; 3 | import HttpStatus from 'http-status-codes'; 4 | dotenv.config(); 5 | 6 | sgMail.setApiKey(process.env.SENDGRID_KEY); 7 | 8 | const sendMail = async (body) => { 9 | const { email, subject, template } = body; 10 | 11 | if (!template || !email || !subject) { 12 | throw { 13 | message: 'Please supply email,subject and template', 14 | data: null, 15 | code: HttpStatus.INTERNAL_SERVER_ERROR 16 | }; 17 | } 18 | 19 | await sgMail.send(body); 20 | return 'Mail Sent Successfully'; 21 | }; 22 | 23 | module.exports = sendMail; 24 | -------------------------------------------------------------------------------- /lib/email/ts/mailgun.ts: -------------------------------------------------------------------------------- 1 | import * as HttpStatus from 'http-status-codes'; 2 | import * as dotenv from 'dotenv'; 3 | import * as mailgun from 'mailgun-js'; 4 | 5 | class MailgunService { 6 | private mailgun: any; 7 | 8 | constructor() { 9 | dotenv.config(); 10 | const api_key = process.env.MAILGUN_API_KEY; 11 | const domain = process.env.MAILGUN_DOMAIN; 12 | this.mailgun = mailgun({ apiKey: api_key, domain: domain }); 13 | } 14 | 15 | private validateMailBody = (body): string => { 16 | const { from, to, subject, text } = body; 17 | if (!from || !to || !subject || !text) return 'Please supply email body params'; 18 | }; 19 | 20 | public sendMail = async (body): Promise => { 21 | this.validateMailBody(body); 22 | this.mailgun.messages().send(body, function (error, result) { 23 | if (error) { 24 | throw { 25 | message: error, 26 | data: null, 27 | code: HttpStatus.INTERNAL_SERVER_ERROR 28 | }; 29 | } 30 | return result; 31 | }); 32 | }; 33 | } 34 | export default MailgunService; 35 | -------------------------------------------------------------------------------- /lib/email/ts/sendgrid.ts: -------------------------------------------------------------------------------- 1 | import sgMail from '@sendgrid/mail'; 2 | import dotenv from 'dotenv'; 3 | import HttpStatus from 'http-status-codes'; 4 | dotenv.config(); 5 | 6 | class MailService { 7 | constructor() { 8 | dotenv.config(); 9 | sgMail.setApiKey(process.env.SENDGRID_KEY); 10 | } 11 | 12 | public sendMail = async (body): Promise => { 13 | const { email, subject, template } = body; 14 | if (!template || !email || !subject) { 15 | throw { 16 | message: 'Please supply email,subject and template', 17 | data: null, 18 | code: HttpStatus.INTERNAL_SERVER_ERROR 19 | }; 20 | } 21 | 22 | await sgMail.send(body); 23 | return 'Mail Sent Successfully'; 24 | }; 25 | } 26 | export default MailService; 27 | -------------------------------------------------------------------------------- /lib/mongoose/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tolustar/express-api-cli/d39a7ce7f3da09188023519a595e58bb2cadc51a/lib/mongoose/.DS_Store -------------------------------------------------------------------------------- /lib/mongoose/js/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tolustar/express-api-cli/d39a7ce7f3da09188023519a595e58bb2cadc51a/lib/mongoose/js/.DS_Store -------------------------------------------------------------------------------- /lib/mongoose/js/express/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tolustar/express-api-cli/d39a7ce7f3da09188023519a595e58bb2cadc51a/lib/mongoose/js/express/.DS_Store -------------------------------------------------------------------------------- /lib/mongoose/js/express/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"], 3 | "plugins": [ 4 | ["@babel/plugin-transform-runtime", 5 | { 6 | "regenerator": true 7 | } 8 | ] 9 | ] 10 | } -------------------------------------------------------------------------------- /lib/mongoose/js/express/.env: -------------------------------------------------------------------------------- 1 | APP_HOST = http://localhost 2 | APP_PORT = 3000 3 | 4 | API_VERSION = v1 5 | 6 | DATABASE = mongodb+srv://test:test@demo.aoloa.mongodb.net/express?retryWrites=true&w=majority 7 | DATABASE_TEST = mongodb+srv://test:test@demo.aoloa.mongodb.net/test?retryWrites=true&w=majority 8 | -------------------------------------------------------------------------------- /lib/mongoose/js/express/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "babel-eslint", 4 | "extends": ["plugin:prettier/recommended"], 5 | "rules": { 6 | "no-console": 0, 7 | "no-unused-vars": "error", 8 | "quotes": [ 9 | "error", 10 | "single", 11 | { 12 | "allowTemplateLiterals": true 13 | } 14 | ], 15 | "semi-style": ["error", "last"], 16 | "max-len": [ 17 | "error", 18 | { 19 | "code": 80 20 | } 21 | ], 22 | "no-irregular-whitespace": "error", 23 | "no-trailing-spaces": "error", 24 | "no-multi-spaces": "error", 25 | "eqeqeq": ["error", "always"] 26 | }, 27 | "env": { 28 | "browser": true, 29 | "node": true 30 | } 31 | } -------------------------------------------------------------------------------- /lib/mongoose/js/express/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | logs -------------------------------------------------------------------------------- /lib/mongoose/js/express/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 2, 4 | "singleQuote": true, 5 | "trailingComma": "none", 6 | "arrowParens": "always", 7 | "proseWrap": "always" 8 | } 9 | -------------------------------------------------------------------------------- /lib/mongoose/js/express/README.md: -------------------------------------------------------------------------------- 1 | **Express API** 2 | -------------------------------------------------------------------------------- /lib/mongoose/js/express/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "build/index.js", 6 | "scripts": { 7 | "dev": "nodemon --exec babel-node src/index.js", 8 | "build": "rimraf build && babel src --out-dir build", 9 | "prestart": "rimraf build && babel src --out-dir build", 10 | "start": "node build/index.js", 11 | "test": "cross-env NODE_ENV=test mocha --exit --recursive --require @babel/register --timeout=5000 tests/**/*.js", 12 | "test:coverage": "cross-env NODE_ENV=test nyc --require @babel/register --reporter lcov --reporter text mocha --exit --recursive --timeout=5000 tests/**/*.js", 13 | "lint": "eslint . --ext .js", 14 | "lint-fix": "eslint . --ext .js --fix" 15 | }, 16 | "author": "Tolustar", 17 | "license": "MIT", 18 | "dependencies": { 19 | "@hapi/joi": "^17.1.1", 20 | "cors": "^2.8.5", 21 | "dotenv": "^8.2.0", 22 | "express": "^4.17.1", 23 | "helmet": "^4.1.0", 24 | "http-status-codes": "^2.1.2", 25 | "jsonwebtoken": "^8.5.1", 26 | "mongoose": "^5.10.2", 27 | "@sendgrid/mail": "^7.2.5", 28 | "mailgun-js": "^0.22.0", 29 | "morgan": "^1.10.0", 30 | "winston": "^3.3.3", 31 | "winston-daily-rotate-file": "^4.5.0" 32 | }, 33 | "devDependencies": { 34 | "@babel/cli": "^7.10.5", 35 | "@babel/core": "^7.11.4", 36 | "@babel/node": "^7.10.5", 37 | "@babel/plugin-transform-runtime": "^7.11.5", 38 | "@babel/preset-env": "^7.11.0", 39 | "@babel/register": "^7.11.5", 40 | "@babel/runtime": "^7.11.2", 41 | "babel-eslint": "^10.1.0", 42 | "chai": "^4.2.0", 43 | "cross-env": "^7.0.2", 44 | "eslint": "^7.7.0", 45 | "eslint-config-prettier": "^6.11.0", 46 | "eslint-plugin-prettier": "^3.1.4", 47 | "mocha": "^8.1.3", 48 | "nodemon": "^2.0.4", 49 | "nyc": "^15.1.0", 50 | "prettier": "^2.1.1", 51 | "rimraf": "^3.0.2", 52 | "supertest": "^4.0.2" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/mongoose/js/express/src/config/database.js: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose'; 2 | import logger from './logger'; 3 | 4 | const database = async () => { 5 | try { 6 | // Replace database value in the .env file with your database config url 7 | const DATABASE = 8 | process.env.NODE_ENV === 'test' 9 | ? process.env.DATABASE_TEST 10 | : process.env.DATABASE; 11 | 12 | await mongoose.connect(DATABASE, { 13 | useFindAndModify: false, 14 | useCreateIndex: true, 15 | useNewUrlParser: true, 16 | useUnifiedTopology: true 17 | }); 18 | logger.info('Connected to the database.'); 19 | } catch (error) { 20 | logger.error('Could not connect to the database.', error); 21 | } 22 | }; 23 | export default database; 24 | -------------------------------------------------------------------------------- /lib/mongoose/js/express/src/config/logger.js: -------------------------------------------------------------------------------- 1 | import winston, { format } from 'winston'; 2 | import 'winston-daily-rotate-file'; 3 | 4 | /** 5 | * Logger handles all logs in the application 6 | */ 7 | const logger = winston.createLogger({ 8 | format: format.combine(format.timestamp(), format.simple()), 9 | colorize: true, 10 | transports: [ 11 | new winston.transports.File({ 12 | filename: 'logs/server/error.log', 13 | level: 'error', 14 | handleExceptions: true 15 | }), 16 | new winston.transports.File({ 17 | filename: 'logs/server/all.log', 18 | level: 'info', 19 | handleExceptions: true 20 | }), 21 | new winston.transports.DailyRotateFile({ 22 | maxFiles: '14d', 23 | level: 'info', 24 | dirname: 'logs/server/daily', 25 | datePattern: 'YYYY-MM-DD', 26 | filename: '%DATE%.log' 27 | }), 28 | new winston.transports.Console({ 29 | level: 'debug', 30 | json: false, 31 | handleExceptions: true 32 | }) 33 | ] 34 | }); 35 | 36 | /** 37 | * morganLogger logs all http request in a dedicated file and on console 38 | */ 39 | const morganLogger = winston.createLogger({ 40 | format: format.combine(format.simple()), 41 | transports: [ 42 | new winston.transports.File({ 43 | filename: 'logs/requests/all.log', 44 | level: 'debug', 45 | handleExceptions: true 46 | }), 47 | new winston.transports.Console({ 48 | level: 'debug', 49 | json: false, 50 | handleExceptions: true 51 | }), 52 | new winston.transports.DailyRotateFile({ 53 | maxFiles: '14d', 54 | level: 'info', 55 | dirname: 'logs/requests/daily', 56 | datePattern: 'YYYY-MM-DD', 57 | filename: '%DATE%.log' 58 | }) 59 | ] 60 | }); 61 | 62 | export const logStream = { 63 | /** 64 | * A writable stream for winston logger. 65 | * 66 | * @param {any} message 67 | */ 68 | write(message) { 69 | morganLogger.info(message.toString()); 70 | } 71 | }; 72 | 73 | export default logger; -------------------------------------------------------------------------------- /lib/mongoose/js/express/src/controllers/user.controller.js: -------------------------------------------------------------------------------- 1 | import HttpStatus from 'http-status-codes'; 2 | import * as UserService from '../services/user.service'; 3 | 4 | /** 5 | * Controller to get all users available 6 | * @param {object} req - request object 7 | * @param {object} res - response object 8 | * @param {Function} next 9 | */ 10 | export const getAllUsers = async (req, res, next) => { 11 | try { 12 | const data = await UserService.getAllUsers(); 13 | res.status(HttpStatus.OK).json({ 14 | code: HttpStatus.OK, 15 | data: data, 16 | message: 'All users fetched successfully' 17 | }); 18 | } catch (error) { 19 | next(error); 20 | } 21 | }; 22 | 23 | /** 24 | * Controller to get a single user 25 | * @param {object} req - request object 26 | * @param {object} res - response object 27 | * @param {Function} next 28 | */ 29 | export const getUser = async (req, res, next) => { 30 | try { 31 | const data = await UserService.getUser(req.params._id); 32 | res.status(HttpStatus.OK).json({ 33 | code: HttpStatus.OK, 34 | data: data, 35 | message: 'User fetched successfully' 36 | }); 37 | } catch (error) { 38 | next(error); 39 | } 40 | }; 41 | 42 | /** 43 | * Controller to create a new user 44 | * @param {object} req - request object 45 | * @param {object} res - response object 46 | * @param {Function} next 47 | */ 48 | export const newUser = async (req, res, next) => { 49 | try { 50 | const data = await UserService.newUser(req.body); 51 | res.status(HttpStatus.CREATED).json({ 52 | code: HttpStatus.CREATED, 53 | data: data, 54 | message: 'User created successfully' 55 | }); 56 | } catch (error) { 57 | next(error); 58 | } 59 | }; 60 | 61 | /** 62 | * Controller to update a user 63 | * @param {object} req - request object 64 | * @param {object} res - response object 65 | * @param {Function} next 66 | */ 67 | export const updateUser = async (req, res, next) => { 68 | try { 69 | const data = await UserService.updateUser(req.params._id, req.body); 70 | res.status(HttpStatus.ACCEPTED).json({ 71 | code: HttpStatus.ACCEPTED, 72 | data: data, 73 | message: 'User updated successfully' 74 | }); 75 | } catch (error) { 76 | next(error); 77 | } 78 | }; 79 | 80 | /** 81 | * Controller to delete a user 82 | * @param {object} req - request object 83 | * @param {object} res - response object 84 | * @param {Function} next 85 | */ 86 | export const deleteUser = async (req, res, next) => { 87 | try { 88 | await UserService.deleteUser(req.params._id); 89 | res.status(HttpStatus.OK).json({ 90 | code: HttpStatus.OK, 91 | data: [], 92 | message: 'User deleted successfully' 93 | }); 94 | } catch (error) { 95 | next(error); 96 | } 97 | }; 98 | -------------------------------------------------------------------------------- /lib/mongoose/js/express/src/index.js: -------------------------------------------------------------------------------- 1 | import dotenv from 'dotenv'; 2 | dotenv.config(); 3 | 4 | import express from 'express'; 5 | import cors from 'cors'; 6 | import helmet from 'helmet'; 7 | 8 | import routes from './routes'; 9 | import database from './config/database'; 10 | import { 11 | appErrorHandler, 12 | genericErrorHandler, 13 | notFound 14 | } from './middlewares/error.middleware'; 15 | import logger, { logStream } from './config/logger'; 16 | 17 | import morgan from 'morgan'; 18 | 19 | const app = express(); 20 | const host = process.env.APP_HOST; 21 | const port = process.env.APP_PORT; 22 | const api_version = process.env.API_VERSION; 23 | 24 | app.use(cors()); 25 | app.use(helmet()); 26 | app.use(express.urlencoded({ extended: true })); 27 | app.use(express.json()); 28 | app.use(morgan('combined', { stream: logStream })); 29 | 30 | database(); 31 | 32 | app.use(`/api/${api_version}`, routes()); 33 | app.use(appErrorHandler); 34 | app.use(genericErrorHandler); 35 | app.use(notFound); 36 | 37 | app.listen(port, () => { 38 | logger.info(`Server started at ${host}:${port}/api/${api_version}/`); 39 | }); 40 | 41 | export default app; 42 | -------------------------------------------------------------------------------- /lib/mongoose/js/express/src/middlewares/auth.middleware.js: -------------------------------------------------------------------------------- 1 | import HttpStatus from 'http-status-codes'; 2 | import jwt from 'jsonwebtoken'; 3 | 4 | /** 5 | * Middleware to authenticate if user has a valid Authorization token 6 | * Authorization: Bearer 7 | * 8 | * @param {Object} req 9 | * @param {Object} res 10 | * @param {Function} next 11 | */ 12 | export const userAuth = async (req, res, next) => { 13 | try { 14 | let bearerToken = req.header('Authorization'); 15 | if (!bearerToken) 16 | throw { 17 | code: HttpStatus.BAD_REQUEST, 18 | message: 'Authorization token is required' 19 | }; 20 | bearerToken = bearerToken.split(' ')[1]; 21 | 22 | const { user } = await jwt.verify(bearerToken, 'your-secret-key'); 23 | res.locals.user = user; 24 | res.locals.token = bearerToken; 25 | next(); 26 | } catch (error) { 27 | next(error); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /lib/mongoose/js/express/src/middlewares/error.middleware.js: -------------------------------------------------------------------------------- 1 | import HttpStatus from 'http-status-codes'; 2 | 3 | import logger from '../config/logger'; 4 | 5 | /** 6 | * Error response middleware for 404 not found. 7 | * 8 | * @param {Object} req 9 | * @param {Object} res 10 | */ 11 | export function notFound(req, res) { 12 | res.status(HttpStatus.NOT_FOUND).json({ 13 | code: HttpStatus.NOT_FOUND, 14 | message: 'Ooops, route not found' 15 | }); 16 | } 17 | 18 | /** 19 | * Error response middleware for handling all app errors except generic errors. 20 | * 21 | * @param {Object} err 22 | * @param {Object} req 23 | * @param {Object} res 24 | * @param {Function} next 25 | */ 26 | // eslint-disable-next-line no-unused-vars 27 | export function appErrorHandler(err, req, res, next) { 28 | if (err.code && typeof err.code === 'number') { 29 | logger.error(` 30 | status - ${err.code} 31 | message - ${err.message} 32 | url - ${req.originalUrl} 33 | method - ${req.method} 34 | IP - ${req.ip} 35 | `); 36 | res.status(err.code).json({ 37 | code: err.code, 38 | message: err.message 39 | }); 40 | } else { 41 | next(err); 42 | } 43 | } 44 | 45 | /** 46 | * Generic error response middleware for internal server errors. 47 | * 48 | * @param {Object} err 49 | * @param {Object} req 50 | * @param {Object} res 51 | * @param {Function} next 52 | */ 53 | // eslint-disable-next-line no-unused-vars 54 | export function genericErrorHandler(err, req, res, next) { 55 | logger.error(` 56 | status - ${HttpStatus.INTERNAL_SERVER_ERROR} 57 | message - ${err.stack} 58 | url - ${req.originalUrl} 59 | method - ${req.method} 60 | IP - ${req.ip} 61 | `); 62 | 63 | res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ 64 | code: HttpStatus.INTERNAL_SERVER_ERROR, 65 | data: '', 66 | message: err.message 67 | }); 68 | } 69 | -------------------------------------------------------------------------------- /lib/mongoose/js/express/src/models/user.model.js: -------------------------------------------------------------------------------- 1 | import { Schema, model } from 'mongoose'; 2 | 3 | const userSchema = new Schema( 4 | { 5 | name: { 6 | type: String 7 | } 8 | }, 9 | { 10 | timestamps: true 11 | } 12 | ); 13 | 14 | export default model('User', userSchema); 15 | -------------------------------------------------------------------------------- /lib/mongoose/js/express/src/routes/index.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | const router = express.Router(); 3 | 4 | import userRoute from './user.route'; 5 | /** 6 | * Function contains Application routes 7 | * 8 | * @returns router 9 | */ 10 | const routes = () => { 11 | router.get('/', (req, res) => { 12 | res.json('Welcome'); 13 | }); 14 | router.use('/users', userRoute); 15 | 16 | return router; 17 | }; 18 | 19 | export default routes; 20 | -------------------------------------------------------------------------------- /lib/mongoose/js/express/src/routes/user.route.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import * as userController from '../controllers/user.controller'; 3 | import { newUserValidator } from '../validators/user.validator'; 4 | import { userAuth } from '../middlewares/auth.middleware'; 5 | 6 | const router = express.Router(); 7 | 8 | //route to get all users 9 | router.get('', userController.getAllUsers); 10 | 11 | //route to create a new user 12 | router.post('', newUserValidator, userController.newUser); 13 | 14 | //route to get a single user by their user id 15 | router.get('/:_id', userAuth, userController.getUser); 16 | 17 | //route to update a single user by their user id 18 | router.put('/:_id', userController.updateUser); 19 | 20 | //route to delete a single user by their user id 21 | router.delete('/:_id', userController.deleteUser); 22 | 23 | export default router; 24 | -------------------------------------------------------------------------------- /lib/mongoose/js/express/src/services/user.service.js: -------------------------------------------------------------------------------- 1 | import User from '../models/user.model'; 2 | 3 | //get all users 4 | export const getAllUsers = async () => { 5 | const data = await User.find(); 6 | return data; 7 | }; 8 | 9 | //create new user 10 | export const newUser = async (body) => { 11 | const data = await User.create(body); 12 | return data; 13 | }; 14 | 15 | //update single user 16 | export const updateUser = async (_id, body) => { 17 | const data = await User.findByIdAndUpdate( 18 | { 19 | _id 20 | }, 21 | body, 22 | { 23 | new: true 24 | } 25 | ); 26 | return data; 27 | }; 28 | 29 | //delete single user 30 | export const deleteUser = async (id) => { 31 | await User.findByIdAndDelete(id); 32 | return ''; 33 | }; 34 | 35 | //get single user 36 | export const getUser = async (id) => { 37 | const data = await User.findById(id); 38 | return data; 39 | }; 40 | -------------------------------------------------------------------------------- /lib/mongoose/js/express/src/utils/user.util.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tolustar/express-api-cli/d39a7ce7f3da09188023519a595e58bb2cadc51a/lib/mongoose/js/express/src/utils/user.util.js -------------------------------------------------------------------------------- /lib/mongoose/js/express/src/validators/user.validator.js: -------------------------------------------------------------------------------- 1 | import Joi from '@hapi/joi'; 2 | 3 | export const newUserValidator = (req, res, next) => { 4 | const schema = Joi.object({ 5 | name: Joi.string().min(4).required() 6 | }); 7 | const { error, value } = schema.validate(req.body); 8 | if (error) { 9 | next(error); 10 | } else { 11 | req.validatedBody = value; 12 | next(); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /lib/mongoose/js/express/tests/integration/user.test.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import request from 'supertest'; 3 | import mongoose from 'mongoose'; 4 | 5 | import app from '../../src/index'; 6 | 7 | describe('User APIs Test', () => { 8 | before((done) => { 9 | const clearCollections = () => { 10 | for (const collection in mongoose.connection.collections) { 11 | mongoose.connection.collections[collection].deleteOne(() => {}); 12 | } 13 | }; 14 | 15 | const mongooseConnect = async () => { 16 | await mongoose.connect(process.env.DATABASE_TEST); 17 | clearCollections(); 18 | }; 19 | 20 | if (mongoose.connection.readyState === 0) { 21 | mongooseConnect(); 22 | } else { 23 | clearCollections(); 24 | } 25 | 26 | done(); 27 | }); 28 | 29 | describe('GET /users', () => { 30 | it('should return empty array', (done) => { 31 | request(app) 32 | .get('/api/v1/users') 33 | .end((err, res) => { 34 | expect(res.statusCode).to.be.equal(200); 35 | expect(res.body.data).to.be.an('array'); 36 | 37 | done(); 38 | }); 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /lib/mongoose/js/express/tests/unit/user.test.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import * as UserService from '../../src/services/user.service'; 3 | import mongoose from 'mongoose'; 4 | 5 | import dotenv from 'dotenv'; 6 | dotenv.config(); 7 | 8 | describe('User', () => { 9 | before((done) => { 10 | const clearCollections = () => { 11 | for (const collection in mongoose.connection.collections) { 12 | mongoose.connection.collections[collection].deleteOne(() => {}); 13 | } 14 | }; 15 | 16 | const mongooseConnect = async () => { 17 | await mongoose.connect(process.env.DATABASE_TEST); 18 | clearCollections(); 19 | }; 20 | 21 | if (mongoose.connection.readyState === 0) { 22 | mongooseConnect(); 23 | } else { 24 | clearCollections(); 25 | } 26 | 27 | done(); 28 | }); 29 | 30 | describe('Get Users', () => { 31 | it('should return empty array', async () => { 32 | const result = await UserService.getAllUsers(); 33 | expect(result).to.be.an('array'); 34 | }); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /lib/mongoose/ts/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tolustar/express-api-cli/d39a7ce7f3da09188023519a595e58bb2cadc51a/lib/mongoose/ts/.DS_Store -------------------------------------------------------------------------------- /lib/mongoose/ts/express/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tolustar/express-api-cli/d39a7ce7f3da09188023519a595e58bb2cadc51a/lib/mongoose/ts/express/.DS_Store -------------------------------------------------------------------------------- /lib/mongoose/ts/express/.env: -------------------------------------------------------------------------------- 1 | APP_HOST = http://localhost 2 | APP_PORT = 3000 3 | 4 | API_VERSION = v1 5 | 6 | DATABASE = mongodb+srv://test:test@demo.aoloa.mongodb.net/express?retryWrites=true&w=majority 7 | DATABASE_TEST = mongodb+srv://test:test@demo.aoloa.mongodb.net/test?retryWrites=true&w=majority 8 | -------------------------------------------------------------------------------- /lib/mongoose/ts/express/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | -------------------------------------------------------------------------------- /lib/mongoose/ts/express/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "extends": [ 5 | "plugin:prettier/recommended", 6 | "plugin:@typescript-eslint/eslint-recommended", 7 | "plugin:@typescript-eslint/recommended" 8 | ], 9 | "rules": { 10 | "no-console": 0, 11 | "no-unused-vars": "error", 12 | "quotes": [ 13 | "error", 14 | "single", 15 | { 16 | "allowTemplateLiterals": true 17 | } 18 | ], 19 | "semi-style": ["error", "last"], 20 | "max-len": [ 21 | "error", 22 | { 23 | "code": 80 24 | } 25 | ], 26 | "no-irregular-whitespace": "error", 27 | "no-trailing-spaces": "error", 28 | "no-multi-spaces": "error", 29 | "eqeqeq": ["error", "always"] 30 | }, 31 | "env": { 32 | "browser": true, 33 | "node": true 34 | } 35 | } -------------------------------------------------------------------------------- /lib/mongoose/ts/express/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | logs 4 | .nyc_output 5 | coverage -------------------------------------------------------------------------------- /lib/mongoose/ts/express/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 2, 4 | "singleQuote": true, 5 | "trailingComma": "none", 6 | "arrowParens": "always", 7 | "proseWrap": "always" 8 | } 9 | -------------------------------------------------------------------------------- /lib/mongoose/ts/express/README.md: -------------------------------------------------------------------------------- 1 | **Express API** 2 | -------------------------------------------------------------------------------- /lib/mongoose/ts/express/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "build/index.ts", 6 | "scripts": { 7 | "build": "tsc", 8 | "dev": "ts-node-dev --respawn --transpile-only ./src/index.ts", 9 | "start": "tsc && node build/index.js", 10 | "test": "cross-env NODE_ENV=test mocha --exit --recursive --require ts-node/register --timeout=5000 tests/**/*.ts", 11 | "test:coverage": "cross-env NODE_ENV=test nyc --require ts-node/register --reporter lcov --reporter text mocha --exit --recursive --timeout=5000 tests/**/*.ts", 12 | "lint": "eslint . --ext .ts", 13 | "lint-fix": "eslint . --ext .ts --fix" 14 | }, 15 | "author": "Tolustar", 16 | "license": "MIT", 17 | "dependencies": { 18 | "@hapi/joi": "^17.1.1", 19 | "cors": "^2.8.5", 20 | "dotenv": "^8.2.0", 21 | "express": "^4.17.1", 22 | "helmet": "^4.1.0", 23 | "http-status-codes": "^2.1.2", 24 | "jsonwebtoken": "^8.5.1", 25 | "mongoose": "^5.10.2", 26 | "morgan": "^1.10.0", 27 | "@sendgrid/mail": "^7.2.5", 28 | "mailgun-js": "^0.22.0", 29 | "ts-node-dev": "^1.0.0-pre.62", 30 | "winston": "^3.3.3", 31 | "winston-daily-rotate-file": "^4.5.0" 32 | }, 33 | "devDependencies": { 34 | "@types/bcrypt": "^3.0.0", 35 | "@types/chai": "^4.2.12", 36 | "@types/cors": "^2.8.7", 37 | "@types/express": "^4.17.8", 38 | "@types/hapi__joi": "^17.1.4", 39 | "@types/helmet": "0.0.48", 40 | "@types/jsonwebtoken": "^8.5.0", 41 | "@types/mocha": "^8.0.3", 42 | "@types/mongoose": "^5.7.36", 43 | "@typescript-eslint/eslint-plugin": "^4.0.1", 44 | "@typescript-eslint/parser": "^4.0.1", 45 | "chai": "^4.2.0", 46 | "cross-env": "^7.0.2", 47 | "eslint": "^7.7.0", 48 | "eslint-config-prettier": "^6.11.0", 49 | "eslint-plugin-prettier": "^3.1.4", 50 | "mocha": "^8.1.3", 51 | "nodemon": "^2.0.4", 52 | "nyc": "^15.1.0", 53 | "prettier": "^2.1.1", 54 | "rimraf": "^3.0.2", 55 | "supertest": "^4.0.2", 56 | "ts-node": "^9.0.0", 57 | "typescript": "^4.0.2" 58 | } 59 | } -------------------------------------------------------------------------------- /lib/mongoose/ts/express/src/config/database.ts: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose'; 2 | import Logger from './logger'; 3 | 4 | class Database { 5 | private DATABASE: string; 6 | private logger; 7 | 8 | constructor() { 9 | // Replace database value in the .env file with your database config url 10 | this.DATABASE = 11 | process.env.NODE_ENV === 'test' 12 | ? process.env.DATABASE_TEST 13 | : process.env.DATABASE; 14 | 15 | this.logger = Logger.logger; 16 | } 17 | 18 | public initializeDatabase = async (): Promise => { 19 | try { 20 | await mongoose.connect(this.DATABASE, { 21 | useFindAndModify: false, 22 | useCreateIndex: true, 23 | useNewUrlParser: true, 24 | useUnifiedTopology: true 25 | }); 26 | this.logger.info('Connected to the database.'); 27 | } catch (error) { 28 | this.logger.error('Could not connect to the database.', error); 29 | } 30 | }; 31 | } 32 | export default Database; 33 | -------------------------------------------------------------------------------- /lib/mongoose/ts/express/src/config/logger.ts: -------------------------------------------------------------------------------- 1 | import winston, { format } from 'winston'; 2 | import 'winston-daily-rotate-file'; 3 | 4 | class Logger { 5 | /** 6 | * Logger handles all logs in the application 7 | */ 8 | static logger = winston.createLogger({ 9 | format: format.combine(format.timestamp(), format.simple()), 10 | transports: [ 11 | new winston.transports.File({ 12 | filename: 'logs/server/error.log', 13 | level: 'error', 14 | handleExceptions: true 15 | }), 16 | new winston.transports.File({ 17 | filename: 'logs/server/all.log', 18 | level: 'info', 19 | handleExceptions: true 20 | }), 21 | new winston.transports.DailyRotateFile({ 22 | maxFiles: '14d', 23 | level: 'info', 24 | dirname: 'logs/server/daily', 25 | datePattern: 'YYYY-MM-DD', 26 | filename: '%DATE%.log' 27 | }), 28 | new winston.transports.Console({ 29 | level: 'debug', 30 | handleExceptions: true 31 | }) 32 | ] 33 | }); 34 | 35 | static logStream = { 36 | /** 37 | * A writable stream for winston logger. 38 | * 39 | * @param {any} message 40 | */ 41 | write(message) { 42 | /** 43 | * morganLogger logs all http request in a dedicated file and on console 44 | */ 45 | const morganLogger = winston.createLogger({ 46 | format: format.combine(format.simple()), 47 | transports: [ 48 | new winston.transports.File({ 49 | filename: 'logs/requests/all.log', 50 | level: 'debug', 51 | handleExceptions: true 52 | }), 53 | new winston.transports.Console({ 54 | level: 'debug', 55 | handleExceptions: true 56 | }), 57 | new winston.transports.DailyRotateFile({ 58 | maxFiles: '14d', 59 | level: 'info', 60 | dirname: 'logs/requests/daily', 61 | datePattern: 'YYYY-MM-DD', 62 | filename: '%DATE%.log' 63 | }) 64 | ] 65 | }); 66 | morganLogger.info(message.toString()); 67 | } 68 | }; 69 | } 70 | 71 | export default Logger; 72 | -------------------------------------------------------------------------------- /lib/mongoose/ts/express/src/controllers/user.controller.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | import HttpStatus from 'http-status-codes'; 3 | import userService from '../services/user.service'; 4 | 5 | import { Request, Response, NextFunction } from 'express'; 6 | 7 | class UserController { 8 | public UserService = new userService(); 9 | 10 | /** 11 | * Controller to get all users available 12 | * @param {object} Request - request object 13 | * @param {object} Response - response object 14 | * @param {Function} NextFunction 15 | */ 16 | public getAllUsers = async ( 17 | req: Request, 18 | res: Response, 19 | next: NextFunction 20 | ): Promise => { 21 | try { 22 | const data = await this.UserService.getAllUsers(); 23 | res.status(HttpStatus.OK).json({ 24 | code: HttpStatus.OK, 25 | data: data, 26 | message: 'All users fetched successfully' 27 | }); 28 | } catch (error) { 29 | next(error); 30 | } 31 | }; 32 | 33 | /** 34 | * Controller to get a user 35 | * @param {object} Request - request object 36 | * @param {object} Response - response object 37 | * @param {Function} NextFunction 38 | */ 39 | public getUser = async ( 40 | req: Request, 41 | res: Response, 42 | next: NextFunction 43 | ): Promise => { 44 | try { 45 | const data = await this.UserService.getUser(req.params._id); 46 | res.status(HttpStatus.OK).json({ 47 | code: HttpStatus.OK, 48 | data: data, 49 | message: 'User fetched successfully' 50 | }); 51 | } catch (error) { 52 | next(error); 53 | } 54 | }; 55 | 56 | /** 57 | * Controller to create new user 58 | * @param {object} Request - request object 59 | * @param {object} Response - response object 60 | * @param {Function} NextFunction 61 | */ 62 | public newUser = async ( 63 | req: Request, 64 | res: Response, 65 | next: NextFunction 66 | ): Promise => { 67 | try { 68 | const data = await this.UserService.newUser(req.body); 69 | res.status(HttpStatus.CREATED).json({ 70 | code: HttpStatus.CREATED, 71 | data: data, 72 | message: 'User created successfully' 73 | }); 74 | } catch (error) { 75 | next(error); 76 | } 77 | }; 78 | 79 | /** 80 | * Controller to update a user 81 | * @param {object} Request - request object 82 | * @param {object} Response - response object 83 | * @param {Function} NextFunction 84 | */ 85 | public updateUser = async ( 86 | req: Request, 87 | res: Response, 88 | next: NextFunction 89 | ): Promise => { 90 | try { 91 | const data = await this.UserService.updateUser(req.params._id, req.body); 92 | res.status(HttpStatus.ACCEPTED).json({ 93 | code: HttpStatus.ACCEPTED, 94 | data: data, 95 | message: 'User updated successfully' 96 | }); 97 | } catch (error) { 98 | next(error); 99 | } 100 | }; 101 | 102 | /** 103 | * Controller to delete a single user 104 | * @param {object} Request - request object 105 | * @param {object} Response - response object 106 | * @param {Function} NextFunction 107 | */ 108 | public deleteUser = async ( 109 | req: Request, 110 | res: Response, 111 | next: NextFunction 112 | ): Promise => { 113 | try { 114 | await this.UserService.deleteUser(req.params._id); 115 | res.status(HttpStatus.OK).json({ 116 | code: HttpStatus.OK, 117 | data: {}, 118 | message: 'User deleted successfully' 119 | }); 120 | } catch (error) { 121 | next(error); 122 | } 123 | }; 124 | } 125 | 126 | export default UserController; 127 | -------------------------------------------------------------------------------- /lib/mongoose/ts/express/src/index.ts: -------------------------------------------------------------------------------- 1 | import dotenv from 'dotenv'; 2 | dotenv.config(); 3 | 4 | import express, { Application } from 'express'; 5 | import cors from 'cors'; 6 | import helmet from 'helmet'; 7 | 8 | import routes from './routes'; 9 | import Database from './config/database'; 10 | import ErrorHandler from './middlewares/error.middleware'; 11 | import Logger from './config/logger'; 12 | 13 | import morgan from 'morgan'; 14 | 15 | class App { 16 | public app: Application; 17 | public host: string | number; 18 | public port: string | number; 19 | public api_version: string | number; 20 | public env: boolean; 21 | private db = new Database(); 22 | private logStream = Logger.logStream; 23 | private logger = Logger.logger; 24 | public errorHandler = new ErrorHandler(); 25 | 26 | constructor() { 27 | this.app = express(); 28 | this.host = process.env.APP_HOST; 29 | this.port = process.env.APP_PORT; 30 | this.api_version = process.env.API_VERSION; 31 | 32 | this.initializeMiddleWares(); 33 | this.initializeRoutes(); 34 | this.initializeDatabase(); 35 | this.initializeErrorHandlers(); 36 | this.startApp(); 37 | } 38 | 39 | public initializeMiddleWares(): void { 40 | this.app.use(cors()); 41 | this.app.use(helmet()); 42 | this.app.use(express.urlencoded({ extended: true })); 43 | this.app.use(express.json()); 44 | this.app.use(morgan('combined', { stream: this.logStream })); 45 | } 46 | 47 | public initializeDatabase(): void { 48 | this.db.initializeDatabase(); 49 | } 50 | 51 | public initializeRoutes(): void { 52 | this.app.use(`/api/${this.api_version}`, routes()); 53 | } 54 | 55 | public initializeErrorHandlers(): void { 56 | this.app.use(this.errorHandler.appErrorHandler); 57 | this.app.use(this.errorHandler.genericErrorHandler); 58 | this.app.use(this.errorHandler.notFound); 59 | } 60 | 61 | public startApp(): void { 62 | this.app.listen(this.port, () => { 63 | this.logger.info( 64 | `Server started at ${this.host}:${this.port}/api/${this.api_version}/` 65 | ); 66 | }); 67 | } 68 | 69 | public getApp(): Application { 70 | return this.app; 71 | } 72 | } 73 | 74 | const app = new App(); 75 | 76 | export default app; 77 | -------------------------------------------------------------------------------- /lib/mongoose/ts/express/src/interfaces/user.interface.ts: -------------------------------------------------------------------------------- 1 | import { Document } from 'mongoose'; 2 | 3 | export interface IUser extends Document { 4 | _id: string | number; 5 | name: string; 6 | } 7 | -------------------------------------------------------------------------------- /lib/mongoose/ts/express/src/middlewares/auth.middleware.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | import HttpStatus from 'http-status-codes'; 3 | import jwt from 'jsonwebtoken'; 4 | import { Request, Response, NextFunction } from 'express'; 5 | 6 | /** 7 | * Middleware to authenticate if user has a valid Authorization token 8 | * Authorization: Bearer 9 | * 10 | * @param {Object} req 11 | * @param {Object} res 12 | * @param {Function} next 13 | */ 14 | export const userAuth = async ( 15 | req: Request, 16 | res: Response, 17 | next: NextFunction 18 | ): Promise => { 19 | try { 20 | let bearerToken = req.header('Authorization'); 21 | if (!bearerToken) 22 | throw { 23 | code: HttpStatus.BAD_REQUEST, 24 | message: 'Authorization token is required' 25 | }; 26 | bearerToken = bearerToken.split(' ')[1]; 27 | 28 | const { user }: any = await jwt.verify(bearerToken, 'your-secret-key'); 29 | res.locals.user = user; 30 | res.locals.token = bearerToken; 31 | next(); 32 | } catch (error) { 33 | next(error); 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /lib/mongoose/ts/express/src/middlewares/error.middleware.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ 2 | /* eslint-disable @typescript-eslint/no-explicit-any */ 3 | /* eslint-disable no-unused-vars */ 4 | /* eslint-disable @typescript-eslint/no-unused-vars */ 5 | import HttpStatus from 'http-status-codes'; 6 | import Logger from '../config/logger'; 7 | import { Request, Response, NextFunction } from 'express'; 8 | 9 | class ErrorMiddleware { 10 | private logger; 11 | 12 | constructor() { 13 | this.logger = Logger.logger; 14 | } 15 | 16 | /** 17 | * Error response middleware for 404 not found. 18 | * 19 | * @param {Object} req 20 | * @param {Object} res 21 | */ 22 | public notFound = (req: Request, res: Response): void => { 23 | res.status(HttpStatus.NOT_FOUND).json({ 24 | code: HttpStatus.NOT_FOUND, 25 | message: 'Ooops, route not found' 26 | }); 27 | }; 28 | 29 | /** 30 | * Error response middleware for handling 31 | * all app errors except generic errors. 32 | * 33 | * @param {Object} err 34 | * @param {Object} req 35 | * @param {Object} res 36 | * @param {Function} next 37 | */ 38 | // eslint-disable-next-line no-unused-vars 39 | public appErrorHandler = ( 40 | err: any, 41 | req: Request, 42 | res: Response, 43 | next: NextFunction 44 | ): void => { 45 | if (err.code && typeof err.code === 'number') { 46 | this.logger.error(` 47 | status - ${err.code} 48 | message - ${err.message} 49 | url - ${req.originalUrl} 50 | method - ${req.method} 51 | IP - ${req.ip} 52 | `); 53 | res.status(err.code).json({ 54 | code: err.code, 55 | message: err.message 56 | }); 57 | } else { 58 | next(err); 59 | } 60 | }; 61 | 62 | /** 63 | * Generic error response middleware for internal server errors. 64 | * 65 | * @param {Object} err 66 | * @param {Object} req 67 | * @param {Object} res 68 | * @param {Function} next 69 | */ 70 | // eslint-disable-next-line no-unused-vars 71 | public genericErrorHandler = ( 72 | err: any, 73 | req: Request, 74 | res: Response, 75 | next: NextFunction 76 | ): void => { 77 | this.logger.error(` 78 | status - ${HttpStatus.INTERNAL_SERVER_ERROR} 79 | message - ${err.stack} 80 | url - ${req.originalUrl} 81 | method - ${req.method} 82 | IP - ${req.ip} 83 | `); 84 | 85 | res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ 86 | code: HttpStatus.INTERNAL_SERVER_ERROR, 87 | data: '', 88 | message: err.message 89 | }); 90 | }; 91 | } 92 | 93 | export default ErrorMiddleware; 94 | -------------------------------------------------------------------------------- /lib/mongoose/ts/express/src/models/user.model.ts: -------------------------------------------------------------------------------- 1 | import { Schema, model } from 'mongoose'; 2 | import { IUser } from '../interfaces/user.interface'; 3 | 4 | const userSchema = new Schema( 5 | { 6 | name: { 7 | type: String 8 | } 9 | }, 10 | { 11 | timestamps: true 12 | } 13 | ); 14 | 15 | export default model('User', userSchema); 16 | -------------------------------------------------------------------------------- /lib/mongoose/ts/express/src/routes/index.ts: -------------------------------------------------------------------------------- 1 | import express, { IRouter } from 'express'; 2 | const router = express.Router(); 3 | 4 | import userRoute from './user.route'; 5 | 6 | /** 7 | * Function contains Application routes 8 | * 9 | * @returns router 10 | */ 11 | const routes = (): IRouter => { 12 | router.get('/', (req, res) => { 13 | res.json('Welcome'); 14 | }); 15 | router.use('/users', new userRoute().getRoutes()); 16 | 17 | return router; 18 | }; 19 | 20 | export default routes; 21 | -------------------------------------------------------------------------------- /lib/mongoose/ts/express/src/routes/user.route.ts: -------------------------------------------------------------------------------- 1 | import express, { IRouter } from 'express'; 2 | import userController from '../controllers/user.controller'; 3 | import userValidator from '../validators/user.validator'; 4 | import { userAuth } from '../middlewares/auth.middleware'; 5 | 6 | class UserRoutes { 7 | private UserController = new userController(); 8 | private router = express.Router(); 9 | private UserValidator = new userValidator(); 10 | 11 | constructor() { 12 | this.routes(); 13 | } 14 | 15 | private routes = () => { 16 | //route to get all users 17 | this.router.get('', this.UserController.getAllUsers); 18 | 19 | //route to create a new user 20 | this.router.post( 21 | '', 22 | this.UserValidator.newUser, 23 | this.UserController.newUser 24 | ); 25 | 26 | //route to get a single user 27 | this.router.get('/:_id', userAuth, this.UserController.getUser); 28 | 29 | //route to update a single user 30 | this.router.put('/:_id', this.UserController.updateUser); 31 | 32 | //route to delete a single user 33 | this.router.delete('/:_id', this.UserController.deleteUser); 34 | }; 35 | 36 | public getRoutes = (): IRouter => { 37 | return this.router; 38 | }; 39 | } 40 | 41 | export default UserRoutes; 42 | -------------------------------------------------------------------------------- /lib/mongoose/ts/express/src/services/user.service.ts: -------------------------------------------------------------------------------- 1 | import User from '../models/user.model'; 2 | import { IUser } from '../interfaces/user.interface'; 3 | 4 | class UserService { 5 | 6 | //get all users 7 | public getAllUsers = async (): Promise => { 8 | const data = await User.find(); 9 | return data; 10 | }; 11 | 12 | //create new user 13 | public newUser = async (body: IUser): Promise => { 14 | const data = await User.create(body); 15 | return data; 16 | }; 17 | 18 | //update a user 19 | public updateUser = async (_id: string, body: IUser): Promise => { 20 | const data = await User.findByIdAndUpdate( 21 | { 22 | _id 23 | }, 24 | body, 25 | { 26 | new: true 27 | } 28 | ); 29 | return data; 30 | }; 31 | 32 | //delete a user 33 | public deleteUser = async (_id: string): Promise => { 34 | await User.findByIdAndDelete(_id); 35 | return ''; 36 | }; 37 | 38 | //get a single user 39 | public getUser = async (_id: string): Promise => { 40 | const data = await User.findById(_id); 41 | return data; 42 | }; 43 | } 44 | 45 | export default UserService; 46 | -------------------------------------------------------------------------------- /lib/mongoose/ts/express/src/utils/user.util.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tolustar/express-api-cli/d39a7ce7f3da09188023519a595e58bb2cadc51a/lib/mongoose/ts/express/src/utils/user.util.ts -------------------------------------------------------------------------------- /lib/mongoose/ts/express/src/validators/user.validator.ts: -------------------------------------------------------------------------------- 1 | import Joi from '@hapi/joi'; 2 | import { Request, Response, NextFunction } from 'express'; 3 | 4 | class UserValidator { 5 | public newUser = (req: Request, res: Response, next: NextFunction): void => { 6 | const schema = Joi.object({ 7 | name: Joi.string().min(4).required() 8 | }); 9 | const { error } = schema.validate(req.body); 10 | if (error) { 11 | next(error); 12 | } 13 | next(); 14 | }; 15 | } 16 | 17 | export default UserValidator; 18 | -------------------------------------------------------------------------------- /lib/mongoose/ts/express/tests/integration/user.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import request from 'supertest'; 3 | import mongoose from 'mongoose'; 4 | 5 | import app from '../../src/index'; 6 | 7 | describe('User APIs Test', () => { 8 | before((done) => { 9 | const clearCollections = () => { 10 | for (const collection in mongoose.connection.collections) { 11 | mongoose.connection.collections[collection].deleteOne(() => {}); 12 | } 13 | }; 14 | 15 | const mongooseConnect = async () => { 16 | await mongoose.connect(process.env.DATABASE_TEST); 17 | clearCollections(); 18 | }; 19 | 20 | if (mongoose.connection.readyState === 0) { 21 | mongooseConnect(); 22 | } else { 23 | clearCollections(); 24 | } 25 | 26 | done(); 27 | }); 28 | 29 | describe('GET /users', () => { 30 | it('should return empty array', (done) => { 31 | request(app.getApp()) 32 | .get('/api/v1/users') 33 | .end((err, res) => { 34 | expect(res.statusCode).to.be.equal(200); 35 | expect(res.body.data).to.be.an('array'); 36 | 37 | done(); 38 | }); 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /lib/mongoose/ts/express/tests/unit/user.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import UserService from '../../src/services/user.service'; 3 | import mongoose from 'mongoose'; 4 | 5 | import dotenv from 'dotenv'; 6 | dotenv.config(); 7 | 8 | describe('User', () => { 9 | before((done) => { 10 | const clearCollections = () => { 11 | for (const collection in mongoose.connection.collections) { 12 | mongoose.connection.collections[collection].deleteOne(() => {}); 13 | } 14 | }; 15 | 16 | const mongooseConnect = async () => { 17 | await mongoose.connect(process.env.DATABASE_TEST); 18 | clearCollections(); 19 | }; 20 | 21 | if (mongoose.connection.readyState === 0) { 22 | mongooseConnect(); 23 | } else { 24 | clearCollections(); 25 | } 26 | 27 | done(); 28 | }); 29 | 30 | describe('Get Users', () => { 31 | it('should return empty array', async () => { 32 | const result = await new UserService().getAllUsers(); 33 | expect(result).to.be.an('array'); 34 | }); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /lib/mongoose/ts/express/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "allowJs": true, 6 | "outDir": "./build", 7 | "rootDir": "./src", 8 | "esModuleInterop": true, 9 | "noImplicitAny": false, 10 | "allowSyntheticDefaultImports": true 11 | }, 12 | "include": ["src/**/*"], 13 | "exclude": ["node_modules", "src/tests"] 14 | } -------------------------------------------------------------------------------- /lib/sequelize/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tolustar/express-api-cli/d39a7ce7f3da09188023519a595e58bb2cadc51a/lib/sequelize/.DS_Store -------------------------------------------------------------------------------- /lib/sequelize/js/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tolustar/express-api-cli/d39a7ce7f3da09188023519a595e58bb2cadc51a/lib/sequelize/js/.DS_Store -------------------------------------------------------------------------------- /lib/sequelize/js/express/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tolustar/express-api-cli/d39a7ce7f3da09188023519a595e58bb2cadc51a/lib/sequelize/js/express/.DS_Store -------------------------------------------------------------------------------- /lib/sequelize/js/express/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"], 3 | "plugins": [ 4 | ["@babel/plugin-transform-runtime", 5 | { 6 | "regenerator": true 7 | } 8 | ] 9 | ] 10 | } -------------------------------------------------------------------------------- /lib/sequelize/js/express/.env: -------------------------------------------------------------------------------- 1 | APP_HOST = http://localhost 2 | APP_PORT = 3000 3 | 4 | API_VERSION = v1 5 | 6 | DATABASE = express 7 | USERNAME = root 8 | PASSWORD = root 9 | HOST = 127.0.0.1 10 | PORT = 3306 11 | DIALECT = mysql 12 | 13 | DATABASE_TEST = express 14 | USERNAME_TEST = root 15 | PASSWORD_TEST = root 16 | HOST_TEST = 127.0.0.1 17 | PORT_TEST = 3306 18 | DIALECT_TEST = mysql 19 | -------------------------------------------------------------------------------- /lib/sequelize/js/express/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "babel-eslint", 4 | "extends": ["plugin:prettier/recommended"], 5 | "rules": { 6 | "no-console": 0, 7 | "no-unused-vars": "error", 8 | "quotes": [ 9 | "error", 10 | "single", 11 | { 12 | "allowTemplateLiterals": true 13 | } 14 | ], 15 | "semi-style": ["error", "last"], 16 | "max-len": [ 17 | "error", 18 | { 19 | "code": 80 20 | } 21 | ], 22 | "no-irregular-whitespace": "error", 23 | "no-trailing-spaces": "error", 24 | "no-multi-spaces": "error", 25 | "eqeqeq": ["error", "always"] 26 | }, 27 | "env": { 28 | "browser": true, 29 | "node": true 30 | } 31 | } -------------------------------------------------------------------------------- /lib/sequelize/js/express/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | logs -------------------------------------------------------------------------------- /lib/sequelize/js/express/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 2, 4 | "singleQuote": true, 5 | "trailingComma": "none", 6 | "arrowParens": "always", 7 | "proseWrap": "always" 8 | } 9 | -------------------------------------------------------------------------------- /lib/sequelize/js/express/.sequelizerc: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | require('dotenv').config(); 3 | 4 | const dir = process.env.NODE_ENV === 'production' ? 'build' : 'src'; 5 | 6 | module.exports = { 7 | 'config': path.resolve(dir,'config', 'config.js'), 8 | 'models-path': path.resolve(dir, 'models'), 9 | 'seeders-path': path.resolve(dir, 'seeders'), 10 | 'migrations-path': path.resolve(dir, 'migrations') 11 | } -------------------------------------------------------------------------------- /lib/sequelize/js/express/README.md: -------------------------------------------------------------------------------- 1 | **Express API** 2 | -------------------------------------------------------------------------------- /lib/sequelize/js/express/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-starter", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "build/index.js", 6 | "scripts": { 7 | "dev": "nodemon --exec babel-node src/index.js", 8 | "build": "rimraf build && babel src --out-dir build", 9 | "prestart": "rimraf build && babel src --out-dir build", 10 | "start": "node build/index.js", 11 | "test": "cross-env NODE_ENV=test mocha --exit --recursive --require @babel/register --timeout=5000 tests/**/*.js", 12 | "test:coverage": "cross-env NODE_ENV=test nyc --require @babel/register --reporter lcov --reporter text mocha --exit --recursive --timeout=5000 tests/**/*.js", 13 | "lint": "eslint . --ext .js", 14 | "lint-fix": "eslint . --ext .js --fix" 15 | }, 16 | "author": "Tolustar", 17 | "license": "MIT", 18 | "dependencies": { 19 | "@hapi/joi": "^17.1.1", 20 | "cors": "^2.8.5", 21 | "dotenv": "^8.2.0", 22 | "express": "^4.17.1", 23 | "helmet": "^4.1.0", 24 | "http-status-codes": "^2.1.2", 25 | "jsonwebtoken": "^8.5.1", 26 | "mongoose": "^5.10.2", 27 | "morgan": "^1.10.0", 28 | "@sendgrid/mail": "^7.2.5", 29 | "mailgun-js": "^0.22.0", 30 | "mysql2": "^2.1.0", 31 | "sequelize": "^6.3.5", 32 | "winston": "^3.3.3", 33 | "winston-daily-rotate-file": "^4.5.0" 34 | }, 35 | "devDependencies": { 36 | "@babel/cli": "^7.10.5", 37 | "@babel/core": "^7.11.4", 38 | "@babel/node": "^7.10.5", 39 | "@babel/plugin-transform-runtime": "^7.11.5", 40 | "@babel/preset-env": "^7.11.0", 41 | "@babel/register": "^7.11.5", 42 | "@babel/runtime": "^7.11.2", 43 | "babel-eslint": "^10.1.0", 44 | "chai": "^4.2.0", 45 | "cross-env": "^7.0.2", 46 | "eslint": "^7.7.0", 47 | "eslint-config-prettier": "^6.11.0", 48 | "eslint-plugin-prettier": "^3.1.4", 49 | "mocha": "^8.1.3", 50 | "nodemon": "^2.0.4", 51 | "nyc": "^15.1.0", 52 | "prettier": "^2.1.1", 53 | "rimraf": "^3.0.2", 54 | "sequelize-cli": "^6.2.0", 55 | "supertest": "^4.0.2" 56 | } 57 | } -------------------------------------------------------------------------------- /lib/sequelize/js/express/src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tolustar/express-api-cli/d39a7ce7f3da09188023519a595e58bb2cadc51a/lib/sequelize/js/express/src/.DS_Store -------------------------------------------------------------------------------- /lib/sequelize/js/express/src/config/config.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | 3 | module.exports = { 4 | development: { 5 | username: process.env.USERNAME, 6 | password: process.env.PASSWORD, 7 | database: process.env.DATABASE, 8 | host: process.env.HOST, 9 | port: process.env.PORT, 10 | dialect: process.env.DIALECT 11 | }, 12 | test: { 13 | username: process.env.USERNAME_TEST, 14 | password: process.env.PASSWORD_TEST, 15 | database: process.env.DATABASE_TEST, 16 | host: process.env.HOST_TEST, 17 | port: process.env.PORT_TEST, 18 | dialect: process.env.DIALECT_TEST 19 | }, 20 | production: { 21 | username: process.env.USERNAME, 22 | password: process.env.PASSWORD, 23 | database: process.env.DATABASE, 24 | host: process.env.HOST, 25 | port: process.env.PORT, 26 | dialect: process.env.DIALECT 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /lib/sequelize/js/express/src/config/database.js: -------------------------------------------------------------------------------- 1 | import Sequelize from 'sequelize'; 2 | import logger from '../config/logger'; 3 | 4 | import dotenv from 'dotenv'; 5 | dotenv.config(); 6 | 7 | export { DataTypes } from 'sequelize'; 8 | 9 | let DATABASE = process.env.DATABASE; 10 | let USERNAME = process.env.USERNAME; 11 | let PASSWORD = process.env.PASSWORD; 12 | let HOST = process.env.HOST; 13 | let PORT = process.env.PORT; 14 | let DIALECT = process.env.DIALECT; 15 | 16 | if (process.env.NODE_ENV === 'test') { 17 | DATABASE = process.env.DATABASE_TEST; 18 | USERNAME = process.env.USERNAME_TEST; 19 | PASSWORD = process.env.PASSWORD_TEST; 20 | HOST = process.env.HOST_TEST; 21 | PORT = process.env.PORT_TEST; 22 | DIALECT = process.env.DIALECT_TEST; 23 | } 24 | 25 | const sequelize = new Sequelize(DATABASE, USERNAME, PASSWORD, { 26 | host: HOST, 27 | port: PORT, 28 | dialect: DIALECT, 29 | pool: { 30 | max: 5, 31 | min: 0, 32 | acquire: 30000, 33 | idle: 10000 34 | } 35 | }); 36 | 37 | sequelize 38 | .authenticate() 39 | .then(() => { 40 | logger.info('Connected to the database.'); 41 | }) 42 | .catch((error) => { 43 | logger.error('Could not connect to the database.', error); 44 | }); 45 | 46 | sequelize.sync(); 47 | 48 | export default sequelize; 49 | -------------------------------------------------------------------------------- /lib/sequelize/js/express/src/config/logger.js: -------------------------------------------------------------------------------- 1 | import winston, { format } from 'winston'; 2 | import 'winston-daily-rotate-file'; 3 | 4 | /** 5 | * Logger handles all logs in the application 6 | */ 7 | const logger = winston.createLogger({ 8 | format: format.combine(format.timestamp(), format.simple()), 9 | colorize: true, 10 | transports: [ 11 | new winston.transports.File({ 12 | filename: 'logs/server/error.log', 13 | level: 'error', 14 | handleExceptions: true 15 | }), 16 | new winston.transports.File({ 17 | filename: 'logs/server/all.log', 18 | level: 'info', 19 | handleExceptions: true 20 | }), 21 | new winston.transports.DailyRotateFile({ 22 | maxFiles: '14d', 23 | level: 'info', 24 | dirname: 'logs/server/daily', 25 | datePattern: 'YYYY-MM-DD', 26 | filename: '%DATE%.log' 27 | }), 28 | new winston.transports.Console({ 29 | level: 'debug', 30 | json: false, 31 | handleExceptions: true 32 | }) 33 | ] 34 | }); 35 | 36 | /** 37 | * morganLogger logs all http request in a dedicated file and on console 38 | */ 39 | const morganLogger = winston.createLogger({ 40 | format: format.combine(format.simple()), 41 | transports: [ 42 | new winston.transports.File({ 43 | filename: 'logs/requests/all.log', 44 | level: 'debug', 45 | handleExceptions: true 46 | }), 47 | new winston.transports.Console({ 48 | level: 'debug', 49 | json: false, 50 | handleExceptions: true 51 | }), 52 | new winston.transports.DailyRotateFile({ 53 | maxFiles: '14d', 54 | level: 'info', 55 | dirname: 'logs/requests/daily', 56 | datePattern: 'YYYY-MM-DD', 57 | filename: '%DATE%.log' 58 | }) 59 | ] 60 | }); 61 | 62 | export const logStream = { 63 | /** 64 | * A writable stream for winston logger. 65 | * 66 | * @param {any} message 67 | */ 68 | write(message) { 69 | morganLogger.info(message.toString()); 70 | } 71 | }; 72 | 73 | export default logger; 74 | -------------------------------------------------------------------------------- /lib/sequelize/js/express/src/controllers/user.controller.js: -------------------------------------------------------------------------------- 1 | import HttpStatus from 'http-status-codes'; 2 | import * as UserService from '../services/user.service'; 3 | 4 | /** 5 | * Controller to get all users available 6 | * @param {object} req - request object 7 | * @param {object} res - response object 8 | * @param {Function} next 9 | */ 10 | export const getAllUsers = async (req, res, next) => { 11 | try { 12 | const data = await UserService.getAllUsers(); 13 | res.status(HttpStatus.OK).json({ 14 | code: HttpStatus.OK, 15 | data: data, 16 | message: 'All users fetched successfully' 17 | }); 18 | } catch (error) { 19 | next(error); 20 | } 21 | }; 22 | 23 | /** 24 | * Controller to get a single user 25 | * @param {object} req - request object 26 | * @param {object} res - response object 27 | * @param {Function} next 28 | */ 29 | export const getUser = async (req, res, next) => { 30 | try { 31 | const data = await UserService.getUser(req.params.id); 32 | res.status(HttpStatus.OK).json({ 33 | code: HttpStatus.OK, 34 | data: data, 35 | message: 'User fetched successfully' 36 | }); 37 | } catch (error) { 38 | next(error); 39 | } 40 | }; 41 | 42 | /** 43 | * Controller to create a new user 44 | * @param {object} req - request object 45 | * @param {object} res - response object 46 | * @param {Function} next 47 | */ 48 | export const newUser = async (req, res, next) => { 49 | try { 50 | const data = await UserService.newUser(req.body); 51 | res.status(HttpStatus.CREATED).json({ 52 | code: HttpStatus.CREATED, 53 | data: data, 54 | message: 'User created successfully' 55 | }); 56 | } catch (error) { 57 | next(error); 58 | } 59 | }; 60 | 61 | /** 62 | * Controller to update a user 63 | * @param {object} req - request object 64 | * @param {object} res - response object 65 | * @param {Function} next 66 | */ 67 | export const updateUser = async (req, res, next) => { 68 | try { 69 | const data = await UserService.updateUser(req.params.id, req.body); 70 | res.status(HttpStatus.ACCEPTED).json({ 71 | code: HttpStatus.ACCEPTED, 72 | data: data, 73 | message: 'User updated successfully' 74 | }); 75 | } catch (error) { 76 | next(error); 77 | } 78 | }; 79 | 80 | /** 81 | * Controller to delete a single user 82 | * @param {object} req - request object 83 | * @param {object} res - response object 84 | * @param {Function} next 85 | */ 86 | export const deleteUser = async (req, res, next) => { 87 | try { 88 | await UserService.updateUser(req.params.id); 89 | res.status(HttpStatus.OK).json({ 90 | code: HttpStatus.OK, 91 | data: [], 92 | message: 'User deleted successfully' 93 | }); 94 | } catch (error) { 95 | next(error); 96 | } 97 | }; 98 | -------------------------------------------------------------------------------- /lib/sequelize/js/express/src/index.js: -------------------------------------------------------------------------------- 1 | import dotenv from 'dotenv'; 2 | dotenv.config(); 3 | 4 | import express from 'express'; 5 | import cors from 'cors'; 6 | import helmet from 'helmet'; 7 | 8 | import routes from './routes'; 9 | import { 10 | appErrorHandler, 11 | genericErrorHandler, 12 | notFound 13 | } from './middlewares/error.middleware'; 14 | import logger, { logStream } from './config/logger'; 15 | 16 | import morgan from 'morgan'; 17 | 18 | const app = express(); 19 | const host = process.env.APP_HOST; 20 | const port = process.env.APP_PORT; 21 | const api_version = process.env.API_VERSION; 22 | 23 | app.use(cors()); 24 | app.use(helmet()); 25 | app.use(express.urlencoded({ extended: true })); 26 | app.use(express.json()); 27 | app.use(morgan('combined', { stream: logStream })); 28 | 29 | app.use(`/api/${api_version}`, routes()); 30 | app.use(appErrorHandler); 31 | app.use(genericErrorHandler); 32 | app.use(notFound); 33 | 34 | app.listen(port, () => { 35 | logger.info(`Server started at ${host}:${port}/api/${api_version}/`); 36 | }); 37 | 38 | export default app; 39 | -------------------------------------------------------------------------------- /lib/sequelize/js/express/src/middlewares/auth.middleware.js: -------------------------------------------------------------------------------- 1 | import HttpStatus from 'http-status-codes'; 2 | import jwt from 'jsonwebtoken'; 3 | 4 | /** 5 | * Middleware to authenticate if user has a valid Authorization token 6 | * Authorization: Bearer 7 | * 8 | * @param {Object} req 9 | * @param {Object} res 10 | * @param {Function} next 11 | */ 12 | export const userAuth = async (req, res, next) => { 13 | try { 14 | let bearerToken = req.header('Authorization'); 15 | if (!bearerToken) 16 | throw { 17 | code: HttpStatus.BAD_REQUEST, 18 | message: 'Authorization token is required' 19 | }; 20 | bearerToken = bearerToken.split(' ')[1]; 21 | 22 | const { user } = await jwt.verify(bearerToken, 'your-secret-key'); 23 | res.locals.user = user; 24 | res.locals.token = bearerToken; 25 | next(); 26 | } catch (error) { 27 | next(error); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /lib/sequelize/js/express/src/middlewares/error.middleware.js: -------------------------------------------------------------------------------- 1 | import HttpStatus from 'http-status-codes'; 2 | 3 | import logger from '../config/logger'; 4 | 5 | /** 6 | * Error response middleware for 404 not found. 7 | * 8 | * @param {Object} req 9 | * @param {Object} res 10 | */ 11 | export function notFound(req, res) { 12 | res.status(HttpStatus.NOT_FOUND).json({ 13 | code: HttpStatus.NOT_FOUND, 14 | message: 'Ooops, route not found' 15 | }); 16 | } 17 | 18 | /** 19 | * Error response middleware for handling all app errors except generic errors. 20 | * 21 | * @param {Object} err 22 | * @param {Object} req 23 | * @param {Object} res 24 | * @param {Function} next 25 | */ 26 | // eslint-disable-next-line no-unused-vars 27 | export function appErrorHandler(err, req, res, next) { 28 | if (err.code && typeof err.code === 'number') { 29 | logger.error(` 30 | status - ${err.code} 31 | message - ${err.message} 32 | url - ${req.originalUrl} 33 | method - ${req.method} 34 | IP - ${req.ip} 35 | `); 36 | res.status(err.code).json({ 37 | code: err.code, 38 | message: err.message 39 | }); 40 | } else { 41 | next(err); 42 | } 43 | } 44 | 45 | /** 46 | * Generic error response middleware for internal server errors. 47 | * 48 | * @param {Object} err 49 | * @param {Object} req 50 | * @param {Object} res 51 | * @param {Function} next 52 | */ 53 | // eslint-disable-next-line no-unused-vars 54 | export function genericErrorHandler(err, req, res, next) { 55 | logger.error(` 56 | status - ${HttpStatus.INTERNAL_SERVER_ERROR} 57 | message - ${err.stack} 58 | url - ${req.originalUrl} 59 | method - ${req.method} 60 | IP - ${req.ip} 61 | `); 62 | 63 | res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ 64 | code: HttpStatus.INTERNAL_SERVER_ERROR, 65 | data: '', 66 | message: err.message 67 | }); 68 | } 69 | -------------------------------------------------------------------------------- /lib/sequelize/js/express/src/models/user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const { Model } = require('sequelize'); 3 | module.exports = (sequelize, DataTypes) => { 4 | class user extends Model { 5 | /** 6 | * Helper method for defining associations. 7 | * This method is not a part of Sequelize lifecycle. 8 | * The `models/index` file will call this method automatically. 9 | */ 10 | static associate(models) { 11 | // define association here 12 | } 13 | } 14 | user.init( 15 | { 16 | firstName: DataTypes.STRING, 17 | lastName: DataTypes.STRING, 18 | email: DataTypes.STRING 19 | }, 20 | { 21 | sequelize, 22 | modelName: 'user' 23 | } 24 | ); 25 | return user; 26 | }; 27 | -------------------------------------------------------------------------------- /lib/sequelize/js/express/src/routes/index.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | const router = express.Router(); 3 | 4 | import userRoute from './user.route'; 5 | /** 6 | * Function contains Application routes 7 | * 8 | * @returns router 9 | */ 10 | const routes = () => { 11 | router.get('/', (req, res) => { 12 | res.json('Welcome'); 13 | }); 14 | router.use('/users', userRoute); 15 | 16 | return router; 17 | }; 18 | 19 | export default routes; 20 | -------------------------------------------------------------------------------- /lib/sequelize/js/express/src/routes/user.route.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import * as userController from '../controllers/user.controller'; 3 | import { newUserValidator } from '../validators/user.validator'; 4 | import { userAuth } from '../middlewares/auth.middleware'; 5 | 6 | const router = express.Router(); 7 | 8 | //route to get all users 9 | router.get('', userController.getAllUsers); 10 | 11 | //route to create a new user 12 | router.post('', newUserValidator, userController.newUser); 13 | 14 | //route to get a single user by their user id 15 | router.get('/:id', userAuth, userController.getUser); 16 | 17 | //route to update a single user by their user id 18 | router.put('/:id', userController.updateUser); 19 | 20 | //route to delete a single user by their user id 21 | router.delete('/:id', userController.deleteUser); 22 | 23 | export default router; 24 | -------------------------------------------------------------------------------- /lib/sequelize/js/express/src/services/user.service.js: -------------------------------------------------------------------------------- 1 | import sequelize, { DataTypes } from '../config/database'; 2 | const User = require('../models/user')(sequelize, DataTypes); 3 | 4 | //get all users 5 | export const getAllUsers = async () => { 6 | const data = await User.findAll(); 7 | return data; 8 | }; 9 | 10 | //create new user 11 | export const newUser = async (body) => { 12 | const data = await User.create(body); 13 | return data; 14 | }; 15 | 16 | //update single user 17 | export const updateUser = async (id, body) => { 18 | await User.update(body, { 19 | where: { id: id } 20 | }); 21 | return body; 22 | }; 23 | 24 | //delete single user 25 | export const deleteUser = async (id) => { 26 | await User.destroy({ where: { id: id } }); 27 | return ''; 28 | }; 29 | 30 | //get single user 31 | export const getUser = async (id) => { 32 | const data = await User.findByPk(id); 33 | return data; 34 | }; 35 | -------------------------------------------------------------------------------- /lib/sequelize/js/express/src/utils/user.util.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tolustar/express-api-cli/d39a7ce7f3da09188023519a595e58bb2cadc51a/lib/sequelize/js/express/src/utils/user.util.js -------------------------------------------------------------------------------- /lib/sequelize/js/express/src/validators/user.validator.js: -------------------------------------------------------------------------------- 1 | import Joi from '@hapi/joi'; 2 | 3 | export const newUserValidator = (req, res, next) => { 4 | const schema = Joi.object({ 5 | firstName: Joi.string().min(3).required(), 6 | lastName: Joi.string().min(3).required(), 7 | email: Joi.string().min(3).required() 8 | }); 9 | const { error, value } = schema.validate(req.body); 10 | if (error) { 11 | next(error); 12 | } else { 13 | req.validatedBody = value; 14 | next(); 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /lib/sequelize/js/express/tests/integration/user.test.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import request from 'supertest'; 3 | 4 | import app from '../../src/index'; 5 | 6 | describe('User APIs Test', () => { 7 | describe('GET /users', () => { 8 | it('should return empty array', (done) => { 9 | request(app) 10 | .get('/api/v1/users') 11 | .end((err, res) => { 12 | expect(res.statusCode).to.be.equal(200); 13 | expect(res.body.data).to.be.an('array'); 14 | 15 | done(); 16 | }); 17 | }); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /lib/sequelize/js/express/tests/unit/user.test.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import * as UserService from '../../src/services/user.service'; 3 | 4 | describe('User', () => { 5 | describe('Get Users', () => { 6 | it('should return empty array', async () => { 7 | const result = await UserService.getAllUsers(); 8 | expect(result).to.be.an('array'); 9 | }); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /lib/sequelize/ts/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tolustar/express-api-cli/d39a7ce7f3da09188023519a595e58bb2cadc51a/lib/sequelize/ts/.DS_Store -------------------------------------------------------------------------------- /lib/sequelize/ts/express/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tolustar/express-api-cli/d39a7ce7f3da09188023519a595e58bb2cadc51a/lib/sequelize/ts/express/.DS_Store -------------------------------------------------------------------------------- /lib/sequelize/ts/express/.env: -------------------------------------------------------------------------------- 1 | APP_HOST = http://localhost 2 | APP_PORT = 3000 3 | 4 | API_VERSION = v1 5 | 6 | DATABASE = express 7 | USERNAME = root 8 | PASSWORD = root 9 | HOST = 127.0.0.1 10 | PORT = 3306 11 | DIALECT = mysql 12 | 13 | DATABASE_TEST = express 14 | USERNAME_TEST = root 15 | PASSWORD_TEST = root 16 | HOST_TEST = 127.0.0.1 17 | PORT_TEST = 3306 18 | DIALECT_TEST = mysql 19 | -------------------------------------------------------------------------------- /lib/sequelize/ts/express/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | -------------------------------------------------------------------------------- /lib/sequelize/ts/express/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "extends": [ 5 | "plugin:prettier/recommended", 6 | "plugin:@typescript-eslint/eslint-recommended", 7 | "plugin:@typescript-eslint/recommended" 8 | ], 9 | "rules": { 10 | "no-console": 0, 11 | "no-unused-vars": "error", 12 | "quotes": [ 13 | "error", 14 | "single", 15 | { 16 | "allowTemplateLiterals": true 17 | } 18 | ], 19 | "semi-style": ["error", "last"], 20 | "max-len": [ 21 | "error", 22 | { 23 | "code": 80 24 | } 25 | ], 26 | "no-irregular-whitespace": "error", 27 | "no-trailing-spaces": "error", 28 | "no-multi-spaces": "error", 29 | "eqeqeq": ["error", "always"] 30 | }, 31 | "env": { 32 | "browser": true, 33 | "node": true 34 | } 35 | } -------------------------------------------------------------------------------- /lib/sequelize/ts/express/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | logs -------------------------------------------------------------------------------- /lib/sequelize/ts/express/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 2, 4 | "singleQuote": true, 5 | "trailingComma": "none", 6 | "arrowParens": "always", 7 | "proseWrap": "always" 8 | } 9 | -------------------------------------------------------------------------------- /lib/sequelize/ts/express/.sequelizerc: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | require('dotenv').config(); 3 | 4 | const dir = process.env.NODE_ENV === 'production' ? 'build' : 'src'; 5 | 6 | module.exports = { 7 | 'config': path.resolve(dir,'config', 'config.js'), 8 | 'models-path': path.resolve(dir, 'models'), 9 | 'seeders-path': path.resolve(dir, 'seeders'), 10 | 'migrations-path': path.resolve(dir, 'migrations') 11 | } -------------------------------------------------------------------------------- /lib/sequelize/ts/express/README.md: -------------------------------------------------------------------------------- 1 | **Express API** 2 | -------------------------------------------------------------------------------- /lib/sequelize/ts/express/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-starter", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "build/index.ts", 6 | "scripts": { 7 | "build": "tsc", 8 | "dev": "ts-node-dev --respawn --transpile-only ./src/index.ts", 9 | "start": "tsc && node build/index.js", 10 | "test": "cross-env NODE_ENV=test mocha --exit --recursive --require ts-node/register --timeout=5000 tests/**/*.ts", 11 | "test:coverage": "cross-env NODE_ENV=test nyc --require ts-node/register --reporter lcov --reporter text mocha --exit --recursive --timeout=5000 tests/**/*.ts", 12 | "lint": "eslint . --ext .ts", 13 | "lint-fix": "eslint . --ext .ts --fix" 14 | }, 15 | "author": "Tolustar", 16 | "license": "MIT", 17 | "dependencies": { 18 | "@hapi/joi": "^17.1.1", 19 | "cors": "^2.8.5", 20 | "dotenv": "^8.2.0", 21 | "express": "^4.17.1", 22 | "helmet": "^4.1.0", 23 | "http-status-codes": "^2.1.2", 24 | "jsonwebtoken": "^8.5.1", 25 | "mongoose": "^5.10.2", 26 | "morgan": "^1.10.0", 27 | "@sendgrid/mail": "^7.2.5", 28 | "mailgun-js": "^0.22.0", 29 | "mysql2": "^2.1.0", 30 | "sequelize": "^6.3.5", 31 | "ts-node-dev": "^1.0.0-pre.62", 32 | "winston": "^3.3.3", 33 | "winston-daily-rotate-file": "^4.5.0" 34 | }, 35 | "devDependencies": { 36 | "@types/bcrypt": "^3.0.0", 37 | "@types/chai": "^4.2.12", 38 | "@types/cors": "^2.8.7", 39 | "@types/express": "^4.17.8", 40 | "@types/hapi__joi": "^17.1.4", 41 | "@types/helmet": "0.0.48", 42 | "@types/jsonwebtoken": "^8.5.0", 43 | "@types/mocha": "^8.0.3", 44 | "@typescript-eslint/eslint-plugin": "^4.0.1", 45 | "@typescript-eslint/parser": "^4.0.1", 46 | "chai": "^4.2.0", 47 | "cross-env": "^7.0.2", 48 | "eslint": "^7.7.0", 49 | "eslint-config-prettier": "^6.11.0", 50 | "eslint-plugin-prettier": "^3.1.4", 51 | "mocha": "^8.1.3", 52 | "nodemon": "^2.0.4", 53 | "nyc": "^15.1.0", 54 | "prettier": "^2.1.1", 55 | "rimraf": "^3.0.2", 56 | "sequelize-cli": "^6.2.0", 57 | "supertest": "^4.0.2", 58 | "ts-node": "^9.0.0", 59 | "typescript": "^4.0.2" 60 | } 61 | } -------------------------------------------------------------------------------- /lib/sequelize/ts/express/src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tolustar/express-api-cli/d39a7ce7f3da09188023519a595e58bb2cadc51a/lib/sequelize/ts/express/src/.DS_Store -------------------------------------------------------------------------------- /lib/sequelize/ts/express/src/config/config.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | 3 | module.exports = { 4 | development: { 5 | username: process.env.USERNAME, 6 | password: process.env.PASSWORD, 7 | database: process.env.DATABASE, 8 | host: process.env.HOST, 9 | port: process.env.PORT, 10 | dialect: process.env.DIALECT 11 | }, 12 | test: { 13 | username: process.env.USERNAME_TEST, 14 | password: process.env.PASSWORD_TEST, 15 | database: process.env.DATABASE_TEST, 16 | host: process.env.HOST_TEST, 17 | port: process.env.PORT_TEST, 18 | dialect: process.env.DIALECT_TEST 19 | }, 20 | production: { 21 | username: process.env.USERNAME, 22 | password: process.env.PASSWORD, 23 | database: process.env.DATABASE, 24 | host: process.env.HOST, 25 | port: process.env.PORT, 26 | dialect: process.env.DIALECT 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /lib/sequelize/ts/express/src/config/database.ts: -------------------------------------------------------------------------------- 1 | import { Sequelize } from 'sequelize'; 2 | import Logger from './logger'; 3 | 4 | import dotenv from 'dotenv'; 5 | dotenv.config(); 6 | 7 | export { DataTypes } from 'sequelize'; 8 | 9 | const logger = Logger.logger; 10 | 11 | let DATABASE = process.env.DATABASE; 12 | let USERNAME = process.env.USERNAME; 13 | let PASSWORD = process.env.PASSWORD; 14 | let HOST = process.env.HOST; 15 | let PORT = parseInt(process.env.PORT); 16 | 17 | if (process.env.NODE_ENV === 'test') { 18 | DATABASE = process.env.DATABASE_TEST; 19 | USERNAME = process.env.USERNAME_TEST; 20 | PASSWORD = process.env.PASSWORD_TEST; 21 | HOST = process.env.HOST_TEST; 22 | PORT = parseInt(process.env.PORT_TEST); 23 | } 24 | 25 | const sequelize = new Sequelize(DATABASE, USERNAME, PASSWORD, { 26 | host: HOST, 27 | port: PORT, 28 | dialect: 'mysql', 29 | pool: { 30 | max: 5, 31 | min: 0, 32 | acquire: 30000, 33 | idle: 10000 34 | } 35 | }); 36 | 37 | sequelize 38 | .authenticate() 39 | .then(() => { 40 | logger.info('Connected to the database.'); 41 | }) 42 | .catch((error) => { 43 | logger.error('Could not connect to the database.', error); 44 | }); 45 | 46 | sequelize.sync(); 47 | 48 | export default sequelize; 49 | -------------------------------------------------------------------------------- /lib/sequelize/ts/express/src/config/logger.ts: -------------------------------------------------------------------------------- 1 | import winston, { format } from 'winston'; 2 | import 'winston-daily-rotate-file'; 3 | 4 | class Logger { 5 | /** 6 | * Logger handles all logs in the application 7 | */ 8 | static logger = winston.createLogger({ 9 | format: format.combine(format.timestamp(), format.simple()), 10 | transports: [ 11 | new winston.transports.File({ 12 | filename: 'logs/server/error.log', 13 | level: 'error', 14 | handleExceptions: true 15 | }), 16 | new winston.transports.File({ 17 | filename: 'logs/server/all.log', 18 | level: 'info', 19 | handleExceptions: true 20 | }), 21 | new winston.transports.DailyRotateFile({ 22 | maxFiles: '14d', 23 | level: 'info', 24 | dirname: 'logs/server/daily', 25 | datePattern: 'YYYY-MM-DD', 26 | filename: '%DATE%.log' 27 | }), 28 | new winston.transports.Console({ 29 | level: 'debug', 30 | handleExceptions: true 31 | }) 32 | ] 33 | }); 34 | 35 | static logStream = { 36 | /** 37 | * A writable stream for winston logger. 38 | * 39 | * @param {any} message 40 | */ 41 | write(message) { 42 | /** 43 | * morganLogger logs all http request in a dedicated file and on console 44 | */ 45 | const morganLogger = winston.createLogger({ 46 | format: format.combine(format.simple()), 47 | transports: [ 48 | new winston.transports.File({ 49 | filename: 'logs/requests/all.log', 50 | level: 'debug', 51 | handleExceptions: true 52 | }), 53 | new winston.transports.Console({ 54 | level: 'debug', 55 | handleExceptions: true 56 | }), 57 | new winston.transports.DailyRotateFile({ 58 | maxFiles: '14d', 59 | level: 'info', 60 | dirname: 'logs/requests/daily', 61 | datePattern: 'YYYY-MM-DD', 62 | filename: '%DATE%.log' 63 | }) 64 | ] 65 | }); 66 | morganLogger.info(message.toString()); 67 | } 68 | }; 69 | } 70 | 71 | export default Logger; 72 | -------------------------------------------------------------------------------- /lib/sequelize/ts/express/src/controllers/user.controller.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | import HttpStatus from 'http-status-codes'; 3 | import userService from '../services/user.service'; 4 | 5 | import { Request, Response, NextFunction } from 'express'; 6 | 7 | class UserController { 8 | public UserService = new userService(); 9 | 10 | /** 11 | * Controller to get all users available 12 | * @param {object} Request - request object 13 | * @param {object} Response - response object 14 | * @param {Function} NextFunction 15 | */ 16 | public getAllUsers = async ( 17 | req: Request, 18 | res: Response, 19 | next: NextFunction 20 | ): Promise => { 21 | try { 22 | const data = await this.UserService.getAllUsers(); 23 | res.status(HttpStatus.OK).json({ 24 | code: HttpStatus.OK, 25 | data: data, 26 | message: 'All users fetched successfully' 27 | }); 28 | } catch (error) { 29 | next(error); 30 | } 31 | }; 32 | 33 | /** 34 | * Controller to get a single user 35 | * @param {object} Request - request object 36 | * @param {object} Response - response object 37 | * @param {Function} NextFunction 38 | */ 39 | public getUser = async ( 40 | req: Request, 41 | res: Response, 42 | next: NextFunction 43 | ): Promise => { 44 | try { 45 | const data = await this.UserService.getUser(req.params.id); 46 | res.status(HttpStatus.OK).json({ 47 | code: HttpStatus.OK, 48 | data: data, 49 | message: 'User fetched successfully' 50 | }); 51 | } catch (error) { 52 | next(error); 53 | } 54 | }; 55 | 56 | /** 57 | * Controller to create new user 58 | * @param {object} Request - request object 59 | * @param {object} Response - response object 60 | * @param {Function} NextFunction 61 | */ 62 | public newUser = async ( 63 | req: Request, 64 | res: Response, 65 | next: NextFunction 66 | ): Promise => { 67 | try { 68 | const data = await this.UserService.newUser(req.body); 69 | res.status(HttpStatus.CREATED).json({ 70 | code: HttpStatus.CREATED, 71 | data: data, 72 | message: 'User created successfully' 73 | }); 74 | } catch (error) { 75 | next(error); 76 | } 77 | }; 78 | 79 | /** 80 | * Controller to update a user 81 | * @param {object} Request - request object 82 | * @param {object} Response - response object 83 | * @param {Function} NextFunction 84 | */ 85 | public updateUser = async ( 86 | req: Request, 87 | res: Response, 88 | next: NextFunction 89 | ): Promise => { 90 | try { 91 | const data = await this.UserService.updateUser(req.params.id, req.body); 92 | res.status(HttpStatus.ACCEPTED).json({ 93 | code: HttpStatus.ACCEPTED, 94 | data: data, 95 | message: 'User updated successfully' 96 | }); 97 | } catch (error) { 98 | next(error); 99 | } 100 | }; 101 | 102 | /** 103 | * Controller to delete a user 104 | * @param {object} Request - request object 105 | * @param {object} Response - response object 106 | * @param {Function} NextFunction 107 | */ 108 | public deleteUser = async ( 109 | req: Request, 110 | res: Response, 111 | next: NextFunction 112 | ): Promise => { 113 | try { 114 | await this.UserService.deleteUser(req.params.id); 115 | res.status(HttpStatus.OK).json({ 116 | code: HttpStatus.OK, 117 | data: {}, 118 | message: 'User deleted successfully' 119 | }); 120 | } catch (error) { 121 | next(error); 122 | } 123 | }; 124 | } 125 | 126 | export default UserController; 127 | -------------------------------------------------------------------------------- /lib/sequelize/ts/express/src/index.ts: -------------------------------------------------------------------------------- 1 | import dotenv from 'dotenv'; 2 | dotenv.config(); 3 | 4 | import express, { Application } from 'express'; 5 | import cors from 'cors'; 6 | import helmet from 'helmet'; 7 | 8 | import routes from './routes'; 9 | import ErrorHandler from './middlewares/error.middleware'; 10 | import Logger from './config/logger'; 11 | 12 | import morgan from 'morgan'; 13 | 14 | class App { 15 | public app: Application; 16 | public host: string | number; 17 | public port: string | number; 18 | public api_version: string | number; 19 | public env: boolean; 20 | private logStream = Logger.logStream; 21 | private logger = Logger.logger; 22 | public errorHandler = new ErrorHandler(); 23 | 24 | constructor() { 25 | this.app = express(); 26 | this.host = process.env.APP_HOST; 27 | this.port = process.env.APP_PORT; 28 | this.api_version = process.env.API_VERSION; 29 | 30 | this.initializeMiddleWares(); 31 | this.initializeRoutes(); 32 | this.initializeErrorHandlers(); 33 | this.startApp(); 34 | } 35 | 36 | public initializeMiddleWares(): void { 37 | this.app.use(cors()); 38 | this.app.use(helmet()); 39 | this.app.use(express.urlencoded({ extended: true })); 40 | this.app.use(express.json()); 41 | this.app.use(morgan('combined', { stream: this.logStream })); 42 | } 43 | 44 | public initializeRoutes(): void { 45 | this.app.use(`/api/${this.api_version}`, routes()); 46 | } 47 | 48 | public initializeErrorHandlers(): void { 49 | this.app.use(this.errorHandler.appErrorHandler); 50 | this.app.use(this.errorHandler.genericErrorHandler); 51 | this.app.use(this.errorHandler.notFound); 52 | } 53 | 54 | public startApp(): void { 55 | this.app.listen(this.port, () => { 56 | this.logger.info( 57 | `Server started at ${this.host}:${this.port}/api/${this.api_version}/` 58 | ); 59 | }); 60 | } 61 | 62 | public getApp(): Application { 63 | return this.app; 64 | } 65 | } 66 | 67 | const app = new App(); 68 | 69 | export default app; 70 | -------------------------------------------------------------------------------- /lib/sequelize/ts/express/src/interfaces/user.interface.ts: -------------------------------------------------------------------------------- 1 | export interface IUser { 2 | firstName: string; 3 | lastName: string; 4 | email: string; 5 | id?: string | number; 6 | } 7 | -------------------------------------------------------------------------------- /lib/sequelize/ts/express/src/middlewares/auth.middleware.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | import HttpStatus from 'http-status-codes'; 3 | import jwt from 'jsonwebtoken'; 4 | import { Request, Response, NextFunction } from 'express'; 5 | 6 | /** 7 | * Middleware to authenticate if user has a valid Authorization token 8 | * Authorization: Bearer 9 | * 10 | * @param {Object} req 11 | * @param {Object} res 12 | * @param {Function} next 13 | */ 14 | export const userAuth = async ( 15 | req: Request, 16 | res: Response, 17 | next: NextFunction 18 | ): Promise => { 19 | try { 20 | let bearerToken = req.header('Authorization'); 21 | if (!bearerToken) 22 | throw { 23 | code: HttpStatus.BAD_REQUEST, 24 | message: 'Authorization token is required' 25 | }; 26 | bearerToken = bearerToken.split(' ')[1]; 27 | 28 | const { user }: any = await jwt.verify(bearerToken, 'your-secret-key'); 29 | res.locals.user = user; 30 | res.locals.token = bearerToken; 31 | next(); 32 | } catch (error) { 33 | next(error); 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /lib/sequelize/ts/express/src/middlewares/error.middleware.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ 2 | /* eslint-disable @typescript-eslint/no-explicit-any */ 3 | /* eslint-disable no-unused-vars */ 4 | /* eslint-disable @typescript-eslint/no-unused-vars */ 5 | import HttpStatus from 'http-status-codes'; 6 | import Logger from '../config/logger'; 7 | import { Request, Response, NextFunction } from 'express'; 8 | 9 | class ErrorMiddleware { 10 | private logger; 11 | 12 | constructor() { 13 | this.logger = Logger.logger; 14 | } 15 | 16 | /** 17 | * Error response middleware for 404 not found. 18 | * 19 | * @param {Object} req 20 | * @param {Object} res 21 | */ 22 | public notFound = (req: Request, res: Response): void => { 23 | res.status(HttpStatus.NOT_FOUND).json({ 24 | code: HttpStatus.NOT_FOUND, 25 | message: 'Ooops, route not found' 26 | }); 27 | }; 28 | 29 | /** 30 | * Error response middleware for handling 31 | * all app errors except generic errors. 32 | * 33 | * @param {Object} err 34 | * @param {Object} req 35 | * @param {Object} res 36 | * @param {Function} next 37 | */ 38 | // eslint-disable-next-line no-unused-vars 39 | public appErrorHandler = ( 40 | err: any, 41 | req: Request, 42 | res: Response, 43 | next: NextFunction 44 | ): void => { 45 | if (err.code && typeof err.code === 'number') { 46 | this.logger.error(` 47 | status - ${err.code} 48 | message - ${err.message} 49 | url - ${req.originalUrl} 50 | method - ${req.method} 51 | IP - ${req.ip} 52 | `); 53 | res.status(err.code).json({ 54 | code: err.code, 55 | message: err.message 56 | }); 57 | } else { 58 | next(err); 59 | } 60 | }; 61 | 62 | /** 63 | * Generic error response middleware for internal server errors. 64 | * 65 | * @param {Object} err 66 | * @param {Object} req 67 | * @param {Object} res 68 | * @param {Function} next 69 | */ 70 | // eslint-disable-next-line no-unused-vars 71 | public genericErrorHandler = ( 72 | err: any, 73 | req: Request, 74 | res: Response, 75 | next: NextFunction 76 | ): void => { 77 | this.logger.error(` 78 | status - ${HttpStatus.INTERNAL_SERVER_ERROR} 79 | message - ${err.stack} 80 | url - ${req.originalUrl} 81 | method - ${req.method} 82 | IP - ${req.ip} 83 | `); 84 | 85 | res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ 86 | code: HttpStatus.INTERNAL_SERVER_ERROR, 87 | data: '', 88 | message: err.message 89 | }); 90 | }; 91 | } 92 | 93 | export default ErrorMiddleware; 94 | -------------------------------------------------------------------------------- /lib/sequelize/ts/express/src/models/user.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-vars */ 2 | /* eslint-disable @typescript-eslint/no-unused-vars */ 3 | 'use strict'; 4 | import { Model } from 'sequelize'; 5 | import { IUser } from '../interfaces/user.interface'; 6 | 7 | export default (sequelize, DataTypes) => { 8 | class User extends Model implements IUser { 9 | public firstName; 10 | public lastName; 11 | public email; 12 | /** 13 | * Helper method for defining associations. 14 | * This method is not a part of Sequelize lifecycle. 15 | * The `models/index` file will call this method automatically. 16 | */ 17 | static associate(models) { 18 | // define association here 19 | } 20 | } 21 | User.init( 22 | { 23 | firstName: DataTypes.STRING, 24 | lastName: DataTypes.STRING, 25 | email: DataTypes.STRING 26 | }, 27 | { 28 | sequelize, 29 | modelName: 'user' 30 | } 31 | ); 32 | return User; 33 | }; 34 | -------------------------------------------------------------------------------- /lib/sequelize/ts/express/src/routes/index.ts: -------------------------------------------------------------------------------- 1 | import express, { IRouter } from 'express'; 2 | const router = express.Router(); 3 | 4 | import userRoute from './user.route'; 5 | 6 | /** 7 | * Function contains Application routes 8 | * 9 | * @returns router 10 | */ 11 | const routes = (): IRouter => { 12 | router.get('/', (req, res) => { 13 | res.json('Welcome'); 14 | }); 15 | router.use('/users', new userRoute().getRoutes()); 16 | 17 | return router; 18 | }; 19 | 20 | export default routes; 21 | -------------------------------------------------------------------------------- /lib/sequelize/ts/express/src/routes/user.route.ts: -------------------------------------------------------------------------------- 1 | import express, { IRouter } from 'express'; 2 | import userController from '../controllers/user.controller'; 3 | import userValidator from '../validators/user.validator'; 4 | import { userAuth } from '../middlewares/auth.middleware'; 5 | 6 | class UserRoutes { 7 | private UserController = new userController(); 8 | private router = express.Router(); 9 | private UserValidator = new userValidator(); 10 | 11 | constructor() { 12 | this.routes(); 13 | } 14 | 15 | private routes = () => { 16 | 17 | //route to get all users 18 | this.router.get('', this.UserController.getAllUsers); 19 | 20 | //route to create a new user 21 | this.router.post( 22 | '', 23 | this.UserValidator.newUser, 24 | this.UserController.newUser 25 | ); 26 | 27 | //route to get a single user by their id 28 | this.router.get('/:id', userAuth, this.UserController.getUser); 29 | 30 | //route to update a user by their id 31 | this.router.put('/:id', this.UserController.updateUser); 32 | 33 | //route to delete a user by their id 34 | this.router.delete('/:id', this.UserController.deleteUser); 35 | }; 36 | 37 | public getRoutes = (): IRouter => { 38 | return this.router; 39 | }; 40 | } 41 | 42 | export default UserRoutes; 43 | -------------------------------------------------------------------------------- /lib/sequelize/ts/express/src/services/user.service.ts: -------------------------------------------------------------------------------- 1 | import sequelize, { DataTypes } from '../config/database'; 2 | import { IUser } from '../interfaces/user.interface'; 3 | 4 | import user from '../models/user'; 5 | 6 | class UserService { 7 | private User = user(sequelize, DataTypes); 8 | 9 | //get all users 10 | public getAllUsers = async (): Promise => { 11 | const data = await this.User.findAll(); 12 | return data; 13 | }; 14 | 15 | //create a new user 16 | public newUser = async (body) => { 17 | const data = await this.User.create(body); 18 | return data; 19 | }; 20 | 21 | //update a user 22 | public updateUser = async (id, body) => { 23 | await this.User.update(body, { 24 | where: { id: id } 25 | }); 26 | return body; 27 | }; 28 | 29 | //delete a user 30 | public deleteUser = async (id) => { 31 | await this.User.destroy({ where: { id: id } }); 32 | return ''; 33 | }; 34 | 35 | //get a single user 36 | public getUser = async (id) => { 37 | const data = await this.User.findByPk(id); 38 | return data; 39 | }; 40 | } 41 | 42 | export default UserService; 43 | -------------------------------------------------------------------------------- /lib/sequelize/ts/express/src/utils/user.util.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tolustar/express-api-cli/d39a7ce7f3da09188023519a595e58bb2cadc51a/lib/sequelize/ts/express/src/utils/user.util.ts -------------------------------------------------------------------------------- /lib/sequelize/ts/express/src/validators/user.validator.ts: -------------------------------------------------------------------------------- 1 | import Joi from '@hapi/joi'; 2 | import { Request, Response, NextFunction } from 'express'; 3 | 4 | class UserValidator { 5 | public newUser = (req: Request, res: Response, next: NextFunction): void => { 6 | const schema = Joi.object({ 7 | name: Joi.string().min(4).required() 8 | }); 9 | const { error } = schema.validate(req.body); 10 | if (error) { 11 | next(error); 12 | } 13 | next(); 14 | }; 15 | } 16 | 17 | export default UserValidator; 18 | -------------------------------------------------------------------------------- /lib/sequelize/ts/express/tests/integration/user.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import request from 'supertest'; 3 | 4 | import app from '../../src/index'; 5 | 6 | describe('User APIs Test', () => { 7 | describe('GET /users', () => { 8 | it('should return empty array', (done) => { 9 | request(app.getApp()) 10 | .get('/api/v1/users') 11 | .end((err, res) => { 12 | expect(res.statusCode).to.be.equal(200); 13 | expect(res.body.data).to.be.an('array'); 14 | 15 | done(); 16 | }); 17 | }); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /lib/sequelize/ts/express/tests/unit/user.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import UserService from '../../src/services/user.service'; 3 | 4 | describe('User', () => { 5 | describe('Get Users', () => { 6 | it('should return empty array', async () => { 7 | const result = await new UserService().getAllUsers(); 8 | expect(result).to.be.an('array'); 9 | }); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /lib/sequelize/ts/express/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "allowJs": true, 6 | "outDir": "./build", 7 | "rootDir": "./src", 8 | "esModuleInterop": true, 9 | "noImplicitAny": false, 10 | "allowSyntheticDefaultImports": true 11 | }, 12 | "include": ["src/**/*"], 13 | "exclude": ["node_modules", "src/tests"] 14 | } -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-api-cli", 3 | "version": "0.0.7", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@babel/code-frame": { 8 | "version": "7.10.4", 9 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", 10 | "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", 11 | "dev": true, 12 | "requires": { 13 | "@babel/highlight": "^7.10.4" 14 | } 15 | }, 16 | "@babel/generator": { 17 | "version": "7.11.6", 18 | "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.6.tgz", 19 | "integrity": "sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA==", 20 | "dev": true, 21 | "requires": { 22 | "@babel/types": "^7.11.5", 23 | "jsesc": "^2.5.1", 24 | "source-map": "^0.5.0" 25 | } 26 | }, 27 | "@babel/helper-function-name": { 28 | "version": "7.10.4", 29 | "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", 30 | "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", 31 | "dev": true, 32 | "requires": { 33 | "@babel/helper-get-function-arity": "^7.10.4", 34 | "@babel/template": "^7.10.4", 35 | "@babel/types": "^7.10.4" 36 | } 37 | }, 38 | "@babel/helper-get-function-arity": { 39 | "version": "7.10.4", 40 | "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", 41 | "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", 42 | "dev": true, 43 | "requires": { 44 | "@babel/types": "^7.10.4" 45 | } 46 | }, 47 | "@babel/helper-split-export-declaration": { 48 | "version": "7.11.0", 49 | "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", 50 | "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", 51 | "dev": true, 52 | "requires": { 53 | "@babel/types": "^7.11.0" 54 | } 55 | }, 56 | "@babel/helper-validator-identifier": { 57 | "version": "7.10.4", 58 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", 59 | "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", 60 | "dev": true 61 | }, 62 | "@babel/highlight": { 63 | "version": "7.10.4", 64 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", 65 | "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", 66 | "dev": true, 67 | "requires": { 68 | "@babel/helper-validator-identifier": "^7.10.4", 69 | "chalk": "^2.0.0", 70 | "js-tokens": "^4.0.0" 71 | }, 72 | "dependencies": { 73 | "ansi-styles": { 74 | "version": "3.2.1", 75 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 76 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 77 | "dev": true, 78 | "requires": { 79 | "color-convert": "^1.9.0" 80 | } 81 | }, 82 | "chalk": { 83 | "version": "2.4.2", 84 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 85 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 86 | "dev": true, 87 | "requires": { 88 | "ansi-styles": "^3.2.1", 89 | "escape-string-regexp": "^1.0.5", 90 | "supports-color": "^5.3.0" 91 | } 92 | }, 93 | "color-convert": { 94 | "version": "1.9.3", 95 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 96 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 97 | "dev": true, 98 | "requires": { 99 | "color-name": "1.1.3" 100 | } 101 | }, 102 | "color-name": { 103 | "version": "1.1.3", 104 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 105 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 106 | "dev": true 107 | }, 108 | "has-flag": { 109 | "version": "3.0.0", 110 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 111 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 112 | "dev": true 113 | }, 114 | "supports-color": { 115 | "version": "5.5.0", 116 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 117 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 118 | "dev": true, 119 | "requires": { 120 | "has-flag": "^3.0.0" 121 | } 122 | } 123 | } 124 | }, 125 | "@babel/parser": { 126 | "version": "7.11.5", 127 | "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz", 128 | "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==", 129 | "dev": true 130 | }, 131 | "@babel/template": { 132 | "version": "7.10.4", 133 | "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", 134 | "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", 135 | "dev": true, 136 | "requires": { 137 | "@babel/code-frame": "^7.10.4", 138 | "@babel/parser": "^7.10.4", 139 | "@babel/types": "^7.10.4" 140 | } 141 | }, 142 | "@babel/traverse": { 143 | "version": "7.11.5", 144 | "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.5.tgz", 145 | "integrity": "sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ==", 146 | "dev": true, 147 | "requires": { 148 | "@babel/code-frame": "^7.10.4", 149 | "@babel/generator": "^7.11.5", 150 | "@babel/helper-function-name": "^7.10.4", 151 | "@babel/helper-split-export-declaration": "^7.11.0", 152 | "@babel/parser": "^7.11.5", 153 | "@babel/types": "^7.11.5", 154 | "debug": "^4.1.0", 155 | "globals": "^11.1.0", 156 | "lodash": "^4.17.19" 157 | }, 158 | "dependencies": { 159 | "globals": { 160 | "version": "11.12.0", 161 | "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", 162 | "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", 163 | "dev": true 164 | } 165 | } 166 | }, 167 | "@babel/types": { 168 | "version": "7.11.5", 169 | "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", 170 | "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", 171 | "dev": true, 172 | "requires": { 173 | "@babel/helper-validator-identifier": "^7.10.4", 174 | "lodash": "^4.17.19", 175 | "to-fast-properties": "^2.0.0" 176 | } 177 | }, 178 | "@eslint/eslintrc": { 179 | "version": "0.1.3", 180 | "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.1.3.tgz", 181 | "integrity": "sha512-4YVwPkANLeNtRjMekzux1ci8hIaH5eGKktGqR0d3LWsKNn5B2X/1Z6Trxy7jQXl9EBGE6Yj02O+t09FMeRllaA==", 182 | "dev": true, 183 | "requires": { 184 | "ajv": "^6.12.4", 185 | "debug": "^4.1.1", 186 | "espree": "^7.3.0", 187 | "globals": "^12.1.0", 188 | "ignore": "^4.0.6", 189 | "import-fresh": "^3.2.1", 190 | "js-yaml": "^3.13.1", 191 | "lodash": "^4.17.19", 192 | "minimatch": "^3.0.4", 193 | "strip-json-comments": "^3.1.1" 194 | } 195 | }, 196 | "@types/color-name": { 197 | "version": "1.1.1", 198 | "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", 199 | "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" 200 | }, 201 | "acorn": { 202 | "version": "7.4.0", 203 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz", 204 | "integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==", 205 | "dev": true 206 | }, 207 | "acorn-jsx": { 208 | "version": "5.2.0", 209 | "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", 210 | "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", 211 | "dev": true 212 | }, 213 | "ajv": { 214 | "version": "6.12.4", 215 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz", 216 | "integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==", 217 | "dev": true, 218 | "requires": { 219 | "fast-deep-equal": "^3.1.1", 220 | "fast-json-stable-stringify": "^2.0.0", 221 | "json-schema-traverse": "^0.4.1", 222 | "uri-js": "^4.2.2" 223 | } 224 | }, 225 | "ansi-colors": { 226 | "version": "4.1.1", 227 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", 228 | "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", 229 | "dev": true 230 | }, 231 | "ansi-escapes": { 232 | "version": "4.3.1", 233 | "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", 234 | "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", 235 | "requires": { 236 | "type-fest": "^0.11.0" 237 | } 238 | }, 239 | "ansi-regex": { 240 | "version": "5.0.0", 241 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", 242 | "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" 243 | }, 244 | "ansi-styles": { 245 | "version": "4.2.1", 246 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", 247 | "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", 248 | "requires": { 249 | "@types/color-name": "^1.1.1", 250 | "color-convert": "^2.0.1" 251 | } 252 | }, 253 | "argparse": { 254 | "version": "1.0.10", 255 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 256 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 257 | "dev": true, 258 | "requires": { 259 | "sprintf-js": "~1.0.2" 260 | } 261 | }, 262 | "astral-regex": { 263 | "version": "1.0.0", 264 | "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", 265 | "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", 266 | "dev": true 267 | }, 268 | "at-least-node": { 269 | "version": "1.0.0", 270 | "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", 271 | "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" 272 | }, 273 | "babel-eslint": { 274 | "version": "10.1.0", 275 | "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", 276 | "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", 277 | "dev": true, 278 | "requires": { 279 | "@babel/code-frame": "^7.0.0", 280 | "@babel/parser": "^7.7.0", 281 | "@babel/traverse": "^7.7.0", 282 | "@babel/types": "^7.7.0", 283 | "eslint-visitor-keys": "^1.0.0", 284 | "resolve": "^1.12.0" 285 | } 286 | }, 287 | "balanced-match": { 288 | "version": "1.0.0", 289 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 290 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 291 | "dev": true 292 | }, 293 | "brace-expansion": { 294 | "version": "1.1.11", 295 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 296 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 297 | "dev": true, 298 | "requires": { 299 | "balanced-match": "^1.0.0", 300 | "concat-map": "0.0.1" 301 | } 302 | }, 303 | "callsites": { 304 | "version": "3.1.0", 305 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", 306 | "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", 307 | "dev": true 308 | }, 309 | "camelcase": { 310 | "version": "5.3.1", 311 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", 312 | "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" 313 | }, 314 | "chalk": { 315 | "version": "4.1.0", 316 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", 317 | "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", 318 | "requires": { 319 | "ansi-styles": "^4.1.0", 320 | "supports-color": "^7.1.0" 321 | } 322 | }, 323 | "chardet": { 324 | "version": "0.7.0", 325 | "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", 326 | "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" 327 | }, 328 | "cli-cursor": { 329 | "version": "3.1.0", 330 | "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", 331 | "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", 332 | "requires": { 333 | "restore-cursor": "^3.1.0" 334 | } 335 | }, 336 | "cli-spinners": { 337 | "version": "2.4.0", 338 | "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.4.0.tgz", 339 | "integrity": "sha512-sJAofoarcm76ZGpuooaO0eDy8saEy+YoZBLjC4h8srt4jeBnkYeOgqxgsJQTpyt2LjI5PTfLJHSL+41Yu4fEJA==" 340 | }, 341 | "cli-width": { 342 | "version": "3.0.0", 343 | "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", 344 | "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==" 345 | }, 346 | "cliui": { 347 | "version": "6.0.0", 348 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", 349 | "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", 350 | "requires": { 351 | "string-width": "^4.2.0", 352 | "strip-ansi": "^6.0.0", 353 | "wrap-ansi": "^6.2.0" 354 | } 355 | }, 356 | "clone": { 357 | "version": "1.0.4", 358 | "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", 359 | "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=" 360 | }, 361 | "color-convert": { 362 | "version": "2.0.1", 363 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 364 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 365 | "requires": { 366 | "color-name": "~1.1.4" 367 | } 368 | }, 369 | "color-name": { 370 | "version": "1.1.4", 371 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 372 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" 373 | }, 374 | "concat-map": { 375 | "version": "0.0.1", 376 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 377 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 378 | "dev": true 379 | }, 380 | "cross-spawn": { 381 | "version": "7.0.3", 382 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", 383 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", 384 | "dev": true, 385 | "requires": { 386 | "path-key": "^3.1.0", 387 | "shebang-command": "^2.0.0", 388 | "which": "^2.0.1" 389 | } 390 | }, 391 | "debug": { 392 | "version": "4.1.1", 393 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", 394 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", 395 | "dev": true, 396 | "requires": { 397 | "ms": "^2.1.1" 398 | } 399 | }, 400 | "decamelize": { 401 | "version": "1.2.0", 402 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 403 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" 404 | }, 405 | "deep-is": { 406 | "version": "0.1.3", 407 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", 408 | "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", 409 | "dev": true 410 | }, 411 | "defaults": { 412 | "version": "1.0.3", 413 | "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", 414 | "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", 415 | "requires": { 416 | "clone": "^1.0.2" 417 | } 418 | }, 419 | "doctrine": { 420 | "version": "3.0.0", 421 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", 422 | "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", 423 | "dev": true, 424 | "requires": { 425 | "esutils": "^2.0.2" 426 | } 427 | }, 428 | "emoji-regex": { 429 | "version": "8.0.0", 430 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 431 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" 432 | }, 433 | "enquirer": { 434 | "version": "2.3.6", 435 | "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", 436 | "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", 437 | "dev": true, 438 | "requires": { 439 | "ansi-colors": "^4.1.1" 440 | } 441 | }, 442 | "escape-string-regexp": { 443 | "version": "1.0.5", 444 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 445 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 446 | }, 447 | "eslint": { 448 | "version": "7.8.1", 449 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.8.1.tgz", 450 | "integrity": "sha512-/2rX2pfhyUG0y+A123d0ccXtMm7DV7sH1m3lk9nk2DZ2LReq39FXHueR9xZwshE5MdfSf0xunSaMWRqyIA6M1w==", 451 | "dev": true, 452 | "requires": { 453 | "@babel/code-frame": "^7.0.0", 454 | "@eslint/eslintrc": "^0.1.3", 455 | "ajv": "^6.10.0", 456 | "chalk": "^4.0.0", 457 | "cross-spawn": "^7.0.2", 458 | "debug": "^4.0.1", 459 | "doctrine": "^3.0.0", 460 | "enquirer": "^2.3.5", 461 | "eslint-scope": "^5.1.0", 462 | "eslint-utils": "^2.1.0", 463 | "eslint-visitor-keys": "^1.3.0", 464 | "espree": "^7.3.0", 465 | "esquery": "^1.2.0", 466 | "esutils": "^2.0.2", 467 | "file-entry-cache": "^5.0.1", 468 | "functional-red-black-tree": "^1.0.1", 469 | "glob-parent": "^5.0.0", 470 | "globals": "^12.1.0", 471 | "ignore": "^4.0.6", 472 | "import-fresh": "^3.0.0", 473 | "imurmurhash": "^0.1.4", 474 | "is-glob": "^4.0.0", 475 | "js-yaml": "^3.13.1", 476 | "json-stable-stringify-without-jsonify": "^1.0.1", 477 | "levn": "^0.4.1", 478 | "lodash": "^4.17.19", 479 | "minimatch": "^3.0.4", 480 | "natural-compare": "^1.4.0", 481 | "optionator": "^0.9.1", 482 | "progress": "^2.0.0", 483 | "regexpp": "^3.1.0", 484 | "semver": "^7.2.1", 485 | "strip-ansi": "^6.0.0", 486 | "strip-json-comments": "^3.1.0", 487 | "table": "^5.2.3", 488 | "text-table": "^0.2.0", 489 | "v8-compile-cache": "^2.0.3" 490 | } 491 | }, 492 | "eslint-config-prettier": { 493 | "version": "6.11.0", 494 | "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz", 495 | "integrity": "sha512-oB8cpLWSAjOVFEJhhyMZh6NOEOtBVziaqdDQ86+qhDHFbZXoRTM7pNSvFRfW/W/L/LrQ38C99J5CGuRBBzBsdA==", 496 | "dev": true, 497 | "requires": { 498 | "get-stdin": "^6.0.0" 499 | } 500 | }, 501 | "eslint-plugin-prettier": { 502 | "version": "3.1.4", 503 | "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.4.tgz", 504 | "integrity": "sha512-jZDa8z76klRqo+TdGDTFJSavwbnWK2ZpqGKNZ+VvweMW516pDUMmQ2koXvxEE4JhzNvTv+radye/bWGBmA6jmg==", 505 | "dev": true, 506 | "requires": { 507 | "prettier-linter-helpers": "^1.0.0" 508 | } 509 | }, 510 | "eslint-scope": { 511 | "version": "5.1.0", 512 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.0.tgz", 513 | "integrity": "sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==", 514 | "dev": true, 515 | "requires": { 516 | "esrecurse": "^4.1.0", 517 | "estraverse": "^4.1.1" 518 | } 519 | }, 520 | "eslint-utils": { 521 | "version": "2.1.0", 522 | "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", 523 | "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", 524 | "dev": true, 525 | "requires": { 526 | "eslint-visitor-keys": "^1.1.0" 527 | } 528 | }, 529 | "eslint-visitor-keys": { 530 | "version": "1.3.0", 531 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", 532 | "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", 533 | "dev": true 534 | }, 535 | "espree": { 536 | "version": "7.3.0", 537 | "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.0.tgz", 538 | "integrity": "sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==", 539 | "dev": true, 540 | "requires": { 541 | "acorn": "^7.4.0", 542 | "acorn-jsx": "^5.2.0", 543 | "eslint-visitor-keys": "^1.3.0" 544 | } 545 | }, 546 | "esprima": { 547 | "version": "4.0.1", 548 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 549 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 550 | "dev": true 551 | }, 552 | "esquery": { 553 | "version": "1.3.1", 554 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", 555 | "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", 556 | "dev": true, 557 | "requires": { 558 | "estraverse": "^5.1.0" 559 | }, 560 | "dependencies": { 561 | "estraverse": { 562 | "version": "5.2.0", 563 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", 564 | "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", 565 | "dev": true 566 | } 567 | } 568 | }, 569 | "esrecurse": { 570 | "version": "4.3.0", 571 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", 572 | "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", 573 | "dev": true, 574 | "requires": { 575 | "estraverse": "^5.2.0" 576 | }, 577 | "dependencies": { 578 | "estraverse": { 579 | "version": "5.2.0", 580 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", 581 | "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", 582 | "dev": true 583 | } 584 | } 585 | }, 586 | "estraverse": { 587 | "version": "4.3.0", 588 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", 589 | "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", 590 | "dev": true 591 | }, 592 | "esutils": { 593 | "version": "2.0.3", 594 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 595 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", 596 | "dev": true 597 | }, 598 | "external-editor": { 599 | "version": "3.1.0", 600 | "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", 601 | "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", 602 | "requires": { 603 | "chardet": "^0.7.0", 604 | "iconv-lite": "^0.4.24", 605 | "tmp": "^0.0.33" 606 | } 607 | }, 608 | "fast-deep-equal": { 609 | "version": "3.1.3", 610 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 611 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 612 | "dev": true 613 | }, 614 | "fast-diff": { 615 | "version": "1.2.0", 616 | "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", 617 | "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", 618 | "dev": true 619 | }, 620 | "fast-json-stable-stringify": { 621 | "version": "2.1.0", 622 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 623 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 624 | "dev": true 625 | }, 626 | "fast-levenshtein": { 627 | "version": "2.0.6", 628 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 629 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", 630 | "dev": true 631 | }, 632 | "figures": { 633 | "version": "3.2.0", 634 | "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", 635 | "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", 636 | "requires": { 637 | "escape-string-regexp": "^1.0.5" 638 | } 639 | }, 640 | "file-entry-cache": { 641 | "version": "5.0.1", 642 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", 643 | "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", 644 | "dev": true, 645 | "requires": { 646 | "flat-cache": "^2.0.1" 647 | } 648 | }, 649 | "find-up": { 650 | "version": "4.1.0", 651 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", 652 | "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", 653 | "requires": { 654 | "locate-path": "^5.0.0", 655 | "path-exists": "^4.0.0" 656 | } 657 | }, 658 | "flat-cache": { 659 | "version": "2.0.1", 660 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", 661 | "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", 662 | "dev": true, 663 | "requires": { 664 | "flatted": "^2.0.0", 665 | "rimraf": "2.6.3", 666 | "write": "1.0.3" 667 | } 668 | }, 669 | "flatted": { 670 | "version": "2.0.2", 671 | "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", 672 | "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", 673 | "dev": true 674 | }, 675 | "fs-extra": { 676 | "version": "9.0.1", 677 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", 678 | "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", 679 | "requires": { 680 | "at-least-node": "^1.0.0", 681 | "graceful-fs": "^4.2.0", 682 | "jsonfile": "^6.0.1", 683 | "universalify": "^1.0.0" 684 | } 685 | }, 686 | "fs.realpath": { 687 | "version": "1.0.0", 688 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 689 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 690 | "dev": true 691 | }, 692 | "functional-red-black-tree": { 693 | "version": "1.0.1", 694 | "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", 695 | "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", 696 | "dev": true 697 | }, 698 | "get-caller-file": { 699 | "version": "2.0.5", 700 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 701 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" 702 | }, 703 | "get-stdin": { 704 | "version": "6.0.0", 705 | "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", 706 | "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", 707 | "dev": true 708 | }, 709 | "glob": { 710 | "version": "7.1.6", 711 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 712 | "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 713 | "dev": true, 714 | "requires": { 715 | "fs.realpath": "^1.0.0", 716 | "inflight": "^1.0.4", 717 | "inherits": "2", 718 | "minimatch": "^3.0.4", 719 | "once": "^1.3.0", 720 | "path-is-absolute": "^1.0.0" 721 | } 722 | }, 723 | "glob-parent": { 724 | "version": "5.1.1", 725 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", 726 | "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", 727 | "dev": true, 728 | "requires": { 729 | "is-glob": "^4.0.1" 730 | } 731 | }, 732 | "globals": { 733 | "version": "12.4.0", 734 | "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", 735 | "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", 736 | "dev": true, 737 | "requires": { 738 | "type-fest": "^0.8.1" 739 | }, 740 | "dependencies": { 741 | "type-fest": { 742 | "version": "0.8.1", 743 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", 744 | "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", 745 | "dev": true 746 | } 747 | } 748 | }, 749 | "graceful-fs": { 750 | "version": "4.2.4", 751 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", 752 | "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" 753 | }, 754 | "has-flag": { 755 | "version": "4.0.0", 756 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 757 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" 758 | }, 759 | "iconv-lite": { 760 | "version": "0.4.24", 761 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 762 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 763 | "requires": { 764 | "safer-buffer": ">= 2.1.2 < 3" 765 | } 766 | }, 767 | "ignore": { 768 | "version": "4.0.6", 769 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", 770 | "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", 771 | "dev": true 772 | }, 773 | "import-fresh": { 774 | "version": "3.2.1", 775 | "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", 776 | "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", 777 | "dev": true, 778 | "requires": { 779 | "parent-module": "^1.0.0", 780 | "resolve-from": "^4.0.0" 781 | } 782 | }, 783 | "imurmurhash": { 784 | "version": "0.1.4", 785 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 786 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", 787 | "dev": true 788 | }, 789 | "inflight": { 790 | "version": "1.0.6", 791 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 792 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 793 | "dev": true, 794 | "requires": { 795 | "once": "^1.3.0", 796 | "wrappy": "1" 797 | } 798 | }, 799 | "inherits": { 800 | "version": "2.0.4", 801 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 802 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 803 | "dev": true 804 | }, 805 | "inquirer": { 806 | "version": "7.3.3", 807 | "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", 808 | "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", 809 | "requires": { 810 | "ansi-escapes": "^4.2.1", 811 | "chalk": "^4.1.0", 812 | "cli-cursor": "^3.1.0", 813 | "cli-width": "^3.0.0", 814 | "external-editor": "^3.0.3", 815 | "figures": "^3.0.0", 816 | "lodash": "^4.17.19", 817 | "mute-stream": "0.0.8", 818 | "run-async": "^2.4.0", 819 | "rxjs": "^6.6.0", 820 | "string-width": "^4.1.0", 821 | "strip-ansi": "^6.0.0", 822 | "through": "^2.3.6" 823 | } 824 | }, 825 | "is-extglob": { 826 | "version": "2.1.1", 827 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 828 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", 829 | "dev": true 830 | }, 831 | "is-fullwidth-code-point": { 832 | "version": "3.0.0", 833 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 834 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" 835 | }, 836 | "is-glob": { 837 | "version": "4.0.1", 838 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", 839 | "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", 840 | "dev": true, 841 | "requires": { 842 | "is-extglob": "^2.1.1" 843 | } 844 | }, 845 | "is-interactive": { 846 | "version": "1.0.0", 847 | "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", 848 | "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==" 849 | }, 850 | "isexe": { 851 | "version": "2.0.0", 852 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 853 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 854 | "dev": true 855 | }, 856 | "js-tokens": { 857 | "version": "4.0.0", 858 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 859 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 860 | "dev": true 861 | }, 862 | "js-yaml": { 863 | "version": "3.14.0", 864 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", 865 | "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", 866 | "dev": true, 867 | "requires": { 868 | "argparse": "^1.0.7", 869 | "esprima": "^4.0.0" 870 | } 871 | }, 872 | "jsesc": { 873 | "version": "2.5.2", 874 | "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", 875 | "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", 876 | "dev": true 877 | }, 878 | "json-schema-traverse": { 879 | "version": "0.4.1", 880 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 881 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 882 | "dev": true 883 | }, 884 | "json-stable-stringify-without-jsonify": { 885 | "version": "1.0.1", 886 | "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 887 | "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", 888 | "dev": true 889 | }, 890 | "jsonfile": { 891 | "version": "6.0.1", 892 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", 893 | "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", 894 | "requires": { 895 | "graceful-fs": "^4.1.6", 896 | "universalify": "^1.0.0" 897 | } 898 | }, 899 | "levn": { 900 | "version": "0.4.1", 901 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", 902 | "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", 903 | "dev": true, 904 | "requires": { 905 | "prelude-ls": "^1.2.1", 906 | "type-check": "~0.4.0" 907 | } 908 | }, 909 | "locate-path": { 910 | "version": "5.0.0", 911 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", 912 | "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", 913 | "requires": { 914 | "p-locate": "^4.1.0" 915 | } 916 | }, 917 | "lodash": { 918 | "version": "4.17.20", 919 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", 920 | "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" 921 | }, 922 | "log-symbols": { 923 | "version": "4.0.0", 924 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", 925 | "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", 926 | "requires": { 927 | "chalk": "^4.0.0" 928 | } 929 | }, 930 | "mimic-fn": { 931 | "version": "2.1.0", 932 | "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", 933 | "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" 934 | }, 935 | "minimatch": { 936 | "version": "3.0.4", 937 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 938 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 939 | "dev": true, 940 | "requires": { 941 | "brace-expansion": "^1.1.7" 942 | } 943 | }, 944 | "minimist": { 945 | "version": "1.2.5", 946 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 947 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", 948 | "dev": true 949 | }, 950 | "mkdirp": { 951 | "version": "0.5.5", 952 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", 953 | "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", 954 | "dev": true, 955 | "requires": { 956 | "minimist": "^1.2.5" 957 | } 958 | }, 959 | "ms": { 960 | "version": "2.1.2", 961 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 962 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 963 | "dev": true 964 | }, 965 | "mute-stream": { 966 | "version": "0.0.8", 967 | "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", 968 | "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" 969 | }, 970 | "natural-compare": { 971 | "version": "1.4.0", 972 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 973 | "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", 974 | "dev": true 975 | }, 976 | "once": { 977 | "version": "1.4.0", 978 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 979 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 980 | "dev": true, 981 | "requires": { 982 | "wrappy": "1" 983 | } 984 | }, 985 | "onetime": { 986 | "version": "5.1.2", 987 | "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", 988 | "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", 989 | "requires": { 990 | "mimic-fn": "^2.1.0" 991 | } 992 | }, 993 | "optionator": { 994 | "version": "0.9.1", 995 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", 996 | "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", 997 | "dev": true, 998 | "requires": { 999 | "deep-is": "^0.1.3", 1000 | "fast-levenshtein": "^2.0.6", 1001 | "levn": "^0.4.1", 1002 | "prelude-ls": "^1.2.1", 1003 | "type-check": "^0.4.0", 1004 | "word-wrap": "^1.2.3" 1005 | } 1006 | }, 1007 | "ora": { 1008 | "version": "5.0.0", 1009 | "resolved": "https://registry.npmjs.org/ora/-/ora-5.0.0.tgz", 1010 | "integrity": "sha512-s26qdWqke2kjN/wC4dy+IQPBIMWBJlSU/0JZhk30ZDBLelW25rv66yutUWARMigpGPzcXHb+Nac5pNhN/WsARw==", 1011 | "requires": { 1012 | "chalk": "^4.1.0", 1013 | "cli-cursor": "^3.1.0", 1014 | "cli-spinners": "^2.4.0", 1015 | "is-interactive": "^1.0.0", 1016 | "log-symbols": "^4.0.0", 1017 | "mute-stream": "0.0.8", 1018 | "strip-ansi": "^6.0.0", 1019 | "wcwidth": "^1.0.1" 1020 | } 1021 | }, 1022 | "os-tmpdir": { 1023 | "version": "1.0.2", 1024 | "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", 1025 | "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" 1026 | }, 1027 | "p-limit": { 1028 | "version": "2.3.0", 1029 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", 1030 | "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", 1031 | "requires": { 1032 | "p-try": "^2.0.0" 1033 | } 1034 | }, 1035 | "p-locate": { 1036 | "version": "4.1.0", 1037 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", 1038 | "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", 1039 | "requires": { 1040 | "p-limit": "^2.2.0" 1041 | } 1042 | }, 1043 | "p-try": { 1044 | "version": "2.2.0", 1045 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", 1046 | "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" 1047 | }, 1048 | "parent-module": { 1049 | "version": "1.0.1", 1050 | "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", 1051 | "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", 1052 | "dev": true, 1053 | "requires": { 1054 | "callsites": "^3.0.0" 1055 | } 1056 | }, 1057 | "path-exists": { 1058 | "version": "4.0.0", 1059 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 1060 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" 1061 | }, 1062 | "path-is-absolute": { 1063 | "version": "1.0.1", 1064 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1065 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 1066 | "dev": true 1067 | }, 1068 | "path-key": { 1069 | "version": "3.1.1", 1070 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 1071 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 1072 | "dev": true 1073 | }, 1074 | "path-parse": { 1075 | "version": "1.0.6", 1076 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", 1077 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", 1078 | "dev": true 1079 | }, 1080 | "prelude-ls": { 1081 | "version": "1.2.1", 1082 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", 1083 | "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", 1084 | "dev": true 1085 | }, 1086 | "prettier": { 1087 | "version": "2.1.1", 1088 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.1.1.tgz", 1089 | "integrity": "sha512-9bY+5ZWCfqj3ghYBLxApy2zf6m+NJo5GzmLTpr9FsApsfjriNnS2dahWReHMi7qNPhhHl9SYHJs2cHZLgexNIw==", 1090 | "dev": true 1091 | }, 1092 | "prettier-linter-helpers": { 1093 | "version": "1.0.0", 1094 | "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", 1095 | "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", 1096 | "dev": true, 1097 | "requires": { 1098 | "fast-diff": "^1.1.2" 1099 | } 1100 | }, 1101 | "progress": { 1102 | "version": "2.0.3", 1103 | "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", 1104 | "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", 1105 | "dev": true 1106 | }, 1107 | "punycode": { 1108 | "version": "2.1.1", 1109 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 1110 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", 1111 | "dev": true 1112 | }, 1113 | "regexpp": { 1114 | "version": "3.1.0", 1115 | "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", 1116 | "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", 1117 | "dev": true 1118 | }, 1119 | "require-directory": { 1120 | "version": "2.1.1", 1121 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 1122 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" 1123 | }, 1124 | "require-main-filename": { 1125 | "version": "2.0.0", 1126 | "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", 1127 | "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" 1128 | }, 1129 | "resolve": { 1130 | "version": "1.17.0", 1131 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", 1132 | "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", 1133 | "dev": true, 1134 | "requires": { 1135 | "path-parse": "^1.0.6" 1136 | } 1137 | }, 1138 | "resolve-from": { 1139 | "version": "4.0.0", 1140 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 1141 | "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", 1142 | "dev": true 1143 | }, 1144 | "restore-cursor": { 1145 | "version": "3.1.0", 1146 | "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", 1147 | "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", 1148 | "requires": { 1149 | "onetime": "^5.1.0", 1150 | "signal-exit": "^3.0.2" 1151 | } 1152 | }, 1153 | "rimraf": { 1154 | "version": "2.6.3", 1155 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", 1156 | "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", 1157 | "dev": true, 1158 | "requires": { 1159 | "glob": "^7.1.3" 1160 | } 1161 | }, 1162 | "run-async": { 1163 | "version": "2.4.1", 1164 | "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", 1165 | "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==" 1166 | }, 1167 | "rxjs": { 1168 | "version": "6.6.2", 1169 | "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.2.tgz", 1170 | "integrity": "sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg==", 1171 | "requires": { 1172 | "tslib": "^1.9.0" 1173 | } 1174 | }, 1175 | "safer-buffer": { 1176 | "version": "2.1.2", 1177 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1178 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 1179 | }, 1180 | "semver": { 1181 | "version": "7.3.2", 1182 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", 1183 | "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", 1184 | "dev": true 1185 | }, 1186 | "set-blocking": { 1187 | "version": "2.0.0", 1188 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 1189 | "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" 1190 | }, 1191 | "shebang-command": { 1192 | "version": "2.0.0", 1193 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 1194 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 1195 | "dev": true, 1196 | "requires": { 1197 | "shebang-regex": "^3.0.0" 1198 | } 1199 | }, 1200 | "shebang-regex": { 1201 | "version": "3.0.0", 1202 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 1203 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 1204 | "dev": true 1205 | }, 1206 | "signal-exit": { 1207 | "version": "3.0.3", 1208 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", 1209 | "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" 1210 | }, 1211 | "slice-ansi": { 1212 | "version": "2.1.0", 1213 | "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", 1214 | "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", 1215 | "dev": true, 1216 | "requires": { 1217 | "ansi-styles": "^3.2.0", 1218 | "astral-regex": "^1.0.0", 1219 | "is-fullwidth-code-point": "^2.0.0" 1220 | }, 1221 | "dependencies": { 1222 | "ansi-styles": { 1223 | "version": "3.2.1", 1224 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 1225 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 1226 | "dev": true, 1227 | "requires": { 1228 | "color-convert": "^1.9.0" 1229 | } 1230 | }, 1231 | "color-convert": { 1232 | "version": "1.9.3", 1233 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 1234 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 1235 | "dev": true, 1236 | "requires": { 1237 | "color-name": "1.1.3" 1238 | } 1239 | }, 1240 | "color-name": { 1241 | "version": "1.1.3", 1242 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 1243 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 1244 | "dev": true 1245 | }, 1246 | "is-fullwidth-code-point": { 1247 | "version": "2.0.0", 1248 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 1249 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", 1250 | "dev": true 1251 | } 1252 | } 1253 | }, 1254 | "source-map": { 1255 | "version": "0.5.7", 1256 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", 1257 | "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", 1258 | "dev": true 1259 | }, 1260 | "sprintf-js": { 1261 | "version": "1.0.3", 1262 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 1263 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 1264 | "dev": true 1265 | }, 1266 | "string-width": { 1267 | "version": "4.2.0", 1268 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", 1269 | "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", 1270 | "requires": { 1271 | "emoji-regex": "^8.0.0", 1272 | "is-fullwidth-code-point": "^3.0.0", 1273 | "strip-ansi": "^6.0.0" 1274 | } 1275 | }, 1276 | "strip-ansi": { 1277 | "version": "6.0.0", 1278 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", 1279 | "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", 1280 | "requires": { 1281 | "ansi-regex": "^5.0.0" 1282 | } 1283 | }, 1284 | "strip-json-comments": { 1285 | "version": "3.1.1", 1286 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 1287 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 1288 | "dev": true 1289 | }, 1290 | "supports-color": { 1291 | "version": "7.2.0", 1292 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 1293 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 1294 | "requires": { 1295 | "has-flag": "^4.0.0" 1296 | } 1297 | }, 1298 | "table": { 1299 | "version": "5.4.6", 1300 | "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", 1301 | "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", 1302 | "dev": true, 1303 | "requires": { 1304 | "ajv": "^6.10.2", 1305 | "lodash": "^4.17.14", 1306 | "slice-ansi": "^2.1.0", 1307 | "string-width": "^3.0.0" 1308 | }, 1309 | "dependencies": { 1310 | "ansi-regex": { 1311 | "version": "4.1.0", 1312 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 1313 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", 1314 | "dev": true 1315 | }, 1316 | "emoji-regex": { 1317 | "version": "7.0.3", 1318 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", 1319 | "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", 1320 | "dev": true 1321 | }, 1322 | "is-fullwidth-code-point": { 1323 | "version": "2.0.0", 1324 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 1325 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", 1326 | "dev": true 1327 | }, 1328 | "string-width": { 1329 | "version": "3.1.0", 1330 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 1331 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 1332 | "dev": true, 1333 | "requires": { 1334 | "emoji-regex": "^7.0.1", 1335 | "is-fullwidth-code-point": "^2.0.0", 1336 | "strip-ansi": "^5.1.0" 1337 | } 1338 | }, 1339 | "strip-ansi": { 1340 | "version": "5.2.0", 1341 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 1342 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 1343 | "dev": true, 1344 | "requires": { 1345 | "ansi-regex": "^4.1.0" 1346 | } 1347 | } 1348 | } 1349 | }, 1350 | "text-table": { 1351 | "version": "0.2.0", 1352 | "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", 1353 | "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", 1354 | "dev": true 1355 | }, 1356 | "through": { 1357 | "version": "2.3.8", 1358 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 1359 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" 1360 | }, 1361 | "tmp": { 1362 | "version": "0.0.33", 1363 | "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", 1364 | "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", 1365 | "requires": { 1366 | "os-tmpdir": "~1.0.2" 1367 | } 1368 | }, 1369 | "to-fast-properties": { 1370 | "version": "2.0.0", 1371 | "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", 1372 | "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", 1373 | "dev": true 1374 | }, 1375 | "tslib": { 1376 | "version": "1.13.0", 1377 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", 1378 | "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" 1379 | }, 1380 | "type-check": { 1381 | "version": "0.4.0", 1382 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", 1383 | "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", 1384 | "dev": true, 1385 | "requires": { 1386 | "prelude-ls": "^1.2.1" 1387 | } 1388 | }, 1389 | "type-fest": { 1390 | "version": "0.11.0", 1391 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", 1392 | "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==" 1393 | }, 1394 | "universalify": { 1395 | "version": "1.0.0", 1396 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", 1397 | "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==" 1398 | }, 1399 | "uri-js": { 1400 | "version": "4.4.0", 1401 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", 1402 | "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", 1403 | "dev": true, 1404 | "requires": { 1405 | "punycode": "^2.1.0" 1406 | } 1407 | }, 1408 | "v8-compile-cache": { 1409 | "version": "2.1.1", 1410 | "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", 1411 | "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", 1412 | "dev": true 1413 | }, 1414 | "wcwidth": { 1415 | "version": "1.0.1", 1416 | "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", 1417 | "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", 1418 | "requires": { 1419 | "defaults": "^1.0.3" 1420 | } 1421 | }, 1422 | "which": { 1423 | "version": "2.0.2", 1424 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 1425 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 1426 | "dev": true, 1427 | "requires": { 1428 | "isexe": "^2.0.0" 1429 | } 1430 | }, 1431 | "which-module": { 1432 | "version": "2.0.0", 1433 | "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", 1434 | "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" 1435 | }, 1436 | "word-wrap": { 1437 | "version": "1.2.3", 1438 | "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", 1439 | "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", 1440 | "dev": true 1441 | }, 1442 | "wrap-ansi": { 1443 | "version": "6.2.0", 1444 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", 1445 | "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", 1446 | "requires": { 1447 | "ansi-styles": "^4.0.0", 1448 | "string-width": "^4.1.0", 1449 | "strip-ansi": "^6.0.0" 1450 | } 1451 | }, 1452 | "wrappy": { 1453 | "version": "1.0.2", 1454 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1455 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 1456 | "dev": true 1457 | }, 1458 | "write": { 1459 | "version": "1.0.3", 1460 | "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", 1461 | "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", 1462 | "dev": true, 1463 | "requires": { 1464 | "mkdirp": "^0.5.1" 1465 | } 1466 | }, 1467 | "y18n": { 1468 | "version": "4.0.0", 1469 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", 1470 | "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" 1471 | }, 1472 | "yargs": { 1473 | "version": "15.4.1", 1474 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", 1475 | "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", 1476 | "requires": { 1477 | "cliui": "^6.0.0", 1478 | "decamelize": "^1.2.0", 1479 | "find-up": "^4.1.0", 1480 | "get-caller-file": "^2.0.1", 1481 | "require-directory": "^2.1.1", 1482 | "require-main-filename": "^2.0.0", 1483 | "set-blocking": "^2.0.0", 1484 | "string-width": "^4.2.0", 1485 | "which-module": "^2.0.0", 1486 | "y18n": "^4.0.0", 1487 | "yargs-parser": "^18.1.2" 1488 | } 1489 | }, 1490 | "yargs-parser": { 1491 | "version": "18.1.3", 1492 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", 1493 | "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", 1494 | "requires": { 1495 | "camelcase": "^5.0.0", 1496 | "decamelize": "^1.2.0" 1497 | } 1498 | } 1499 | } 1500 | } 1501 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-api-cli", 3 | "version": "0.0.7", 4 | "description": "Cli tool for generating an express project. Instead of wasting extra time creating your project structure, start building right away", 5 | "preferGlobal": true, 6 | "bin": { 7 | "exp-api": "./bin/index.js" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "author": "Tolustar (tolustar.com)", 13 | "license": "MIT", 14 | "dependencies": { 15 | "chalk": "^4.1.0", 16 | "fs-extra": "^9.0.1", 17 | "inquirer": "^7.3.3", 18 | "ora": "^5.0.0", 19 | "yargs": "^15.4.1" 20 | }, 21 | "devDependencies": { 22 | "babel-eslint": "^10.1.0", 23 | "eslint": "^7.8.1", 24 | "eslint-config-prettier": "^6.11.0", 25 | "eslint-plugin-prettier": "^3.1.4", 26 | "prettier": "^2.1.1" 27 | }, 28 | "repository": { 29 | "type": "git", 30 | "url": "https://github.com/tolustar/express-api-cli" 31 | }, 32 | "keywords": [ 33 | "express", 34 | "api", 35 | "cli", 36 | "express-api" 37 | ] 38 | } 39 | --------------------------------------------------------------------------------